1/*
2 * tkImage.c --
3 *
4 *	This module implements the image protocol, which allows lots
5 *	of different kinds of images to be used in lots of different
6 *	widgets.
7 *
8 * Copyright (c) 1994 The Regents of the University of California.
9 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
10 *
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 *
14 * RCS: @(#) $Id: tkImage.c,v 1.19.2.2 2003/09/17 23:45:05 dgp Exp $
15 */
16
17#include "tkInt.h"
18#include "tkPort.h"
19
20/*
21 * Each call to Tk_GetImage returns a pointer to one of the following
22 * structures, which is used as a token by clients (widgets) that
23 * display images.
24 */
25
26typedef struct Image {
27    Tk_Window tkwin;		/* Window passed to Tk_GetImage (needed to
28				 * "re-get" the image later if the manager
29				 * changes). */
30    Display *display;		/* Display for tkwin.  Needed because when
31				 * the image is eventually freed tkwin may
32				 * not exist anymore. */
33    struct ImageMaster *masterPtr;
34				/* Master for this image (identifiers image
35				 * manager, for example). */
36    ClientData instanceData;
37				/* One word argument to pass to image manager
38				 * when dealing with this image instance. */
39    Tk_ImageChangedProc *changeProc;
40				/* Code in widget to call when image changes
41				 * in a way that affects redisplay. */
42    ClientData widgetClientData;
43				/* Argument to pass to changeProc. */
44    struct Image *nextPtr;	/* Next in list of all image instances
45				 * associated with the same name. */
46
47} Image;
48
49/*
50 * For each image master there is one of the following structures,
51 * which represents a name in the image table and all of the images
52 * instantiated from it.  Entries in mainPtr->imageTable point to
53 * these structures.
54 */
55
56typedef struct ImageMaster {
57    Tk_ImageType *typePtr;	/* Information about image type.  NULL means
58				 * that no image manager owns this image:  the
59				 * image was deleted. */
60    ClientData masterData;	/* One-word argument to pass to image mgr
61				 * when dealing with the master, as opposed
62				 * to instances. */
63    int width, height;		/* Last known dimensions for image. */
64    Tcl_HashTable *tablePtr;	/* Pointer to hash table containing image
65				 * (the imageTable field in some TkMainInfo
66				 * structure). */
67    Tcl_HashEntry *hPtr;	/* Hash entry in mainPtr->imageTable for
68				 * this structure (used to delete the hash
69				 * entry). */
70    Image *instancePtr;		/* Pointer to first in list of instances
71				 * derived from this name. */
72    int deleted;		/* Flag set when image is being deleted. */
73    TkWindow *winPtr;		/* Main window of interpreter (used to
74				 * detect when the world is falling apart.) */
75} ImageMaster;
76
77typedef struct ThreadSpecificData {
78    Tk_ImageType *imageTypeList;/* First in a list of all known image
79				 * types. */
80    Tk_ImageType *oldImageTypeList;/* First in a list of all known old-style image
81				 * types. */
82} ThreadSpecificData;
83static Tcl_ThreadDataKey dataKey;
84
85/*
86 * Prototypes for local procedures:
87 */
88
89static void	DeleteImage _ANSI_ARGS_((ImageMaster *masterPtr));
90static void	EventuallyDeleteImage _ANSI_ARGS_((ImageMaster *masterPtr,
91						   int forgetHashEntryNow));
92
93/*
94 *----------------------------------------------------------------------
95 *
96 * Tk_CreateOldImageType, Tk_CreateImageType --
97 *
98 *	This procedure is invoked by an image manager to tell Tk about
99 *	a new kind of image and the procedures that manage the new type.
100 *	The procedure is typically invoked during Tcl_AppInit.
101 *
102 * Results:
103 *	None.
104 *
105 * Side effects:
106 *	The new image type is entered into a table used in the "image
107 *	create" command.
108 *
109 *----------------------------------------------------------------------
110 */
111
112void
113Tk_CreateOldImageType(typePtr)
114    Tk_ImageType *typePtr;	/* Structure describing the type.  All of
115				 * the fields except "nextPtr" must be filled
116				 * in by caller.  Must not have been passed
117				 * to Tk_CreateImageType previously. */
118{
119    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
120            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
121
122    typePtr->nextPtr = tsdPtr->oldImageTypeList;
123    tsdPtr->oldImageTypeList = typePtr;
124}
125
126void
127Tk_CreateImageType(typePtr)
128    Tk_ImageType *typePtr;	/* Structure describing the type.  All of
129				 * the fields except "nextPtr" must be filled
130				 * in by caller.  Must not have been passed
131				 * to Tk_CreateImageType previously. */
132{
133    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
134            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
135
136    typePtr->nextPtr = tsdPtr->imageTypeList;
137    tsdPtr->imageTypeList = typePtr;
138}
139
140/*
141 *----------------------------------------------------------------------
142 *
143 * Tk_ImageObjCmd --
144 *
145 *	This procedure is invoked to process the "image" Tcl command.
146 *	See the user documentation for details on what it does.
147 *
148 * Results:
149 *	A standard Tcl result.
150 *
151 * Side effects:
152 *	See the user documentation.
153 *
154 *----------------------------------------------------------------------
155 */
156
157int
158Tk_ImageObjCmd(clientData, interp, objc, objv)
159    ClientData clientData;	/* Main window associated with interpreter. */
160    Tcl_Interp *interp;		/* Current interpreter. */
161    int objc;			/* Number of arguments. */
162    Tcl_Obj *CONST objv[];	/* Argument strings. */
163{
164    static CONST char *imageOptions[] = {
165	"create", "delete", "height", "inuse", "names", "type", "types",
166	    "width", (char *) NULL
167    };
168    enum options {
169	IMAGE_CREATE, IMAGE_DELETE, IMAGE_HEIGHT, IMAGE_INUSE, IMAGE_NAMES,
170	IMAGE_TYPE, IMAGE_TYPES, IMAGE_WIDTH
171    };
172    TkWindow *winPtr = (TkWindow *) clientData;
173    int i, new, firstOption,  index;
174    Tk_ImageType *typePtr;
175    ImageMaster *masterPtr;
176    Image *imagePtr;
177    Tcl_HashEntry *hPtr;
178    Tcl_HashSearch search;
179    char idString[16 + TCL_INTEGER_SPACE], *name;
180    TkDisplay *dispPtr = winPtr->dispPtr;
181    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
182            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
183
184    if (objc < 2) {
185	Tcl_WrongNumArgs(interp, 1, objv, "option ?args?");
186	return TCL_ERROR;
187    }
188
189    if (Tcl_GetIndexFromObj(interp, objv[1], imageOptions, "option", 0,
190	    &index) != TCL_OK) {
191	return TCL_ERROR;
192    }
193    switch ((enum options) index) {
194	case IMAGE_CREATE: {
195	    char *arg;
196	    Tcl_Obj **args;
197	    int oldimage = 0;
198	    if (objc < 3) {
199		Tcl_WrongNumArgs(interp, 2, objv, "type ?name? ?options?");
200		return TCL_ERROR;
201	    }
202
203	    /*
204	     * Look up the image type.
205	     */
206
207	    arg = Tcl_GetString(objv[2]);
208	    for (typePtr = tsdPtr->imageTypeList; typePtr != NULL;
209		 typePtr = typePtr->nextPtr) {
210		if ((*arg == typePtr->name[0])
211			&& (strcmp(arg, typePtr->name) == 0)) {
212		    break;
213		}
214	    }
215	    if (typePtr == NULL) {
216		oldimage = 1;
217		for (typePtr = tsdPtr->oldImageTypeList; typePtr != NULL;
218		     typePtr = typePtr->nextPtr) {
219		    if ((*arg == typePtr->name[0])
220			    && (strcmp(arg, typePtr->name) == 0)) {
221			break;
222		    }
223		}
224	    }
225	    if (typePtr == NULL) {
226		Tcl_AppendResult(interp, "image type \"", arg,
227			"\" doesn't exist", (char *) NULL);
228		return TCL_ERROR;
229	    }
230
231	    /*
232	     * Figure out a name to use for the new image.
233	     */
234
235	    if ((objc == 3) || (*(arg = Tcl_GetString(objv[3])) == '-')) {
236                Tcl_CmdInfo dummy;
237                do {
238		    dispPtr->imageId++;
239		    sprintf(idString, "image%d", dispPtr->imageId);
240		    name = idString;
241                } while (Tcl_GetCommandInfo(interp, name, &dummy) != 0);
242		firstOption = 3;
243	    } else {
244		TkWindow *topWin;
245
246		name = arg;
247		firstOption = 4;
248		/*
249		 * Need to check if the _command_ that we are about to
250		 * create is the name of the current master widget
251		 * command (normally "." but could have been renamed)
252		 * and fail in that case before a really nasty and
253		 * hard to stop crash happens.
254		 */
255		topWin = (TkWindow *) TkToplevelWindowForCommand(interp, name);
256		if (topWin != NULL && winPtr->mainPtr->winPtr == topWin) {
257		    Tcl_AppendResult(interp, "images may not be named the ",
258			    "same as the main window", (char *) NULL);
259		    return TCL_ERROR;
260		}
261	    }
262
263	    /*
264	     * Create the data structure for the new image.
265	     */
266
267	    hPtr = Tcl_CreateHashEntry(&winPtr->mainPtr->imageTable,
268		    name, &new);
269	    if (new) {
270		masterPtr = (ImageMaster *) ckalloc(sizeof(ImageMaster));
271		masterPtr->typePtr = NULL;
272		masterPtr->masterData = NULL;
273		masterPtr->width = masterPtr->height = 1;
274		masterPtr->tablePtr = &winPtr->mainPtr->imageTable;
275		masterPtr->hPtr = hPtr;
276		masterPtr->instancePtr = NULL;
277		masterPtr->deleted = 0;
278		masterPtr->winPtr = winPtr->mainPtr->winPtr;
279		Tcl_Preserve((ClientData) masterPtr->winPtr);
280		Tcl_SetHashValue(hPtr, masterPtr);
281	    } else {
282		/*
283		 * An image already exists by this name.  Disconnect the
284		 * instances from the master.
285		 */
286
287		masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);
288		if (masterPtr->typePtr != NULL) {
289		    for (imagePtr = masterPtr->instancePtr; imagePtr != NULL;
290			 imagePtr = imagePtr->nextPtr) {
291			(*masterPtr->typePtr->freeProc)(
292			    imagePtr->instanceData, imagePtr->display);
293			(*imagePtr->changeProc)(imagePtr->widgetClientData,
294				0, 0, masterPtr->width, masterPtr->height,
295				masterPtr->width, masterPtr->height);
296		    }
297		    (*masterPtr->typePtr->deleteProc)(masterPtr->masterData);
298		    masterPtr->typePtr = NULL;
299		}
300	    }
301
302	    /*
303	     * Call the image type manager so that it can perform its own
304	     * initialization, then re-"get" for any existing instances of
305	     * the image.
306	     */
307
308	    objv += firstOption;
309	    objc -= firstOption;
310	    args = (Tcl_Obj **) objv;
311	    if (oldimage) {
312		int i;
313		args = (Tcl_Obj **) ckalloc((objc+1) * sizeof(char *));
314		for (i = 0; i < objc; i++) {
315		    args[i] = (Tcl_Obj *) Tcl_GetString(objv[i]);
316		}
317		args[objc] = NULL;
318	    }
319	    Tcl_Preserve((ClientData) masterPtr);
320	    if ((*typePtr->createProc)(interp, name, objc,
321		    args, typePtr, (Tk_ImageMaster) masterPtr,
322		    &masterPtr->masterData) != TCL_OK) {
323		EventuallyDeleteImage(masterPtr, 0);
324		Tcl_Release((ClientData) masterPtr);
325		if (oldimage) {
326		    ckfree((char *) args);
327		}
328		return TCL_ERROR;
329	    }
330	    Tcl_Release((ClientData) masterPtr);
331	    if (oldimage) {
332		ckfree((char *) args);
333	    }
334	    masterPtr->typePtr = typePtr;
335	    for (imagePtr = masterPtr->instancePtr; imagePtr != NULL;
336		 imagePtr = imagePtr->nextPtr) {
337		imagePtr->instanceData = (*typePtr->getProc)(
338		    imagePtr->tkwin, masterPtr->masterData);
339	    }
340	    Tcl_SetResult(interp,
341		    Tcl_GetHashKey(&winPtr->mainPtr->imageTable, hPtr),
342		    TCL_STATIC);
343	    break;
344	}
345	case IMAGE_DELETE: {
346	    for (i = 2; i < objc; i++) {
347		char *arg = Tcl_GetString(objv[i]);
348		hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->imageTable, arg);
349		if (hPtr == NULL) {
350		    Tcl_AppendResult(interp, "image \"", arg,
351			    "\" doesn't exist", (char *) NULL);
352		    return TCL_ERROR;
353		}
354		DeleteImage((ImageMaster *) Tcl_GetHashValue(hPtr));
355	    }
356	    break;
357	}
358	case IMAGE_HEIGHT: {
359	    char *arg;
360	    if (objc != 3) {
361		Tcl_WrongNumArgs(interp, 2, objv, "name");
362		return TCL_ERROR;
363	    }
364	    arg = Tcl_GetString(objv[2]);
365	    hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->imageTable, arg);
366	    if (hPtr == NULL) {
367		Tcl_AppendResult(interp, "image \"", arg,
368			"\" doesn't exist", (char *) NULL);
369		return TCL_ERROR;
370	    }
371	    masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);
372	    Tcl_SetIntObj(Tcl_GetObjResult(interp), masterPtr->height);
373	    break;
374	}
375
376	case IMAGE_INUSE: {
377	    int count = 0;
378	    char *arg;
379	    if (objc != 3) {
380		Tcl_WrongNumArgs(interp, 2, objv, "name");
381		return TCL_ERROR;
382	    }
383	    arg = Tcl_GetString(objv[2]);
384	    hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->imageTable, arg);
385	    if (hPtr == NULL) {
386		Tcl_AppendResult(interp, "image \"", arg,
387			"\" doesn't exist", (char *) NULL);
388		return TCL_ERROR;
389	    }
390	    masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);
391	    if (masterPtr->typePtr != NULL && masterPtr->instancePtr != NULL) {
392		count = 1;
393	    }
394	    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), count);
395	    break;
396	}
397
398	case IMAGE_NAMES: {
399	    if (objc != 2) {
400		Tcl_WrongNumArgs(interp, 2, objv, NULL);
401		return TCL_ERROR;
402	    }
403	    hPtr = Tcl_FirstHashEntry(&winPtr->mainPtr->imageTable, &search);
404	    for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
405		Tcl_AppendElement(interp, Tcl_GetHashKey(
406		    &winPtr->mainPtr->imageTable, hPtr));
407	    }
408	    break;
409	}
410
411	case IMAGE_TYPE: {
412	    char *arg;
413	    if (objc != 3) {
414		Tcl_WrongNumArgs(interp, 2, objv, "name");
415		return TCL_ERROR;
416	    }
417	    arg = Tcl_GetString(objv[2]);
418	    hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->imageTable, arg);
419	    if (hPtr == NULL) {
420		Tcl_AppendResult(interp, "image \"", arg,
421			"\" doesn't exist", (char *) NULL);
422		return TCL_ERROR;
423	    }
424	    masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);
425	    if (masterPtr->typePtr != NULL) {
426		Tcl_SetResult(interp, masterPtr->typePtr->name, TCL_STATIC);
427	    }
428	    break;
429	}
430	case IMAGE_TYPES: {
431	    if (objc != 2) {
432		Tcl_WrongNumArgs(interp, 2, objv, NULL);
433		return TCL_ERROR;
434	    }
435	    for (typePtr = tsdPtr->imageTypeList; typePtr != NULL;
436		 typePtr = typePtr->nextPtr) {
437		Tcl_AppendElement(interp, typePtr->name);
438	    }
439	    for (typePtr = tsdPtr->oldImageTypeList; typePtr != NULL;
440		 typePtr = typePtr->nextPtr) {
441		Tcl_AppendElement(interp, typePtr->name);
442	    }
443	    break;
444	}
445	case IMAGE_WIDTH: {
446	    char *arg;
447	    if (objc != 3) {
448		Tcl_WrongNumArgs(interp, 2, objv, "name");
449		return TCL_ERROR;
450	    }
451	    arg = Tcl_GetString(objv[2]);
452	    hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->imageTable, arg);
453	    if (hPtr == NULL) {
454		Tcl_AppendResult(interp, "image \"", arg,
455			"\" doesn't exist", (char *) NULL);
456		return TCL_ERROR;
457	    }
458	    masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);
459	    Tcl_SetIntObj(Tcl_GetObjResult(interp), masterPtr->width);
460	    break;
461	}
462    }
463    return TCL_OK;
464}
465
466/*
467 *----------------------------------------------------------------------
468 *
469 * Tk_ImageChanged --
470 *
471 *	This procedure is called by an image manager whenever something
472 *	has happened that requires the image to be redrawn (some of its
473 *	pixels have changed, or its size has changed).
474 *
475 * Results:
476 *	None.
477 *
478 * Side effects:
479 *	Any widgets that display the image are notified so that they
480 *	can redisplay themselves as appropriate.
481 *
482 *----------------------------------------------------------------------
483 */
484
485void
486Tk_ImageChanged(imageMaster, x, y, width, height, imageWidth,
487	imageHeight)
488    Tk_ImageMaster imageMaster;	/* Image that needs redisplay. */
489    int x, y;			/* Coordinates of upper-left pixel of
490				 * region of image that needs to be
491				 * redrawn. */
492    int width, height;		/* Dimensions (in pixels) of region of
493				 * image to redraw.  If either dimension
494				 * is zero then the image doesn't need to
495				 * be redrawn (perhaps all that happened is
496				 * that its size changed). */
497    int imageWidth, imageHeight;/* New dimensions of image. */
498{
499    ImageMaster *masterPtr = (ImageMaster *) imageMaster;
500    Image *imagePtr;
501
502    masterPtr->width = imageWidth;
503    masterPtr->height = imageHeight;
504    for (imagePtr = masterPtr->instancePtr; imagePtr != NULL;
505	 imagePtr = imagePtr->nextPtr) {
506	(*imagePtr->changeProc)(imagePtr->widgetClientData, x, y,
507		width, height, imageWidth, imageHeight);
508    }
509}
510
511/*
512 *----------------------------------------------------------------------
513 *
514 * Tk_NameOfImage --
515 *
516 *	Given a token for an image master, this procedure returns
517 *	the name of the image.
518 *
519 * Results:
520 *	The return value is the string name for imageMaster.
521 *
522 * Side effects:
523 *	None.
524 *
525 *----------------------------------------------------------------------
526 */
527
528CONST char *
529Tk_NameOfImage(imageMaster)
530    Tk_ImageMaster imageMaster;		/* Token for image. */
531{
532    ImageMaster *masterPtr = (ImageMaster *) imageMaster;
533
534    if (masterPtr->hPtr == NULL) {
535	return NULL;
536    }
537    return Tcl_GetHashKey(masterPtr->tablePtr, masterPtr->hPtr);
538}
539
540/*
541 *----------------------------------------------------------------------
542 *
543 * Tk_GetImage --
544 *
545 *	This procedure is invoked by a widget when it wants to use
546 *	a particular image in a particular window.
547 *
548 * Results:
549 *	The return value is a token for the image.  If there is no image
550 *	by the given name, then NULL is returned and an error message is
551 *	left in the interp's result.
552 *
553 * Side effects:
554 *	Tk records the fact that the widget is using the image, and
555 *	it will invoke changeProc later if the widget needs redisplay
556 *	(i.e. its size changes or some of its pixels change).  The
557 *	caller must eventually invoke Tk_FreeImage when it no longer
558 *	needs the image.
559 *
560 *----------------------------------------------------------------------
561 */
562
563Tk_Image
564Tk_GetImage(interp, tkwin, name, changeProc, clientData)
565    Tcl_Interp *interp;		/* Place to leave error message if image
566				 * can't be found. */
567    Tk_Window tkwin;		/* Token for window in which image will
568				 * be used. */
569    CONST char *name;		/* Name of desired image. */
570    Tk_ImageChangedProc *changeProc;
571				/* Procedure to invoke when redisplay is
572				 * needed because image's pixels or size
573				 * changed. */
574    ClientData clientData;	/* One-word argument to pass to damageProc. */
575{
576    Tcl_HashEntry *hPtr;
577    ImageMaster *masterPtr;
578    Image *imagePtr;
579
580    hPtr = Tcl_FindHashEntry(&((TkWindow *) tkwin)->mainPtr->imageTable, name);
581    if (hPtr == NULL) {
582	goto noSuchImage;
583    }
584    masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);
585    if (masterPtr->typePtr == NULL) {
586	goto noSuchImage;
587    }
588    imagePtr = (Image *) ckalloc(sizeof(Image));
589    imagePtr->tkwin = tkwin;
590    imagePtr->display = Tk_Display(tkwin);
591    imagePtr->masterPtr = masterPtr;
592    imagePtr->instanceData =
593	    (*masterPtr->typePtr->getProc)(tkwin, masterPtr->masterData);
594    imagePtr->changeProc = changeProc;
595    imagePtr->widgetClientData = clientData;
596    imagePtr->nextPtr = masterPtr->instancePtr;
597    masterPtr->instancePtr = imagePtr;
598    return (Tk_Image) imagePtr;
599
600    noSuchImage:
601    Tcl_AppendResult(interp, "image \"", name, "\" doesn't exist",
602	    (char *) NULL);
603    return NULL;
604}
605
606/*
607 *----------------------------------------------------------------------
608 *
609 * Tk_FreeImage --
610 *
611 *	This procedure is invoked by a widget when it no longer needs
612 *	an image acquired by a previous call to Tk_GetImage.  For each
613 *	call to Tk_GetImage there must be exactly one call to Tk_FreeImage.
614 *
615 * Results:
616 *	None.
617 *
618 * Side effects:
619 *	The association between the image and the widget is removed.
620 *
621 *----------------------------------------------------------------------
622 */
623
624void
625Tk_FreeImage(image)
626    Tk_Image image;		/* Token for image that is no longer
627				 * needed by a widget. */
628{
629    Image *imagePtr = (Image *) image;
630    ImageMaster *masterPtr = imagePtr->masterPtr;
631    Image *prevPtr;
632
633    /*
634     * Clean up the particular instance.
635     */
636
637    if (masterPtr->typePtr != NULL) {
638	(*masterPtr->typePtr->freeProc)(imagePtr->instanceData,
639		imagePtr->display);
640    }
641    prevPtr = masterPtr->instancePtr;
642    if (prevPtr == imagePtr) {
643	masterPtr->instancePtr = imagePtr->nextPtr;
644    } else {
645	while (prevPtr->nextPtr != imagePtr) {
646	    prevPtr = prevPtr->nextPtr;
647	}
648	prevPtr->nextPtr = imagePtr->nextPtr;
649    }
650    ckfree((char *) imagePtr);
651
652    /*
653     * If there are no more instances left for the master, and if the
654     * master image has been deleted, then delete the master too.
655     */
656
657    if ((masterPtr->typePtr == NULL) && (masterPtr->instancePtr == NULL)) {
658	if (masterPtr->hPtr != NULL) {
659	    Tcl_DeleteHashEntry(masterPtr->hPtr);
660	}
661	Tcl_Release(masterPtr->winPtr);
662	ckfree((char *) masterPtr);
663    }
664}
665
666/*
667 *----------------------------------------------------------------------
668 *
669 * Tk_PostscriptImage --
670 *
671 *	This procedure is called by widgets that contain images in order
672 *	to redisplay an image on the screen or an off-screen pixmap.
673 *
674 * Results:
675 *	None.
676 *
677 * Side effects:
678 *	The image's manager is notified, and it redraws the desired
679 *	portion of the image before returning.
680 *
681 *----------------------------------------------------------------------
682 */
683
684int
685Tk_PostscriptImage(image, interp, tkwin, psinfo, x, y, width, height, prepass)
686    Tk_Image image;		/* Token for image to redisplay. */
687    Tcl_Interp *interp;
688    Tk_Window tkwin;
689    Tk_PostscriptInfo psinfo;	/* postscript info */
690    int x, y;			/* Upper-left pixel of region in image that
691				 * needs to be redisplayed. */
692    int width, height;		/* Dimensions of region to redraw. */
693    int prepass;
694{
695    Image *imagePtr = (Image *) image;
696    int result;
697    XImage *ximage;
698    Pixmap pmap;
699    GC newGC;
700    XGCValues gcValues;
701
702    if (imagePtr->masterPtr->typePtr == NULL) {
703	/*
704	 * No master for image, so nothing to display on postscript.
705	 */
706	return TCL_OK;
707    }
708
709    /*
710     * Check if an image specific postscript-generation function
711     * exists; otherwise go on with generic code.
712     */
713
714    if (imagePtr->masterPtr->typePtr->postscriptProc != NULL) {
715	return (*imagePtr->masterPtr->typePtr->postscriptProc)(
716	    imagePtr->masterPtr->masterData, interp, tkwin, psinfo,
717	    x, y, width, height, prepass);
718    }
719
720    if (prepass) {
721	return TCL_OK;
722    }
723
724    /*
725     * Create a Pixmap, tell the image to redraw itself there, and then
726     * generate an XImage from the Pixmap.  We can then read pixel
727     * values out of the XImage.
728     */
729
730    pmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
731                        width, height, Tk_Depth(tkwin));
732
733    gcValues.foreground = WhitePixelOfScreen(Tk_Screen(tkwin));
734    newGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
735    if (newGC != None) {
736	XFillRectangle(Tk_Display(tkwin), pmap, newGC,
737		0, 0, (unsigned int)width, (unsigned int)height);
738	Tk_FreeGC(Tk_Display(tkwin), newGC);
739    }
740
741    Tk_RedrawImage(image, x, y, width, height, pmap, 0, 0);
742
743    ximage = XGetImage(Tk_Display(tkwin), pmap, 0, 0,
744	    (unsigned int)width, (unsigned int)height, AllPlanes, ZPixmap);
745
746    Tk_FreePixmap(Tk_Display(tkwin), pmap);
747
748    if (ximage == NULL) {
749	/* The XGetImage() function is apparently not
750	 * implemented on this system. Just ignore it.
751	 */
752	return TCL_OK;
753    }
754    result = TkPostscriptImage(interp, tkwin, psinfo, ximage, x, y,
755	    width, height);
756
757    XDestroyImage(ximage);
758    return result;
759}
760
761/*
762 *----------------------------------------------------------------------
763 *
764 * Tk_RedrawImage --
765 *
766 *	This procedure is called by widgets that contain images in order
767 *	to redisplay an image on the screen or an off-screen pixmap.
768 *
769 * Results:
770 *	None.
771 *
772 * Side effects:
773 *	The image's manager is notified, and it redraws the desired
774 *	portion of the image before returning.
775 *
776 *----------------------------------------------------------------------
777 */
778
779void
780Tk_RedrawImage(image, imageX, imageY, width, height, drawable,
781	drawableX, drawableY)
782    Tk_Image image;		/* Token for image to redisplay. */
783    int imageX, imageY;		/* Upper-left pixel of region in image that
784				 * needs to be redisplayed. */
785    int width, height;		/* Dimensions of region to redraw. */
786    Drawable drawable;		/* Drawable in which to display image
787				 * (window or pixmap).  If this is a pixmap,
788				 * it must have the same depth as the window
789				 * used in the Tk_GetImage call for the
790				 * image. */
791    int drawableX, drawableY;	/* Coordinates in drawable that correspond
792				 * to imageX and imageY. */
793{
794    Image *imagePtr = (Image *) image;
795
796    if (imagePtr->masterPtr->typePtr == NULL) {
797	/*
798	 * No master for image, so nothing to display.
799	 */
800
801	return;
802    }
803
804    /*
805     * Clip the redraw area to the area of the image.
806     */
807
808    if (imageX < 0) {
809	width += imageX;
810	drawableX -= imageX;
811	imageX = 0;
812    }
813    if (imageY < 0) {
814	height += imageY;
815	drawableY -= imageY;
816	imageY = 0;
817    }
818    if ((imageX + width) > imagePtr->masterPtr->width) {
819	width = imagePtr->masterPtr->width - imageX;
820    }
821    if ((imageY + height) > imagePtr->masterPtr->height) {
822	height = imagePtr->masterPtr->height - imageY;
823    }
824    (*imagePtr->masterPtr->typePtr->displayProc)(
825	    imagePtr->instanceData, imagePtr->display, drawable,
826	    imageX, imageY, width, height, drawableX, drawableY);
827}
828
829/*
830 *----------------------------------------------------------------------
831 *
832 * Tk_SizeOfImage --
833 *
834 *	This procedure returns the current dimensions of an image.
835 *
836 * Results:
837 *	The width and height of the image are returned in *widthPtr
838 *	and *heightPtr.
839 *
840 * Side effects:
841 *	None.
842 *
843 *----------------------------------------------------------------------
844 */
845
846void
847Tk_SizeOfImage(image, widthPtr, heightPtr)
848    Tk_Image image;		/* Token for image whose size is wanted. */
849    int *widthPtr;		/* Return width of image here. */
850    int *heightPtr;		/* Return height of image here. */
851{
852    Image *imagePtr = (Image *) image;
853
854    *widthPtr = imagePtr->masterPtr->width;
855    *heightPtr = imagePtr->masterPtr->height;
856}
857
858/*
859 *----------------------------------------------------------------------
860 *
861 * Tk_DeleteImage --
862 *
863 *	Given the name of an image, this procedure destroys the
864 *	image.
865 *
866 * Results:
867 *	None.
868 *
869 * Side effects:
870 *	The image is destroyed; existing instances will display as
871 *	blank areas.  If no such image exists then the procedure does
872 *	nothing.
873 *
874 *----------------------------------------------------------------------
875 */
876
877void
878Tk_DeleteImage(interp, name)
879    Tcl_Interp *interp;		/* Interpreter in which the image was
880				 * created. */
881    CONST char *name;		/* Name of image. */
882{
883    Tcl_HashEntry *hPtr;
884    TkWindow *winPtr;
885
886    winPtr = (TkWindow *) Tk_MainWindow(interp);
887    if (winPtr == NULL) {
888	return;
889    }
890    hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->imageTable, name);
891    if (hPtr == NULL) {
892	return;
893    }
894    DeleteImage((ImageMaster *)Tcl_GetHashValue(hPtr));
895}
896
897/*
898 *----------------------------------------------------------------------
899 *
900 * DeleteImage --
901 *
902 *	This procedure is responsible for deleting an image.
903 *
904 * Results:
905 *	None.
906 *
907 * Side effects:
908 *	The connection is dropped between instances of this image and
909 *	an image master.  Image instances will redisplay themselves
910 *	as empty areas, but existing instances will not be deleted.
911 *
912 *----------------------------------------------------------------------
913 */
914
915static void
916DeleteImage(masterPtr)
917    ImageMaster *masterPtr;	/* Pointer to main data structure for image. */
918{
919    Image *imagePtr;
920    Tk_ImageType *typePtr;
921
922    typePtr = masterPtr->typePtr;
923    masterPtr->typePtr = NULL;
924    if (typePtr != NULL) {
925	for (imagePtr = masterPtr->instancePtr; imagePtr != NULL;
926		imagePtr = imagePtr->nextPtr) {
927	   (*typePtr->freeProc)(imagePtr->instanceData,
928		   imagePtr->display);
929	   (*imagePtr->changeProc)(imagePtr->widgetClientData, 0, 0,
930		    masterPtr->width, masterPtr->height, masterPtr->width,
931		    masterPtr->height);
932	}
933	(*typePtr->deleteProc)(masterPtr->masterData);
934    }
935    if (masterPtr->instancePtr == NULL) {
936	if (masterPtr->hPtr != NULL) {
937	    Tcl_DeleteHashEntry(masterPtr->hPtr);
938	}
939	Tcl_Release((ClientData) masterPtr->winPtr);
940	ckfree((char *) masterPtr);
941    }
942}
943
944/*
945 *----------------------------------------------------------------------
946 *
947 * EventuallyDeleteImage --
948 *
949 *	Arrange for an image to be deleted when it is safe to do so.
950 *
951 * Results:
952 *	None.
953 *
954 * Side effects:
955 *	Image will get freed, though not until it is no longer
956 *	Tcl_Preserve()d by anything.  May be called multiple times on
957 *	the same image without ill effects.
958 *
959 *----------------------------------------------------------------------
960 */
961
962static void
963EventuallyDeleteImage(masterPtr, forgetHashEntryNow)
964    ImageMaster *masterPtr;	/* Pointer to main data structure for image. */
965    int forgetHashEntryNow;
966{
967    if (forgetHashEntryNow) {
968	masterPtr->hPtr = NULL;
969    }
970    if (!masterPtr->deleted) {
971	masterPtr->deleted = 1;
972	Tcl_EventuallyFree((ClientData) masterPtr,
973		(Tcl_FreeProc *)DeleteImage);
974    }
975}
976
977/*
978 *----------------------------------------------------------------------
979 *
980 * TkDeleteAllImages --
981 *
982 *	This procedure is called when an application is deleted.  It
983 *	calls back all of the managers for all images so that they
984 *	can cleanup, then it deletes all of Tk's internal information
985 *	about images.
986 *
987 * Results:
988 *	None.
989 *
990 * Side effects:
991 *	All information for all images gets deleted.
992 *
993 *----------------------------------------------------------------------
994 */
995
996void
997TkDeleteAllImages(mainPtr)
998    TkMainInfo *mainPtr;	/* Structure describing application that is
999				 * going away. */
1000{
1001    Tcl_HashSearch search;
1002    Tcl_HashEntry *hPtr;
1003
1004    for (hPtr = Tcl_FirstHashEntry(&mainPtr->imageTable, &search);
1005	    hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
1006	EventuallyDeleteImage((ImageMaster *) Tcl_GetHashValue(hPtr), 1);
1007    }
1008    Tcl_DeleteHashTable(&mainPtr->imageTable);
1009}
1010
1011/*
1012 *----------------------------------------------------------------------
1013 *
1014 * Tk_GetImageMasterData --
1015 *
1016 *	Given the name of an image, this procedure returns the type
1017 *	of the image and the clientData associated with its master.
1018 *
1019 * Results:
1020 *	If there is no image by the given name, then NULL is returned
1021 *	and a NULL value is stored at *typePtrPtr.  Otherwise the return
1022 *	value is the clientData returned by the createProc when the
1023 *	image was created and a pointer to the type structure for the
1024 *	image is stored at *typePtrPtr.
1025 *
1026 * Side effects:
1027 *	None.
1028 *
1029 *----------------------------------------------------------------------
1030 */
1031
1032ClientData
1033Tk_GetImageMasterData(interp, name, typePtrPtr)
1034    Tcl_Interp *interp;		/* Interpreter in which the image was
1035				 * created. */
1036    CONST char *name;		/* Name of image. */
1037    Tk_ImageType **typePtrPtr;	/* Points to location to fill in with
1038				 * pointer to type information for image. */
1039{
1040    Tcl_HashEntry *hPtr;
1041    TkWindow *winPtr;
1042    ImageMaster *masterPtr;
1043
1044    winPtr = (TkWindow *) Tk_MainWindow(interp);
1045    hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->imageTable, name);
1046    if (hPtr == NULL) {
1047	*typePtrPtr = NULL;
1048	return NULL;
1049    }
1050    masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);
1051    *typePtrPtr = masterPtr->typePtr;
1052    return masterPtr->masterData;
1053}
1054
1055/*
1056 *----------------------------------------------------------------------
1057 *
1058 * Tk_SetTSOrigin --
1059 *
1060 *	Set the pattern origin of the tile to a common point (i.e. the
1061 *	origin (0,0) of the top level window) so that tiles from two
1062 *	different widgets will match up.  This done by setting the
1063 *	GCTileStipOrigin field is set to the translated origin of the
1064 *	toplevel window in the hierarchy.
1065 *
1066 * Results:
1067 *	None.
1068 *
1069 * Side Effects:
1070 *	The GCTileStipOrigin is reset in the GC.  This will cause the
1071 *	tile origin to change when the GC is used for drawing.
1072 *
1073 *----------------------------------------------------------------------
1074 */
1075/*ARGSUSED*/
1076void
1077Tk_SetTSOrigin(tkwin, gc, x, y)
1078    Tk_Window tkwin;
1079    GC gc;
1080    int x, y;
1081{
1082    while (!Tk_TopWinHierarchy(tkwin)) {
1083	x -= Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
1084	y -= Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
1085	tkwin = Tk_Parent(tkwin);
1086    }
1087    XSetTSOrigin(Tk_Display(tkwin), gc, x, y);
1088}
1089
1090