1 /*
2  * tkCanvImg.c --
3  *
4  *      This file implements image items for canvas widgets.
5  *
6  * Copyright (c) 1994 The Regents of the University of California.
7  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
8  *
9  * See the file "license.terms" for information on usage and redistribution
10  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11  *
12  * RCS: @(#) $Id: tkCanvImg.c,v 1.6 2003/02/09 07:48:22 hobbs Exp $
13  */
14 
15 #include "tkInt.h"
16 #include "tkPort.h"
17 #include "tkCanvases.h"
18 
19 /*
20  * The structure below defines the record for each image item.
21  */
22 
23 typedef struct ImageItem  {
24     Tk_Item header;             /* Generic stuff that's the same for all
25 				 * types.  MUST BE FIRST IN STRUCTURE. */
26     Tk_Canvas canvas;           /* Canvas containing the image. */
27     double x, y;                /* Coordinates of positioning point for
28 				 * image. */
29     Tk_Anchor anchor;           /* Where to anchor image relative to
30 				 * (x,y). */
31     char *imageString;          /* String describing -image option (malloc-ed).
32 				 * NULL means no image right now. */
33     char *activeImageString;    /* String describing -activeimage option.
34 				 * NULL means no image right now. */
35     char *disabledImageString;  /* String describing -disabledimage option.
36 				 * NULL means no image right now. */
37     Tk_Image image;             /* Image to display in window, or NULL if
38 				 * no image at present. */
39     Tk_Image activeImage;       /* Image to display in window, or NULL if
40 				 * no image at present. */
41     Tk_Image disabledImage;     /* Image to display in window, or NULL if
42 				 * no image at present. */
43 } ImageItem;
44 
45 /*
46  * Information used for parsing configuration specs:
47  */
48 
49 static Tk_CustomOption stateOption = {
50     TkStateParseProc,
51     TkStatePrintProc, (ClientData) 2
52 };
53 static Tk_CustomOption tagsOption = {
54     Tk_CanvasTagsParseProc,
55     Tk_CanvasTagsPrintProc, (ClientData) NULL
56 };
57 
58 static Tk_ConfigSpec configSpecs[] = {
59     {TK_CONFIG_STRING, "-activeimage", (char *) NULL, (char *) NULL,
60 	(char *) NULL, Tk_Offset(ImageItem, activeImageString),
61 	TK_CONFIG_NULL_OK},
62     {TK_CONFIG_ANCHOR, "-anchor", (char *) NULL, (char *) NULL,
63 	"center", Tk_Offset(ImageItem, anchor), TK_CONFIG_DONT_SET_DEFAULT},
64     {TK_CONFIG_OBJECT, "-disabledimage", (char *) NULL, (char *) NULL,
65 	(char *) NULL, Tk_Offset(ImageItem, disabledImageString),
66 	TK_CONFIG_NULL_OK},
67     {TK_CONFIG_OBJECT, "-image", (char *) NULL, (char *) NULL,
68 	(char *) NULL, Tk_Offset(ImageItem, imageString), TK_CONFIG_NULL_OK},
69     {TK_CONFIG_CUSTOM, "-state", (char *) NULL, (char *) NULL,
70 	(char *) NULL, Tk_Offset(Tk_Item, state), TK_CONFIG_NULL_OK,
71 	&stateOption},
72     {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
73 	(char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},
74     {TK_CONFIG_CALLBACK, "-updatecommand", (char *) NULL, (char *) NULL,
75 	(char *) NULL, Tk_Offset(Tk_Item, updateCmd), TK_CONFIG_NULL_OK},
76     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
77 	(char *) NULL, 0, 0}
78 };
79 
80 /*
81  * Prototypes for procedures defined in this file:
82  */
83 
84 static void             ImageChangedProc _ANSI_ARGS_((ClientData clientData,
85 			    int x, int y, int width, int height, int imgWidth,
86 			    int imgHeight));
87 static int              ImageCoords _ANSI_ARGS_((Tcl_Interp *interp,
88 			    Tk_Canvas canvas, Tk_Item *itemPtr, int argc,
89 			    Tcl_Obj *CONST objv[]));
90 static int              ImageToArea _ANSI_ARGS_((Tk_Canvas canvas,
91 			    Tk_Item *itemPtr, double *rectPtr));
92 static double           ImageToPoint _ANSI_ARGS_((Tk_Canvas canvas,
93 			    Tk_Item *itemPtr, double *coordPtr));
94 static int              ImageToPostscript _ANSI_ARGS_((Tcl_Interp *interp,
95 			    Tk_Canvas canvas, Tk_Item *itemPtr, int prepass));
96 static void             ComputeImageBbox _ANSI_ARGS_((Tk_Canvas canvas,
97 			    ImageItem *imgPtr));
98 static int              ConfigureImage _ANSI_ARGS_((Tcl_Interp *interp,
99 			    Tk_Canvas canvas, Tk_Item *itemPtr, int argc,
100 			    Tcl_Obj *CONST objv[], int flags));
101 static int              CreateImage _ANSI_ARGS_((Tcl_Interp *interp,
102 			    Tk_Canvas canvas, struct Tk_Item *itemPtr,
103 			    int argc, Tcl_Obj *CONST objv[]));
104 static void             DeleteImage _ANSI_ARGS_((Tk_Canvas canvas,
105 			    Tk_Item *itemPtr, Display *display));
106 static void             DisplayImage _ANSI_ARGS_((Tk_Canvas canvas,
107 			    Tk_Item *itemPtr, Display *display, Drawable dst,
108 			    int x, int y, int width, int height));
109 static void             ScaleImage _ANSI_ARGS_((Tk_Canvas canvas,
110 			    Tk_Item *itemPtr, double originX, double originY,
111 			    double scaleX, double scaleY));
112 static void             TranslateImage _ANSI_ARGS_((Tk_Canvas canvas,
113 			    Tk_Item *itemPtr, double deltaX, double deltaY));
114 
115 /*
116  * The structures below defines the image item type in terms of
117  * procedures that can be invoked by generic item code.
118  */
119 
120 Tk_ItemType tkImageType = {
121     "image",                            /* name */
122     sizeof(ImageItem),                  /* itemSize */
123     CreateImage,                        /* createProc */
124     configSpecs,                        /* configSpecs */
125     ConfigureImage,                     /* configureProc */
126     ImageCoords,                        /* coordProc */
127     DeleteImage,                        /* deleteProc */
128     DisplayImage,                       /* displayProc */
129     TK_CONFIG_OBJS,                     /* flags */
130     ImageToPoint,                       /* pointProc */
131     ImageToArea,                        /* areaProc */
132     ImageToPostscript,                  /* postscriptProc */
133     ScaleImage,                         /* scaleProc */
134     TranslateImage,                     /* translateProc */
135     (Tk_ItemIndexProc *) NULL,          /* indexProc */
136     (Tk_ItemCursorProc *) NULL,         /* icursorProc */
137     (Tk_ItemSelectionProc *) NULL,      /* selectionProc */
138     (Tk_ItemInsertProc *) NULL,         /* insertProc */
139     (Tk_ItemDCharsProc *) NULL,         /* dTextProc */
140     (Tk_ItemType *) NULL,               /* nextPtr */
141 };
142 
143 /*
144  *--------------------------------------------------------------
145  *
146  * CreateImage --
147  *
148  *      This procedure is invoked to create a new image
149  *      item in a canvas.
150  *
151  * Results:
152  *      A standard Tcl return value.  If an error occurred in
153  *      creating the item, then an error message is left in
154  *      the interp's result;  in this case itemPtr is left uninitialized,
155  *      so it can be safely freed by the caller.
156  *
157  * Side effects:
158  *      A new image item is created.
159  *
160  *--------------------------------------------------------------
161  */
162 
163 static int
CreateImage(interp,canvas,itemPtr,objc,objv)164 CreateImage(interp, canvas, itemPtr, objc, objv)
165     Tcl_Interp *interp;                 /* Interpreter for error reporting. */
166     Tk_Canvas canvas;                   /* Canvas to hold new item. */
167     Tk_Item *itemPtr;                   /* Record to hold new item;  header
168 					 * has been initialized by caller. */
169     int objc;                           /* Number of arguments in objv. */
170     Tcl_Obj *CONST objv[];              /* Arguments describing rectangle. */
171 {
172     ImageItem *imgPtr = (ImageItem *) itemPtr;
173     int i;
174 
175     if (objc == 0) {
176 	panic("canvas did not pass any coords\n");
177     }
178 
179     /*
180      * Initialize item's record.
181      */
182 
183     imgPtr->canvas = canvas;
184     imgPtr->anchor = TK_ANCHOR_CENTER;
185     imgPtr->imageString = NULL;
186     imgPtr->activeImageString = NULL;
187     imgPtr->disabledImageString = NULL;
188     imgPtr->image = NULL;
189     imgPtr->activeImage = NULL;
190     imgPtr->disabledImage = NULL;
191 
192     /*
193      * Process the arguments to fill in the item record.
194      * Only 1 (list) or 2 (x y) coords are allowed.
195      */
196 
197     if (objc == 1) {
198 	i = 1;
199     } else {
200 	char *arg = Tcl_GetString(objv[1]);
201 	i = 2;
202 	if ((arg[0] == '-') && (arg[1] >= 'a') && (arg[1] <= 'z')) {
203 	    i = 1;
204 	}
205     }
206     if ((ImageCoords(interp, canvas, itemPtr, i, objv) != TCL_OK)) {
207 	goto error;
208     }
209     if (ConfigureImage(interp, canvas, itemPtr, objc-i, objv+i, 0) == TCL_OK) {
210 	return TCL_OK;
211     }
212 
213     error:
214     DeleteImage(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
215     return TCL_ERROR;
216 }
217 
218 /*
219  *--------------------------------------------------------------
220  *
221  * ImageCoords --
222  *
223  *      This procedure is invoked to process the "coords" widget
224  *      command on image items.  See the user documentation for
225  *      details on what it does.
226  *
227  * Results:
228  *      Returns TCL_OK or TCL_ERROR, and sets the interp's result.
229  *
230  * Side effects:
231  *      The coordinates for the given item may be changed.
232  *
233  *--------------------------------------------------------------
234  */
235 
236 static int
ImageCoords(interp,canvas,itemPtr,objc,objv)237 ImageCoords(interp, canvas, itemPtr, objc, objv)
238     Tcl_Interp *interp;                 /* Used for error reporting. */
239     Tk_Canvas canvas;                   /* Canvas containing item. */
240     Tk_Item *itemPtr;                   /* Item whose coordinates are to be
241 					 * read or modified. */
242     int objc;                           /* Number of coordinates supplied in
243 					 * objv. */
244     Tcl_Obj *CONST objv[];              /* Array of coordinates: x1, y1,
245 					 * x2, y2, ... */
246 {
247     ImageItem *imgPtr = (ImageItem *) itemPtr;
248 
249     if (objc == 0) {
250 	Tcl_Obj *obj = Tcl_NewObj();
251 	Tcl_Obj *subobj = Tcl_NewDoubleObj(imgPtr->x);
252 	Tcl_ListObjAppendElement(interp, obj, subobj);
253 	subobj = Tcl_NewDoubleObj(imgPtr->y);
254 	Tcl_ListObjAppendElement(interp, obj, subobj);
255 	Tcl_SetObjResult(interp, obj);
256     } else if (objc < 3) {
257 	if (objc==1) {
258 	    if (Tcl_ListObjGetElements(interp, objv[0], &objc,
259 		    (Tcl_Obj ***) &objv) != TCL_OK) {
260 		return TCL_ERROR;
261 	    } else if (objc != 2) {
262 		char buf[64];
263 
264 		sprintf(buf, "wrong # coordinates: expected 2, got %d", objc);
265 		Tcl_SetResult(interp, buf, TCL_VOLATILE);
266 		return TCL_ERROR;
267 	    }
268 	}
269 	if ((Tk_CanvasGetCoordFromObj(interp, canvas, objv[0], &imgPtr->x) != TCL_OK)
270 		|| (Tk_CanvasGetCoordFromObj(interp, canvas, objv[1],
271 		    &imgPtr->y) != TCL_OK)) {
272 	    return TCL_ERROR;
273 	}
274 	ComputeImageBbox(canvas, imgPtr);
275     } else {
276 	char buf[64];
277 
278 	sprintf(buf, "wrong # coordinates: expected 0 or 2, got %d", objc);
279 	Tcl_SetResult(interp, buf, TCL_VOLATILE);
280 	return TCL_ERROR;
281     }
282     return TCL_OK;
283 }
284 
285 /*
286  *--------------------------------------------------------------
287  *
288  * ConfigureImage --
289  *
290  *      This procedure is invoked to configure various aspects
291  *      of an image item, such as its anchor position.
292  *
293  * Results:
294  *      A standard Tcl result code.  If an error occurs, then
295  *      an error message is left in the interp's result.
296  *
297  * Side effects:
298  *      Configuration information may be set for itemPtr.
299  *
300  *--------------------------------------------------------------
301  */
302 
303 static int
ConfigureImage(interp,canvas,itemPtr,objc,objv,flags)304 ConfigureImage(interp, canvas, itemPtr, objc, objv, flags)
305     Tcl_Interp *interp;         /* Used for error reporting. */
306     Tk_Canvas canvas;           /* Canvas containing itemPtr. */
307     Tk_Item *itemPtr;           /* Image item to reconfigure. */
308     int objc;                   /* Number of elements in objv.  */
309     Tcl_Obj *CONST objv[];      /* Arguments describing things to configure. */
310     int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
311 {
312     ImageItem *imgPtr = (ImageItem *) itemPtr;
313     Tk_Window tkwin;
314     Tk_Image image;
315 
316     tkwin = Tk_CanvasTkwin(canvas);
317     if (TCL_OK != Tk_ConfigureWidget(interp, tkwin, configSpecs, objc,
318 	    objv, (char *) imgPtr, flags|TK_CONFIG_OBJS)) {
319 	return TCL_ERROR;
320     }
321 
322     /*
323      * Create the image.  Save the old image around and don't free it
324      * until after the new one is allocated.  This keeps the reference
325      * count from going to zero so the image doesn't have to be recreated
326      * if it hasn't changed.
327      */
328 
329     if (imgPtr->activeImageString != NULL) {
330 	itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT;
331     } else {
332 	itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT;
333     }
334     if (imgPtr->imageString != NULL) {
335 	image = Tk_GetImage(interp, tkwin, imgPtr->imageString,
336 		ImageChangedProc, (ClientData) imgPtr);
337 	if (image == NULL) {
338 	    return TCL_ERROR;
339 	}
340     } else {
341 	image = NULL;
342     }
343     if (imgPtr->image != NULL) {
344 	Tk_FreeImage(imgPtr->image);
345     }
346     imgPtr->image = image;
347     if (imgPtr->activeImageString != NULL) {
348 	image = Tk_GetImage(interp, tkwin, imgPtr->activeImageString,
349 		ImageChangedProc, (ClientData) imgPtr);
350 	if (image == NULL) {
351 	    return TCL_ERROR;
352 	}
353     } else {
354 	image = NULL;
355     }
356     if (imgPtr->activeImage != NULL) {
357 	Tk_FreeImage(imgPtr->activeImage);
358     }
359     imgPtr->activeImage = image;
360     if (imgPtr->disabledImageString != NULL) {
361 	image = Tk_GetImage(interp, tkwin, imgPtr->disabledImageString,
362 		ImageChangedProc, (ClientData) imgPtr);
363 	if (image == NULL) {
364 	    return TCL_ERROR;
365 	}
366     } else {
367 	image = NULL;
368     }
369     if (imgPtr->disabledImage != NULL) {
370 	Tk_FreeImage(imgPtr->disabledImage);
371     }
372     imgPtr->disabledImage = image;
373     ComputeImageBbox(canvas, imgPtr);
374     return TCL_OK;
375 }
376 
377 /*
378  *--------------------------------------------------------------
379  *
380  * DeleteImage --
381  *
382  *      This procedure is called to clean up the data structure
383  *      associated with a image item.
384  *
385  * Results:
386  *      None.
387  *
388  * Side effects:
389  *      Resources associated with itemPtr are released.
390  *
391  *--------------------------------------------------------------
392  */
393 
394 static void
DeleteImage(canvas,itemPtr,display)395 DeleteImage(canvas, itemPtr, display)
396     Tk_Canvas canvas;                   /* Info about overall canvas widget. */
397     Tk_Item *itemPtr;                   /* Item that is being deleted. */
398     Display *display;                   /* Display containing window for
399 					 * canvas. */
400 {
401     ImageItem *imgPtr = (ImageItem *) itemPtr;
402 
403     if (imgPtr->imageString != NULL) {
404 	ckfree(imgPtr->imageString);
405     }
406     if (imgPtr->activeImageString != NULL) {
407 	ckfree(imgPtr->activeImageString);
408     }
409     if (imgPtr->disabledImageString != NULL) {
410 	ckfree(imgPtr->disabledImageString);
411     }
412     if (imgPtr->image != NULL) {
413 	Tk_FreeImage(imgPtr->image);
414     }
415     if (imgPtr->activeImage != NULL) {
416 	Tk_FreeImage(imgPtr->activeImage);
417     }
418     if (imgPtr->disabledImage != NULL) {
419 	Tk_FreeImage(imgPtr->disabledImage);
420     }
421 }
422 
423 /*
424  *--------------------------------------------------------------
425  *
426  * ComputeImageBbox --
427  *
428  *      This procedure is invoked to compute the bounding box of
429  *      all the pixels that may be drawn as part of a image item.
430  *      This procedure is where the child image's placement is
431  *      computed.
432  *
433  * Results:
434  *      None.
435  *
436  * Side effects:
437  *      The fields x1, y1, x2, and y2 are updated in the header
438  *      for itemPtr.
439  *
440  *--------------------------------------------------------------
441  */
442 
443 	/* ARGSUSED */
444 static void
ComputeImageBbox(canvas,imgPtr)445 ComputeImageBbox(canvas, imgPtr)
446     Tk_Canvas canvas;                   /* Canvas that contains item. */
447     ImageItem *imgPtr;                  /* Item whose bbox is to be
448 					 * recomputed. */
449 {
450     int width, height;
451     int x, y;
452     Tk_Image image;
453     Tk_State state = Tk_GetItemState(canvas, &imgPtr->header);
454 
455     image = imgPtr->image;
456     if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)imgPtr) {
457 	if (imgPtr->activeImage != NULL) {
458 	    image = imgPtr->activeImage;
459 	}
460     } else if (state == TK_STATE_DISABLED) {
461 	if (imgPtr->disabledImage != NULL) {
462 	    image = imgPtr->disabledImage;
463 	}
464     }
465 
466     x = (int) (imgPtr->x + ((imgPtr->x >= 0) ? 0.5 : - 0.5));
467     y = (int) (imgPtr->y + ((imgPtr->y >= 0) ? 0.5 : - 0.5));
468 
469     if ((state == TK_STATE_HIDDEN) || (image == None)) {
470 	imgPtr->header.x1 = imgPtr->header.x2 = x;
471 	imgPtr->header.y1 = imgPtr->header.y2 = y;
472 	return;
473     }
474 
475     /*
476      * Compute location and size of image, using anchor information.
477      */
478 
479     Tk_SizeOfImage(image, &width, &height);
480     switch (imgPtr->anchor) {
481 	case TK_ANCHOR_N:
482 	    x -= width/2;
483 	    break;
484 	case TK_ANCHOR_NE:
485 	    x -= width;
486 	    break;
487 	case TK_ANCHOR_E:
488 	    x -= width;
489 	    y -= height/2;
490 	    break;
491 	case TK_ANCHOR_SE:
492 	    x -= width;
493 	    y -= height;
494 	    break;
495 	case TK_ANCHOR_S:
496 	    x -= width/2;
497 	    y -= height;
498 	    break;
499 	case TK_ANCHOR_SW:
500 	    y -= height;
501 	    break;
502 	case TK_ANCHOR_W:
503 	    y -= height/2;
504 	    break;
505 	case TK_ANCHOR_NW:
506 	    break;
507 	case TK_ANCHOR_CENTER:
508 	    x -= width/2;
509 	    y -= height/2;
510 	    break;
511     }
512 
513     /*
514      * Store the information in the item header.
515      */
516 
517     imgPtr->header.x1 = x;
518     imgPtr->header.y1 = y;
519     imgPtr->header.x2 = x + width;
520     imgPtr->header.y2 = y + height;
521 }
522 
523 /*
524  *--------------------------------------------------------------
525  *
526  * DisplayImage --
527  *
528  *      This procedure is invoked to draw a image item in a given
529  *      drawable.
530  *
531  * Results:
532  *      None.
533  *
534  * Side effects:
535  *      ItemPtr is drawn in drawable using the transformation
536  *      information in canvas.
537  *
538  *--------------------------------------------------------------
539  */
540 
541 static void
DisplayImage(canvas,itemPtr,display,drawable,x,y,width,height)542 DisplayImage(canvas, itemPtr, display, drawable, x, y, width, height)
543     Tk_Canvas canvas;                   /* Canvas that contains item. */
544     Tk_Item *itemPtr;                   /* Item to be displayed. */
545     Display *display;                   /* Display on which to draw item. */
546     Drawable drawable;                  /* Pixmap or window in which to draw
547 					 * item. */
548     int x, y, width, height;            /* Describes region of canvas that
549 					 * must be redisplayed (not used). */
550 {
551     ImageItem *imgPtr = (ImageItem *) itemPtr;
552     short drawableX, drawableY;
553     Tk_Image image;
554     Tk_State state = Tk_GetItemState(canvas, itemPtr);
555 
556     image = imgPtr->image;
557     if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
558 	if (imgPtr->activeImage != NULL) {
559 	    image = imgPtr->activeImage;
560 	}
561     } else if (state == TK_STATE_DISABLED) {
562 	if (imgPtr->disabledImage != NULL) {
563 	    image = imgPtr->disabledImage;
564 	}
565     }
566 
567     if (image == NULL) {
568 	return;
569     }
570 
571     /*
572      * Translate the coordinates to those of the image, then redisplay it.
573      */
574 
575     Tk_CanvasDrawableCoords(canvas, (double) x, (double) y,
576 	    &drawableX, &drawableY);
577     Tk_RedrawImage(image, x - imgPtr->header.x1, y - imgPtr->header.y1,
578 	    width, height, drawable, drawableX, drawableY);
579 }
580 
581 /*
582  *--------------------------------------------------------------
583  *
584  * ImageToPoint --
585  *
586  *      Computes the distance from a given point to a given
587  *      rectangle, in canvas units.
588  *
589  * Results:
590  *      The return value is 0 if the point whose x and y coordinates
591  *      are coordPtr[0] and coordPtr[1] is inside the image.  If the
592  *      point isn't inside the image then the return value is the
593  *      distance from the point to the image.
594  *
595  * Side effects:
596  *      None.
597  *
598  *--------------------------------------------------------------
599  */
600 
601 static double
ImageToPoint(canvas,itemPtr,coordPtr)602 ImageToPoint(canvas, itemPtr, coordPtr)
603     Tk_Canvas canvas;           /* Canvas containing item. */
604     Tk_Item *itemPtr;           /* Item to check against point. */
605     double *coordPtr;           /* Pointer to x and y coordinates. */
606 {
607     ImageItem *imgPtr = (ImageItem *) itemPtr;
608     double x1, x2, y1, y2, xDiff, yDiff;
609 
610     x1 = imgPtr->header.x1;
611     y1 = imgPtr->header.y1;
612     x2 = imgPtr->header.x2;
613     y2 = imgPtr->header.y2;
614 
615     /*
616      * Point is outside rectangle.
617      */
618 
619     if (coordPtr[0] < x1) {
620 	xDiff = x1 - coordPtr[0];
621     } else if (coordPtr[0] > x2)  {
622 	xDiff = coordPtr[0] - x2;
623     } else {
624 	xDiff = 0;
625     }
626 
627     if (coordPtr[1] < y1) {
628 	yDiff = y1 - coordPtr[1];
629     } else if (coordPtr[1] > y2)  {
630 	yDiff = coordPtr[1] - y2;
631     } else {
632 	yDiff = 0;
633     }
634 
635     return hypot(xDiff, yDiff);
636 }
637 
638 /*
639  *--------------------------------------------------------------
640  *
641  * ImageToArea --
642  *
643  *      This procedure is called to determine whether an item
644  *      lies entirely inside, entirely outside, or overlapping
645  *      a given rectangle.
646  *
647  * Results:
648  *      -1 is returned if the item is entirely outside the area
649  *      given by rectPtr, 0 if it overlaps, and 1 if it is entirely
650  *      inside the given area.
651  *
652  * Side effects:
653  *      None.
654  *
655  *--------------------------------------------------------------
656  */
657 
658 static int
ImageToArea(canvas,itemPtr,rectPtr)659 ImageToArea(canvas, itemPtr, rectPtr)
660     Tk_Canvas canvas;           /* Canvas containing item. */
661     Tk_Item *itemPtr;           /* Item to check against rectangle. */
662     double *rectPtr;            /* Pointer to array of four coordinates
663 				 * (x1, y1, x2, y2) describing rectangular
664 				 * area.  */
665 {
666     ImageItem *imgPtr = (ImageItem *) itemPtr;
667 
668     if ((rectPtr[2] <= imgPtr->header.x1)
669 	    || (rectPtr[0] >= imgPtr->header.x2)
670 	    || (rectPtr[3] <= imgPtr->header.y1)
671 	    || (rectPtr[1] >= imgPtr->header.y2)) {
672 	return -1;
673     }
674     if ((rectPtr[0] <= imgPtr->header.x1)
675 	    && (rectPtr[1] <= imgPtr->header.y1)
676 	    && (rectPtr[2] >= imgPtr->header.x2)
677 	    && (rectPtr[3] >= imgPtr->header.y2)) {
678 	return 1;
679     }
680     return 0;
681 }
682 
683 /*
684  *--------------------------------------------------------------
685  *
686  * ImageToPostscript --
687  *
688  *      This procedure is called to generate Postscript for
689  *      image items.
690  *
691  * Results:
692  *      The return value is a standard Tcl result.  If an error
693  *      occurs in generating Postscript then an error message is
694  *      left in interp->result, replacing whatever used to be there.
695  *      If no error occurs, then Postscript for the item is appended
696  *      to the result.
697  *
698  * Side effects:
699  *      None.
700  *
701  *--------------------------------------------------------------
702  */
703 
704 static int
ImageToPostscript(interp,canvas,itemPtr,prepass)705 ImageToPostscript(interp, canvas, itemPtr, prepass)
706     Tcl_Interp *interp;                 /* Leave Postscript or error message
707 					 * here. */
708     Tk_Canvas canvas;                   /* Information about overall canvas. */
709     Tk_Item *itemPtr;                   /* Item for which Postscript is
710 					 * wanted. */
711     int prepass;                        /* 1 means this is a prepass to
712 					 * collect font information;  0 means
713 					 * final Postscript is being created.*/
714 {
715     ImageItem *imgPtr = (ImageItem *)itemPtr;
716     Tk_Window canvasWin = Tk_CanvasTkwin(canvas);
717 
718     char buffer[256];
719     double x, y;
720     int width, height;
721     Tk_Image image;
722     Tk_State state = Tk_GetItemState(canvas, itemPtr);
723 
724     image = imgPtr->image;
725     if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
726 	if (imgPtr->activeImage != NULL) {
727 	    image = imgPtr->activeImage;
728 	}
729     } else if (state == TK_STATE_DISABLED) {
730 	if (imgPtr->disabledImage != NULL) {
731 	    image = imgPtr->disabledImage;
732 	}
733     }
734     Tk_SizeOfImage(image, &width, &height);
735 
736     /*
737      * Compute the coordinates of the lower-left corner of the image,
738      * taking into account the anchor position for the image.
739      */
740 
741     x = imgPtr->x;
742     y = Tk_CanvasPsY(canvas, imgPtr->y);
743 
744     switch (imgPtr->anchor) {
745 	case TK_ANCHOR_NW:                      y -= height;            break;
746 	case TK_ANCHOR_N:       x -= width/2.0; y -= height;            break;
747 	case TK_ANCHOR_NE:      x -= width;     y -= height;            break;
748 	case TK_ANCHOR_E:       x -= width;     y -= height/2.0;        break;
749 	case TK_ANCHOR_SE:      x -= width;                             break;
750 	case TK_ANCHOR_S:       x -= width/2.0;                         break;
751 	case TK_ANCHOR_SW:                                              break;
752 	case TK_ANCHOR_W:                       y -= height/2.0;        break;
753 	case TK_ANCHOR_CENTER:  x -= width/2.0; y -= height/2.0;        break;
754     }
755 
756     if (image == NULL) {
757 	return TCL_OK;
758     }
759 
760     if (!prepass) {
761 	sprintf(buffer, "%.15g %.15g", x, y);
762 	Tcl_AppendResult(interp, buffer, " translate\n", (char *) NULL);
763     }
764 
765     return Tk_PostscriptImage(image, interp, canvasWin,
766 	    ((TkCanvas *) canvas)->psInfo, 0, 0, width, height, prepass);
767 }
768 
769 /*
770  *--------------------------------------------------------------
771  *
772  * ScaleImage --
773  *
774  *      This procedure is invoked to rescale an item.
775  *
776  * Results:
777  *      None.
778  *
779  * Side effects:
780  *      The item referred to by itemPtr is rescaled so that the
781  *      following transformation is applied to all point coordinates:
782  *              x' = originX + scaleX*(x-originX)
783  *              y' = originY + scaleY*(y-originY)
784  *
785  *--------------------------------------------------------------
786  */
787 
788 static void
ScaleImage(canvas,itemPtr,originX,originY,scaleX,scaleY)789 ScaleImage(canvas, itemPtr, originX, originY, scaleX, scaleY)
790     Tk_Canvas canvas;                   /* Canvas containing rectangle. */
791     Tk_Item *itemPtr;                   /* Rectangle to be scaled. */
792     double originX, originY;            /* Origin about which to scale rect. */
793     double scaleX;                      /* Amount to scale in X direction. */
794     double scaleY;                      /* Amount to scale in Y direction. */
795 {
796     ImageItem *imgPtr = (ImageItem *) itemPtr;
797 
798     imgPtr->x = originX + scaleX*(imgPtr->x - originX);
799     imgPtr->y = originY + scaleY*(imgPtr->y - originY);
800     ComputeImageBbox(canvas, imgPtr);
801 }
802 
803 /*
804  *--------------------------------------------------------------
805  *
806  * TranslateImage --
807  *
808  *      This procedure is called to move an item by a given amount.
809  *
810  * Results:
811  *      None.
812  *
813  * Side effects:
814  *      The position of the item is offset by (xDelta, yDelta), and
815  *      the bounding box is updated in the generic part of the item
816  *      structure.
817  *
818  *--------------------------------------------------------------
819  */
820 
821 static void
TranslateImage(canvas,itemPtr,deltaX,deltaY)822 TranslateImage(canvas, itemPtr, deltaX, deltaY)
823     Tk_Canvas canvas;                   /* Canvas containing item. */
824     Tk_Item *itemPtr;                   /* Item that is being moved. */
825     double deltaX, deltaY;              /* Amount by which item is to be
826 					 * moved. */
827 {
828     ImageItem *imgPtr = (ImageItem *) itemPtr;
829 
830     imgPtr->x += deltaX;
831     imgPtr->y += deltaY;
832     ComputeImageBbox(canvas, imgPtr);
833 }
834 
835 /*
836  *----------------------------------------------------------------------
837  *
838  * ImageChangedProc --
839  *
840  *      This procedure is invoked by the image code whenever the manager
841  *      for an image does something that affects the image's size or
842  *      how it is displayed.
843  *
844  * Results:
845  *      None.
846  *
847  * Side effects:
848  *      Arranges for the canvas to get redisplayed.
849  *
850  *----------------------------------------------------------------------
851  */
852 
853 static void
ImageChangedProc(clientData,x,y,width,height,imgWidth,imgHeight)854 ImageChangedProc(clientData, x, y, width, height, imgWidth, imgHeight)
855     ClientData clientData;              /* Pointer to canvas item for image. */
856     int x, y;                           /* Upper left pixel (within image)
857 					 * that must be redisplayed. */
858     int width, height;                  /* Dimensions of area to redisplay
859 					 * (may be <= 0). */
860     int imgWidth, imgHeight;            /* New dimensions of image. */
861 {
862     ImageItem *imgPtr = (ImageItem *) clientData;
863 
864     /*
865      * If the image's size changed and it's not anchored at its
866      * northwest corner then just redisplay the entire area of the
867      * image.  This is a bit over-conservative, but we need to do
868      * something because a size change also means a position change.
869      */
870 
871     if (((imgPtr->header.x2 - imgPtr->header.x1) != imgWidth)
872 	    || ((imgPtr->header.y2 - imgPtr->header.y1) != imgHeight)) {
873 	x = y = 0;
874 	width = imgWidth;
875 	height = imgHeight;
876 	Tk_CanvasEventuallyRedraw(imgPtr->canvas, imgPtr->header.x1,
877 		imgPtr->header.y1, imgPtr->header.x2, imgPtr->header.y2);
878     }
879     ComputeImageBbox(imgPtr->canvas, imgPtr);
880     Tk_CanvasEventuallyRedraw(imgPtr->canvas, imgPtr->header.x1 + x,
881 	    imgPtr->header.y1 + y, (int) (imgPtr->header.x1 + x + width),
882 	    (int) (imgPtr->header.y1 + y + height));
883 }
884 
885