/* * tkCanvImg.c -- * * This file implements image items for canvas widgets. * * Copyright (c) 1994 The Regents of the University of California. * Copyright (c) 1994-1996 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tkCanvImg.c 1.18 96/05/03 10:49:09 */ #include <stdio.h> #include "tkInt.h" #include "tkPort.h" #include "tkCanvas.h" /* * The structure below defines the record for each image item. */ typedef struct ImageItem { Tk_Item header; /* Generic stuff that's the same for all * types. MUST BE FIRST IN STRUCTURE. */ Tk_Canvas canvas; /* Canvas containing the image. */ double x, y; /* Coordinates of positioning point for * image. */ Tk_Anchor anchor; /* Where to anchor image relative to * (x,y). */ char *imageString; /* String describing -image option (malloc-ed). * NULL means no image right now. */ Tk_Image image; /* Image to display in window, or NULL if * no image at present. */ } ImageItem; /* * Information used for parsing configuration specs: */ static Tk_CustomOption tagsOption = {Tk_CanvasTagsParseProc, Tk_CanvasTagsPrintProc, (ClientData) NULL }; static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_ANCHOR, "-anchor", (char *) NULL, (char *) NULL, "center", Tk_Offset(ImageItem, anchor), TK_CONFIG_DONT_SET_DEFAULT}, #ifdef STk_CODE {TK_CONFIG_IMAGE, "-image", (char *) NULL, (char *) NULL, #else {TK_CONFIG_STRING, "-image", (char *) NULL, (char *) NULL, #endif (char *) NULL, Tk_Offset(ImageItem, imageString), TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL, (char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption}, {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, 0, 0} }; /* * Prototypes for procedures defined in this file: */ static void ImageChangedProc _ANSI_ARGS_((ClientData clientData, int x, int y, int width, int height, int imgWidth, int imgHeight)); static int ImageCoords _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr, int argc, char **argv)); static int ImageToArea _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item *itemPtr, double *rectPtr)); static double ImageToPoint _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item *itemPtr, double *coordPtr)); static void ComputeImageBbox _ANSI_ARGS_((Tk_Canvas canvas, ImageItem *imgPtr)); static int ConfigureImage _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr, int argc, char **argv, int flags)); static int CreateImage _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas, struct Tk_Item *itemPtr, int argc, char **argv)); static void DeleteImage _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item *itemPtr, Display *display)); static void DisplayImage _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item *itemPtr, Display *display, Drawable dst, int x, int y, int width, int height)); static void ScaleImage _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item *itemPtr, double originX, double originY, double scaleX, double scaleY)); static void TranslateImage _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item *itemPtr, double deltaX, double deltaY)); /* * The structures below defines the image item type in terms of * procedures that can be invoked by generic item code. */ Tk_ItemType tkImageType = { "image", /* name */ sizeof(ImageItem), /* itemSize */ CreateImage, /* createProc */ configSpecs, /* configSpecs */ ConfigureImage, /* configureProc */ ImageCoords, /* coordProc */ DeleteImage, /* deleteProc */ DisplayImage, /* displayProc */ 0, /* alwaysRedraw */ ImageToPoint, /* pointProc */ ImageToArea, /* areaProc */ (Tk_ItemPostscriptProc *) NULL, /* postscriptProc */ ScaleImage, /* scaleProc */ TranslateImage, /* translateProc */ (Tk_ItemIndexProc *) NULL, /* indexProc */ (Tk_ItemCursorProc *) NULL, /* icursorProc */ (Tk_ItemSelectionProc *) NULL, /* selectionProc */ (Tk_ItemInsertProc *) NULL, /* insertProc */ (Tk_ItemDCharsProc *) NULL, /* dTextProc */ (Tk_ItemType *) NULL /* nextPtr */ }; /* *-------------------------------------------------------------- * * CreateImage -- * * This procedure is invoked to create a new image * item in a canvas. * * Results: * A standard Tcl return value. If an error occurred in * creating the item, then an error message is left in * interp->result; in this case itemPtr is left uninitialized, * so it can be safely freed by the caller. * * Side effects: * A new image item is created. * *-------------------------------------------------------------- */ static int CreateImage(interp, canvas, itemPtr, argc, argv) Tcl_Interp *interp; /* Interpreter for error reporting. */ Tk_Canvas canvas; /* Canvas to hold new item. */ Tk_Item *itemPtr; /* Record to hold new item; header * has been initialized by caller. */ int argc; /* Number of arguments in argv. */ char **argv; /* Arguments describing rectangle. */ { ImageItem *imgPtr = (ImageItem *) itemPtr; if (argc < 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tk_PathName(Tk_CanvasTkwin(canvas)), " create ", itemPtr->typePtr->name, " x y ?options?\"", (char *) NULL); return TCL_ERROR; } /* * Initialize item's record. */ imgPtr->canvas = canvas; imgPtr->anchor = TK_ANCHOR_CENTER; imgPtr->imageString = NULL; imgPtr->image = NULL; /* * Process the arguments to fill in the item record. */ if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &imgPtr->x) != TCL_OK) || (Tk_CanvasGetCoord(interp, canvas, argv[1], &imgPtr->y) != TCL_OK)) { return TCL_ERROR; } if (ConfigureImage(interp, canvas, itemPtr, argc-2, argv+2, 0) != TCL_OK) { DeleteImage(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas))); return TCL_ERROR; } return TCL_OK; } /* *-------------------------------------------------------------- * * ImageCoords -- * * This procedure is invoked to process the "coords" widget * command on image items. See the user documentation for * details on what it does. * * Results: * Returns TCL_OK or TCL_ERROR, and sets interp->result. * * Side effects: * The coordinates for the given item may be changed. * *-------------------------------------------------------------- */ static int ImageCoords(interp, canvas, itemPtr, argc, argv) Tcl_Interp *interp; /* Used for error reporting. */ Tk_Canvas canvas; /* Canvas containing item. */ Tk_Item *itemPtr; /* Item whose coordinates are to be * read or modified. */ int argc; /* Number of coordinates supplied in * argv. */ char **argv; /* Array of coordinates: x1, y1, * x2, y2, ... */ { ImageItem *imgPtr = (ImageItem *) itemPtr; char x[TCL_DOUBLE_SPACE], y[TCL_DOUBLE_SPACE]; if (argc == 0) { Tcl_PrintDouble(interp, imgPtr->x, x); Tcl_PrintDouble(interp, imgPtr->y, y); Tcl_AppendResult(interp, x, " ", y, (char *) NULL); } else if (argc == 2) { if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &imgPtr->x) != TCL_OK) || (Tk_CanvasGetCoord(interp, canvas, argv[1], &imgPtr->y) != TCL_OK)) { return TCL_ERROR; } ComputeImageBbox(canvas, imgPtr); } else { sprintf(interp->result, "wrong # coordinates: expected 0 or 2, got %d", argc); return TCL_ERROR; } return TCL_OK; } /* *-------------------------------------------------------------- * * ConfigureImage -- * * This procedure is invoked to configure various aspects * of an image item, such as its anchor position. * * Results: * A standard Tcl result code. If an error occurs, then * an error message is left in interp->result. * * Side effects: * Configuration information may be set for itemPtr. * *-------------------------------------------------------------- */ static int ConfigureImage(interp, canvas, itemPtr, argc, argv, flags) Tcl_Interp *interp; /* Used for error reporting. */ Tk_Canvas canvas; /* Canvas containing itemPtr. */ Tk_Item *itemPtr; /* Image item to reconfigure. */ int argc; /* Number of elements in argv. */ char **argv; /* Arguments describing things to configure. */ int flags; /* Flags to pass to Tk_ConfigureWidget. */ { ImageItem *imgPtr = (ImageItem *) itemPtr; Tk_Window tkwin; Tk_Image image; tkwin = Tk_CanvasTkwin(canvas); if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc, argv, (char *) imgPtr, flags) != TCL_OK) { return TCL_ERROR; } /* * Create the image. Save the old image around and don't free it * until after the new one is allocated. This keeps the reference * count from going to zero so the image doesn't have to be recreated * if it hasn't changed. */ if (imgPtr->imageString != NULL) { image = Tk_GetImage(interp, tkwin, imgPtr->imageString, ImageChangedProc, (ClientData) imgPtr); if (image == NULL) { return TCL_ERROR; } } else { image = NULL; } if (imgPtr->image != NULL) { Tk_FreeImage(imgPtr->image); } imgPtr->image = image; ComputeImageBbox(canvas, imgPtr); return TCL_OK; } /* *-------------------------------------------------------------- * * DeleteImage -- * * This procedure is called to clean up the data structure * associated with a image item. * * Results: * None. * * Side effects: * Resources associated with itemPtr are released. * *-------------------------------------------------------------- */ static void DeleteImage(canvas, itemPtr, display) Tk_Canvas canvas; /* Info about overall canvas widget. */ Tk_Item *itemPtr; /* Item that is being deleted. */ Display *display; /* Display containing window for * canvas. */ { ImageItem *imgPtr = (ImageItem *) itemPtr; if (imgPtr->imageString != NULL) { ckfree(imgPtr->imageString); } if (imgPtr->image != NULL) { Tk_FreeImage(imgPtr->image); } } /* *-------------------------------------------------------------- * * ComputeImageBbox -- * * This procedure is invoked to compute the bounding box of * all the pixels that may be drawn as part of a image item. * This procedure is where the child image's placement is * computed. * * Results: * None. * * Side effects: * The fields x1, y1, x2, and y2 are updated in the header * for itemPtr. * *-------------------------------------------------------------- */ /* ARGSUSED */ static void ComputeImageBbox(canvas, imgPtr) Tk_Canvas canvas; /* Canvas that contains item. */ ImageItem *imgPtr; /* Item whose bbox is to be * recomputed. */ { int width, height; int x, y; x = (int) (imgPtr->x + ((imgPtr->x >= 0) ? 0.5 : - 0.5)); y = (int) (imgPtr->y + ((imgPtr->y >= 0) ? 0.5 : - 0.5)); if (imgPtr->image == None) { imgPtr->header.x1 = imgPtr->header.x2 = x; imgPtr->header.y1 = imgPtr->header.y2 = y; return; } /* * Compute location and size of image, using anchor information. */ Tk_SizeOfImage(imgPtr->image, &width, &height); switch (imgPtr->anchor) { case TK_ANCHOR_N: x -= width/2; break; case TK_ANCHOR_NE: x -= width; break; case TK_ANCHOR_E: x -= width; y -= height/2; break; case TK_ANCHOR_SE: x -= width; y -= height; break; case TK_ANCHOR_S: x -= width/2; y -= height; break; case TK_ANCHOR_SW: y -= height; break; case TK_ANCHOR_W: y -= height/2; break; case TK_ANCHOR_NW: break; case TK_ANCHOR_CENTER: x -= width/2; y -= height/2; break; } /* * Store the information in the item header. */ imgPtr->header.x1 = x; imgPtr->header.y1 = y; imgPtr->header.x2 = x + width; imgPtr->header.y2 = y + height; } /* *-------------------------------------------------------------- * * DisplayImage -- * * This procedure is invoked to draw a image item in a given * drawable. * * Results: * None. * * Side effects: * ItemPtr is drawn in drawable using the transformation * information in canvas. * *-------------------------------------------------------------- */ static void DisplayImage(canvas, itemPtr, display, drawable, x, y, width, height) Tk_Canvas canvas; /* Canvas that contains item. */ Tk_Item *itemPtr; /* Item to be displayed. */ Display *display; /* Display on which to draw item. */ Drawable drawable; /* Pixmap or window in which to draw * item. */ int x, y, width, height; /* Describes region of canvas that * must be redisplayed (not used). */ { ImageItem *imgPtr = (ImageItem *) itemPtr; short drawableX, drawableY; if (imgPtr->image == NULL) { return; } /* * Translate the coordinates to those of the image, then redisplay it. */ Tk_CanvasDrawableCoords(canvas, (double) x, (double) y, &drawableX, &drawableY); Tk_RedrawImage(imgPtr->image, x - imgPtr->header.x1, y - imgPtr->header.y1, width, height, drawable, drawableX, drawableY); } /* *-------------------------------------------------------------- * * ImageToPoint -- * * Computes the distance from a given point to a given * rectangle, in canvas units. * * Results: * The return value is 0 if the point whose x and y coordinates * are coordPtr[0] and coordPtr[1] is inside the image. If the * point isn't inside the image then the return value is the * distance from the point to the image. * * Side effects: * None. * *-------------------------------------------------------------- */ static double ImageToPoint(canvas, itemPtr, coordPtr) Tk_Canvas canvas; /* Canvas containing item. */ Tk_Item *itemPtr; /* Item to check against point. */ double *coordPtr; /* Pointer to x and y coordinates. */ { ImageItem *imgPtr = (ImageItem *) itemPtr; double x1, x2, y1, y2, xDiff, yDiff; x1 = imgPtr->header.x1; y1 = imgPtr->header.y1; x2 = imgPtr->header.x2; y2 = imgPtr->header.y2; /* * Point is outside rectangle. */ if (coordPtr[0] < x1) { xDiff = x1 - coordPtr[0]; } else if (coordPtr[0] > x2) { xDiff = coordPtr[0] - x2; } else { xDiff = 0; } if (coordPtr[1] < y1) { yDiff = y1 - coordPtr[1]; } else if (coordPtr[1] > y2) { yDiff = coordPtr[1] - y2; } else { yDiff = 0; } return hypot(xDiff, yDiff); } /* *-------------------------------------------------------------- * * ImageToArea -- * * This procedure is called to determine whether an item * lies entirely inside, entirely outside, or overlapping * a given rectangle. * * Results: * -1 is returned if the item is entirely outside the area * given by rectPtr, 0 if it overlaps, and 1 if it is entirely * inside the given area. * * Side effects: * None. * *-------------------------------------------------------------- */ static int ImageToArea(canvas, itemPtr, rectPtr) Tk_Canvas canvas; /* Canvas containing item. */ Tk_Item *itemPtr; /* Item to check against rectangle. */ double *rectPtr; /* Pointer to array of four coordinates * (x1, y1, x2, y2) describing rectangular * area. */ { ImageItem *imgPtr = (ImageItem *) itemPtr; if ((rectPtr[2] <= imgPtr->header.x1) || (rectPtr[0] >= imgPtr->header.x2) || (rectPtr[3] <= imgPtr->header.y1) || (rectPtr[1] >= imgPtr->header.y2)) { return -1; } if ((rectPtr[0] <= imgPtr->header.x1) && (rectPtr[1] <= imgPtr->header.y1) && (rectPtr[2] >= imgPtr->header.x2) && (rectPtr[3] >= imgPtr->header.y2)) { return 1; } return 0; } /* *-------------------------------------------------------------- * * ScaleImage -- * * This procedure is invoked to rescale an item. * * Results: * None. * * Side effects: * The item referred to by itemPtr is rescaled so that the * following transformation is applied to all point coordinates: * x' = originX + scaleX*(x-originX) * y' = originY + scaleY*(y-originY) * *-------------------------------------------------------------- */ static void ScaleImage(canvas, itemPtr, originX, originY, scaleX, scaleY) Tk_Canvas canvas; /* Canvas containing rectangle. */ Tk_Item *itemPtr; /* Rectangle to be scaled. */ double originX, originY; /* Origin about which to scale rect. */ double scaleX; /* Amount to scale in X direction. */ double scaleY; /* Amount to scale in Y direction. */ { ImageItem *imgPtr = (ImageItem *) itemPtr; imgPtr->x = originX + scaleX*(imgPtr->x - originX); imgPtr->y = originY + scaleY*(imgPtr->y - originY); ComputeImageBbox(canvas, imgPtr); } /* *-------------------------------------------------------------- * * TranslateImage -- * * This procedure is called to move an item by a given amount. * * Results: * None. * * Side effects: * The position of the item is offset by (xDelta, yDelta), and * the bounding box is updated in the generic part of the item * structure. * *-------------------------------------------------------------- */ static void TranslateImage(canvas, itemPtr, deltaX, deltaY) Tk_Canvas canvas; /* Canvas containing item. */ Tk_Item *itemPtr; /* Item that is being moved. */ double deltaX, deltaY; /* Amount by which item is to be * moved. */ { ImageItem *imgPtr = (ImageItem *) itemPtr; imgPtr->x += deltaX; imgPtr->y += deltaY; ComputeImageBbox(canvas, imgPtr); } /* *---------------------------------------------------------------------- * * ImageChangedProc -- * * This procedure is invoked by the image code whenever the manager * for an image does something that affects the image's size or * how it is displayed. * * Results: * None. * * Side effects: * Arranges for the canvas to get redisplayed. * *---------------------------------------------------------------------- */ static void ImageChangedProc(clientData, x, y, width, height, imgWidth, imgHeight) ClientData clientData; /* Pointer to canvas item for image. */ int x, y; /* Upper left pixel (within image) * that must be redisplayed. */ int width, height; /* Dimensions of area to redisplay * (may be <= 0). */ int imgWidth, imgHeight; /* New dimensions of image. */ { ImageItem *imgPtr = (ImageItem *) clientData; /* * If the image's size changed and it's not anchored at its * northwest corner then just redisplay the entire area of the * image. This is a bit over-conservative, but we need to do * something because a size change also means a position change. */ if (((imgPtr->header.x2 - imgPtr->header.x1) != imgWidth) || ((imgPtr->header.y2 - imgPtr->header.y1) != imgHeight)) { x = y = 0; width = imgWidth; height = imgHeight; Tk_CanvasEventuallyRedraw(imgPtr->canvas, imgPtr->header.x1, imgPtr->header.y1, imgPtr->header.x2, imgPtr->header.y2); } ComputeImageBbox(imgPtr->canvas, imgPtr); Tk_CanvasEventuallyRedraw(imgPtr->canvas, imgPtr->header.x1 + x, imgPtr->header.y1 + y, (int) (imgPtr->header.x1 + x + width), (int) (imgPtr->header.y1 + y + height)); }