1/*
2 * tkImgPmap.c --
3 *
4 *	This file implements images of type "pixmap" for Tk.
5 *
6 * Copyright (c) 1996, Expert Interface Technologies
7 *
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10 *
11 */
12
13#include <ctype.h>
14#include <stdlib.h>
15#include <string.h>
16#include "pixmapInt.h"
17#include "tkimg.h"
18
19#if defined(__WIN32__) && !defined (__GNUC__)
20#define strncasecmp strnicmp
21#endif
22
23#ifndef MAC_TCL
24#include <sys/types.h>
25#include <sys/stat.h>
26#endif
27
28#define UCHAR(c) ((unsigned char) (c))
29
30/*
31 * Prototypes for procedures used only locally in this file:
32 */
33
34static int TkimgXpmCreate(Tcl_Interp *interp,
35	const char *name, int argc, Tcl_Obj *objv[],
36	const Tk_ImageType *typePtr, Tk_ImageMaster master,
37	ClientData *clientDataPtr);
38static ClientData TkimgXpmGet(Tk_Window tkwin,
39	ClientData clientData);
40static void TkimgXpmDisplay(ClientData clientData,
41	Display *display, Drawable drawable,
42	int imageX, int imageY, int width, int height,
43	int drawableX, int drawableY);
44static void TkimgXpmFree(ClientData clientData,
45	Display *display);
46static void TkimgXpmDelete(ClientData clientData);
47static int TkimgXpmCmd(ClientData clientData,
48	Tcl_Interp *interp, int argc, CONST84 char **argv);
49static void TkimgXpmCmdDeletedProc(
50	ClientData clientData);
51static void TkimgXpmConfigureInstance(
52	PixmapInstance *instancePtr);
53static int TkimgXpmConfigureMaster(
54	PixmapMaster *masterPtr, int argc, CONST84 char **argv,
55	int flags);
56static int TkimgXpmGetData(Tcl_Interp *interp,
57	PixmapMaster *masterPtr);
58static CONST84 char **TkimgXpmGetDataFromFile(Tcl_Interp *interp,
59	char *string, int *numLines_return);
60static CONST84 char **TkimgXpmGetDataFromString(Tcl_Interp *interp,
61	char *string, int *numLines_return);
62static void TkimgXpmGetPixmapFromData(
63	Tcl_Interp *interp,
64	PixmapMaster *masterPtr,
65	PixmapInstance *instancePtr);
66static char *GetType(char *colorDefn,
67	int *type_ret);
68static char *GetColor(char *colorDefn,
69	char *colorName, int *type_ret);
70
71/*
72 * Information used for parsing configuration specs:
73 */
74
75static Tk_ConfigSpec configSpecs[] = {
76    {TK_CONFIG_STRING, "-data", (char *) NULL, (char *) NULL,
77	(char *) NULL, Tk_Offset(PixmapMaster, dataString), TK_CONFIG_NULL_OK},
78    {TK_CONFIG_STRING, "-file", (char *) NULL, (char *) NULL,
79	(char *) NULL, Tk_Offset(PixmapMaster, fileString), TK_CONFIG_NULL_OK},
80    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
81	(char *) NULL, 0, 0}
82};
83
84Tk_ImageType imgPixmapImageType = {
85    "pixmap",				/* name */
86    (Tk_ImageCreateProc *) TkimgXpmCreate,/* createProc */
87    TkimgXpmGet,				/* getProc */
88    TkimgXpmDisplay,			/* displayProc */
89    TkimgXpmFree,				/* freeProc */
90    TkimgXpmDelete,			/* deleteProc */
91#ifdef TK_CONFIG_OBJS
92    (Tk_ImagePostscriptProc *) NULL,	/* postscriptProc */
93#endif
94    (Tk_ImageType *) NULL		/* nextPtr */
95};
96
97/*
98 *----------------------------------------------------------------------
99 *
100 * TkimgXpmCreate --
101 *
102 *	This procedure is called by the Tk image code to create "pixmap"
103 *	images.
104 *
105 * Results:
106 *	A standard Tcl result.
107 *
108 * Side effects:
109 *	The data structure for a new image is allocated.
110 *
111 *----------------------------------------------------------------------
112 */
113
114static int
115TkimgXpmCreate(interp, name, argc, objv, typePtr, master, clientDataPtr)
116    Tcl_Interp *interp;		/* Interpreter for application containing
117				 * image. */
118    const char *name;			/* Name to use for image. */
119    int argc;			/* Number of arguments. */
120    Tcl_Obj *objv[];		/* Argument strings for options (doesn't
121				 * include image name or type). */
122    const Tk_ImageType *typePtr;	/* Pointer to our type record (not used). */
123    Tk_ImageMaster master;	/* Token for image, to be used by us in
124				 * later callbacks. */
125    ClientData *clientDataPtr;	/* Store manager's token for image here;
126				 * it will be returned in later callbacks. */
127{
128    PixmapMaster *masterPtr;
129    int i;
130    char *argvbuf[10];
131    CONST84 char **args = (CONST84 char **) argvbuf;
132
133    /*
134     * Convert the objc/objv arguments into string equivalent.
135     */
136    if (argc > 10) {
137	args = (CONST84 char **) ckalloc(argc * sizeof(char *));
138    }
139    for (i = 0; i < argc; i++) {
140	args[i] = tkimg_GetStringFromObj(objv[i], NULL);
141    }
142
143    masterPtr = (PixmapMaster *) ckalloc(sizeof(PixmapMaster));
144    masterPtr->tkMaster = master;
145    masterPtr->interp = interp;
146    masterPtr->imageCmd = Tcl_CreateCommand(interp, name, TkimgXpmCmd,
147	    (ClientData) masterPtr, TkimgXpmCmdDeletedProc);
148
149    masterPtr->fileString = NULL;
150    masterPtr->dataString = NULL;
151    masterPtr->data = NULL;
152    masterPtr->isDataAlloced = 0;
153    masterPtr->instancePtr = NULL;
154
155    if (TkimgXpmConfigureMaster(masterPtr, argc, args, 0) != TCL_OK) {
156	TkimgXpmDelete((ClientData) masterPtr);
157	if (args != ((CONST84 char **) argvbuf)) {
158	    ckfree((char *) args);
159	}
160	return TCL_ERROR;
161    }
162    *clientDataPtr = (ClientData) masterPtr;
163    if (args != ((CONST84 char **) argvbuf)) {
164	ckfree((char *) args);
165    }
166    return TCL_OK;
167}
168
169/*
170 *----------------------------------------------------------------------
171 *
172 * TkimgXpmConfigureMaster --
173 *
174 *	This procedure is called when a pixmap image is created or
175 *	reconfigured.  It process configuration options and resets
176 *	any instances of the image.
177 *
178 * Results:
179 *	A standard Tcl return value.  If TCL_ERROR is returned then
180 *	an error message is left in masterPtr->interp->result.
181 *
182 * Side effects:
183 *	Existing instances of the image will be redisplayed to match
184 *	the new configuration options.
185 *
186 *	If any error occurs, the state of *masterPtr is restored to
187 *	previous state.
188 *
189 *----------------------------------------------------------------------
190 */
191
192static int
193TkimgXpmConfigureMaster(masterPtr, argc, argv, flags)
194    PixmapMaster *masterPtr;	/* Pointer to data structure describing
195				 * overall pixmap image to (reconfigure). */
196    int argc;			/* Number of entries in argv. */
197    CONST84 char **argv;	/* Pairs of configuration options for image. */
198    int flags;			/* Flags to pass to Tk_ConfigureWidget,
199				 * such as TK_CONFIG_ARGV_ONLY. */
200{
201    PixmapInstance *instancePtr;
202    char * oldData, * oldFile;
203
204    oldData = masterPtr->dataString;
205    oldFile = masterPtr->fileString;
206
207    if (Tk_ConfigureWidget(masterPtr->interp, Tk_MainWindow(masterPtr->interp),
208	    configSpecs, argc, argv, (char *) masterPtr, flags)
209	    != TCL_OK) {
210	return TCL_ERROR;
211    }
212
213    if (masterPtr->dataString != NULL ||
214	masterPtr->fileString != NULL) {
215	if (TkimgXpmGetData(masterPtr->interp, masterPtr) != TCL_OK) {
216	    goto error;
217	}
218    } else {
219	Tcl_AppendResult(masterPtr->interp,
220	    "must specify one of -data or -file", NULL);
221	goto error;
222    }
223
224    /*
225     * Cycle through all of the instances of this image, regenerating
226     * the information for each instance.  Then force the image to be
227     * redisplayed everywhere that it is used.
228     */
229    for (instancePtr = masterPtr->instancePtr; instancePtr != NULL;
230	instancePtr = instancePtr->nextPtr) {
231	TkimgXpmConfigureInstance(instancePtr);
232    }
233
234    if (masterPtr->data) {
235	Tk_ImageChanged(masterPtr->tkMaster, 0, 0,
236	    masterPtr->size[0], masterPtr->size[1],
237	    masterPtr->size[0], masterPtr->size[1]);
238    } else {
239	Tk_ImageChanged(masterPtr->tkMaster, 0, 0, 0, 0, 0, 0);
240    }
241
242    return TCL_OK;
243
244  error:
245    /* Restore it to the original (possible valid) mode */
246    if (masterPtr->dataString && masterPtr->dataString != oldData) {
247	ckfree(masterPtr->dataString);
248    }
249    if (masterPtr->fileString && masterPtr->fileString != oldFile) {
250	ckfree(masterPtr->fileString);
251    }
252    masterPtr->dataString = oldData;
253    masterPtr->fileString = oldFile;
254    return TCL_ERROR;
255}
256
257/*
258 *----------------------------------------------------------------------
259 *
260 * TkimgXpmGetData --
261 *
262 *	Given a file name or ASCII string, this procedure parses the
263 *	file or string contents to produce binary data for a pixmap.
264 *
265 * Results:
266 *	If the pixmap description was parsed successfully then the data
267 *	is read into an array of strings. This array will later be used
268 *	to create X Pixmaps for each instance.
269 *
270 * Side effects:
271 *	The masterPtr->data array is allocated when successful. Contents of
272 *	*masterPtr is changed only when successful.
273 *----------------------------------------------------------------------
274 */
275
276static int
277TkimgXpmGetData(interp, masterPtr)
278    Tcl_Interp *interp;			/* For reporting errors. */
279    PixmapMaster *masterPtr;
280{
281    CONST84 char ** data = NULL;
282    int  isAllocated = 0;		/* do we need to free "data"? */
283    int listArgc;
284    CONST84 char ** listArgv = NULL;
285    int numLines;
286    int size[2];
287    int cpp;
288    int ncolors;
289    int code = TCL_OK;
290
291    if (masterPtr->fileString != NULL) {
292	if (Tcl_IsSafe(interp)) {
293	    Tcl_AppendResult(interp, "can't get image from a file in a",
294		    " safe interpreter", (char *) NULL);
295	    return TCL_ERROR;
296	}
297	data = TkimgXpmGetDataFromFile(interp, masterPtr->fileString, &numLines);
298	isAllocated = 1;
299    }
300    else if (masterPtr->dataString != NULL) {
301	data = TkimgXpmGetDataFromString(interp,masterPtr->dataString,&numLines);
302	isAllocated = 1;
303    }
304    else {
305	/* Should have been enforced by TkimgXpmConfigureMaster() */
306	Tcl_Panic("TkimgXpmGetData(): -data and -file are all NULL");
307    }
308
309    if (data == NULL) {
310	/* nothing has been allocated yet. Don't need to goto done */
311	return TCL_ERROR;
312    }
313
314    /* Parse the first line of the data and get info about this pixmap */
315    if (Tcl_SplitList(interp, data[0], &listArgc, &listArgv) != TCL_OK) {
316	code = TCL_ERROR; goto done;
317    }
318
319    if (listArgc < 4) {	/* file format error */
320	code = TCL_ERROR; goto done;
321    }
322
323    if (Tcl_GetInt(interp, listArgv[0], &size[0]) != TCL_OK) {
324	code = TCL_ERROR; goto done;
325    }
326    if (Tcl_GetInt(interp, listArgv[1], &size[1]) != TCL_OK) {
327	code = TCL_ERROR; goto done;
328    }
329    if (Tcl_GetInt(interp, listArgv[2], &ncolors) != TCL_OK) {
330	code = TCL_ERROR; goto done;
331    }
332    if (Tcl_GetInt(interp, listArgv[3], &cpp) != TCL_OK) {
333	code = TCL_ERROR; goto done;
334    }
335
336    if (isAllocated) {
337	if (numLines != size[1] + ncolors + 1) {
338	    /* the number of lines read from the file/data
339	     * is not the same as specified in the data
340	     */
341	    code = TCL_ERROR; goto done;
342	}
343    }
344
345  done:
346    if (code == TCL_OK) {
347	if (masterPtr->isDataAlloced && masterPtr->data) {
348	    ckfree((char*)masterPtr->data);
349	}
350	masterPtr->isDataAlloced = isAllocated;
351	masterPtr->data = (char **) data;
352	masterPtr->size[0] = size[0];
353	masterPtr->size[1] = size[1];
354	masterPtr->cpp = cpp;
355	masterPtr->ncolors = ncolors;
356    } else {
357	if (isAllocated && data) {
358	    ckfree((char*)data);
359	}
360
361	Tcl_ResetResult(interp);
362	Tcl_AppendResult(interp, "File format error", NULL);
363    }
364
365    if (listArgv) {
366	ckfree((char*)listArgv);
367    }
368
369    return code;
370}
371
372static CONST84 char **
373TkimgXpmGetDataFromString(interp, string, numLines_return)
374    Tcl_Interp * interp;
375    char * string;
376    int * numLines_return;
377{
378    int quoted;
379    char * p, * list;
380    int numLines;
381    CONST84 char ** data;
382
383    /* skip the leading blanks (leading blanks are not defined in the
384     * the XPM definition, but skipping them shouldn't hurt. Also, the ability
385     * to skip the leading blanks is good for using in-line XPM data in TCL
386     * scripts
387     */
388    while (isspace(UCHAR(*string))) {
389	++ string;
390    }
391
392    /* parse the header */
393    if (strncmp("/* XPM", string, 6) != 0) {
394	goto error;
395    }
396
397    /* strip the comments */
398    for (quoted = 0, p=string; *p;) {
399	if (!quoted) {
400	    if (*p == '"') {
401		quoted = 1;
402		++ p;
403		continue;
404	    }
405
406	    if (*p == '/' && *(p+1) == '*') {
407		*p++ = ' ';
408		*p++ = ' ';
409		while (1) {
410		    if (*p == 0) {
411			break;
412		    }
413		    if (*p == '*' && *(p+1) == '/') {
414			*p++ = ' ';
415			*p++ = ' ';
416			break;
417		    }
418		    *p++ = ' ';
419		}
420		continue;
421	    }
422	    ++ p;
423	} else {
424	    if (*p == '"') {
425		quoted = 0;
426	    }
427	    ++ p;
428	}
429    }
430
431    /* Search for the opening brace */
432    for (p=string; *p;) {
433	if (*p != '{') {
434	    ++ p;
435	} else {
436	    ++p;
437	    break;
438	}
439    }
440
441    /* Change the buffer in to a proper TCL list */
442    quoted = 0;
443    list = p;
444
445    while (*p) {
446	if (!quoted) {
447	    if (*p == '"') {
448		quoted = 1;
449		++ p;
450		continue;
451	    }
452
453	    if (isspace(UCHAR(*p))) {
454		*p = ' ';
455	    }
456	    else if (*p == ',') {
457		*p = ' ';
458	    }
459	    else if (*p == '}') {
460		*p = 0;
461		break;
462	    }
463	    ++p;
464	}
465	else {
466	    if (*p == '"') {
467		quoted = 0;
468	    }
469	    ++ p;
470	}
471    }
472
473    /* The following code depends on the fact that Tcl_SplitList
474     * strips away double quoates inside a list: ie:
475     * if string == "\"1\" \"2\"" then
476     *		list[0] = "1"
477     *		list[1] = "2"
478     * and NOT
479     *
480     *		list[0] = "\"1\""
481     *		list[1] = "\"2\""
482     */
483    if (Tcl_SplitList(interp, list, &numLines, &data) != TCL_OK) {
484	goto error;
485    } else {
486	if (numLines == 0) {
487	    /* error: empty data? */
488	    if (data != NULL) {
489		ckfree((char*)data);
490		goto error;
491	    }
492	}
493	* numLines_return = numLines;
494	return data;
495    }
496
497  error:
498    Tcl_AppendResult(interp, "File format error", NULL);
499    return (CONST84 char**) NULL;
500}
501
502static CONST84 char **
503TkimgXpmGetDataFromFile(interp, fileName, numLines_return)
504    Tcl_Interp * interp;
505    char * fileName;
506    int * numLines_return;
507{
508    Tcl_Channel chan;
509    int size;
510    CONST84 char ** data = (CONST84 char **) NULL;
511    char *cmdBuffer = NULL;
512
513    chan = tkimg_OpenFileChannel(interp, fileName, 0);
514    if (!chan) {
515	return (CONST84 char **) NULL;
516    }
517
518    size = Tcl_Seek(chan, 0, SEEK_END);
519    if (size > 0) {
520	Tcl_Seek(chan, 0, SEEK_SET);
521	cmdBuffer = (char *) ckalloc(size+1);
522	size = Tcl_Read(chan, cmdBuffer, size);
523    }
524    if (Tcl_Close(interp, chan) != TCL_OK) {
525	goto error;
526    }
527    if (size < 0) {
528	Tcl_AppendResult(interp, fileName, ": ",
529		Tcl_PosixError(interp), (char *)NULL);
530	goto error;
531    }
532    cmdBuffer[size] = 0;
533
534    data = TkimgXpmGetDataFromString(interp, cmdBuffer, numLines_return);
535    error:
536    if (cmdBuffer) {
537	ckfree(cmdBuffer);
538    }
539    return data;
540}
541
542
543static char *
544GetType(colorDefn, type_ret)
545    char * colorDefn;
546    int  * type_ret;
547{
548    char * p = colorDefn;
549
550    /* skip white spaces */
551    while (*p && isspace(UCHAR(*p))) {
552	p ++;
553    }
554
555    /* parse the type */
556    if (p[0] != '\0' && p[0] == 'm' &&
557	p[1] != '\0' && isspace(UCHAR(p[1]))) {
558	*type_ret = XPM_MONO;
559	p += 2;
560    }
561    else if (p[0] != '\0' && p[0] == 'g' &&
562	     p[1] != '\0' && p[1] == '4' &&
563	     p[2] != '\0' && isspace(UCHAR(p[2]))) {
564	*type_ret = XPM_GRAY_4;
565	p += 3;
566    }
567    else if (p[0] != '\0' && p[0] == 'g' &&
568	     p[1] != '\0' && isspace(UCHAR(p[1]))) {
569	*type_ret = XPM_GRAY;
570	p += 2;
571    }
572    else if (p[0] != '\0' && p[0] == 'c' &&
573	     p[1] != '\0' && isspace(UCHAR(p[1]))) {
574	*type_ret = XPM_COLOR;
575	p += 2;
576    }
577    else if (p[0] != '\0' && p[0] == 's' &&
578	     p[1] != '\0' && isspace(UCHAR(p[1]))) {
579	*type_ret = XPM_SYMBOLIC;
580	p += 2;
581    }
582    else {
583	*type_ret = XPM_UNKNOWN;
584	return NULL;
585    }
586
587    return p;
588}
589
590/*
591 * colorName is guaranteed to be big enough
592 */
593
594static char *
595GetColor(colorDefn, colorName, type_ret)
596    char * colorDefn;
597    char * colorName;		/* if found, name is copied to this array */
598    int  * type_ret;
599{
600    int type;
601    char * p;
602
603    if (!colorDefn) {
604	return NULL;
605    }
606
607    if ((colorDefn = GetType(colorDefn, &type)) == NULL) {
608	/* unknown type */
609	return NULL;
610    }
611    else {
612	*type_ret = type;
613    }
614
615    /* skip white spaces */
616    while (*colorDefn && isspace(UCHAR(*colorDefn))) {
617	colorDefn ++;
618    }
619
620    p = colorName;
621
622    while (1) {
623	int dummy;
624
625	while (*colorDefn && !isspace(UCHAR(*colorDefn))) {
626	    *p++ = *colorDefn++;
627	}
628
629	if (!*colorDefn) {
630	    break;
631	}
632
633	if (GetType(colorDefn, &dummy) == NULL) {
634	    /* the next string should also be considered as a part of a color
635	     * name */
636
637	    while (*colorDefn && isspace(UCHAR(*colorDefn))) {
638		*p++ = *colorDefn++;
639	    }
640	} else {
641	    break;
642	}
643	if (!*colorDefn) {
644	    break;
645	}
646    }
647
648    /* Mark the end of the colorName */
649    *p = '\0';
650
651    return colorDefn;
652}
653
654/*----------------------------------------------------------------------
655 * TkimgXpmGetPixmapFromData --
656 *
657 *	Creates a pixmap for an image instance.
658 *----------------------------------------------------------------------
659 */
660
661static void
662TkimgXpmGetPixmapFromData(interp, masterPtr, instancePtr)
663    Tcl_Interp * interp;
664    PixmapMaster *masterPtr;
665    PixmapInstance *instancePtr;
666{
667    XImage * image = NULL, * mask = NULL;
668    int depth, i, j, k, lOffset, isTransp = 0, isMono;
669    ColorStruct * colors;
670
671    depth = Tk_Depth(instancePtr->tkwin);
672
673    switch ((Tk_Visual(instancePtr->tkwin))->class) {
674      case StaticGray:
675      case GrayScale:
676	isMono = 1;
677	break;
678      default:
679	isMono = 0;
680    }
681
682    TkimgXpmAllocTmpBuffer(masterPtr, instancePtr, &image, &mask);
683
684    /*
685     * Parse the colors
686     */
687    lOffset = 1;
688    colors = (ColorStruct*)ckalloc(sizeof(ColorStruct)*masterPtr->ncolors);
689
690    /*
691     * Initialize the color structures
692     */
693    for (i=0; i<masterPtr->ncolors; i++) {
694	colors[i].colorPtr = NULL;
695	if (masterPtr->cpp == 1) {
696	    colors[i].c = 0;
697	} else {
698	    colors[i].cstring = (char*)ckalloc(masterPtr->cpp);
699	    colors[i].cstring[0] = 0;
700	}
701    }
702
703    for (i=0; i<masterPtr->ncolors; i++) {
704	char * colorDefn;		/* the color definition line */
705	char * colorName;		/* temp place to hold the color name
706					 * defined for one type of visual */
707	char * useName;			/* the color name used for this
708					 * color. If there are many names
709					 * defined, choose the name that is
710					 * "best" for the target visual
711					 */
712	int found;
713
714	colorDefn = masterPtr->data[i+lOffset]+masterPtr->cpp;
715	colorName = (char*)ckalloc(strlen(colorDefn));
716	useName   = (char*)ckalloc(strlen(colorDefn));
717	found     = 0;
718
719	while (colorDefn && *colorDefn) {
720	    int type;
721
722	    if ((colorDefn=GetColor(colorDefn, colorName, &type)) == NULL) {
723		break;
724	    }
725	    if (colorName[0] == '\0') {
726		continue;
727	    }
728
729	    switch (type) {
730	      case XPM_MONO:
731		if (isMono && depth == 1) {
732		    strcpy(useName, colorName);
733		    found = 1; goto gotcolor;
734		}
735		break;
736	      case XPM_GRAY_4:
737		if (isMono && depth == 4) {
738		    strcpy(useName, colorName);
739		    found = 1; goto gotcolor;
740		}
741		break;
742	      case XPM_GRAY:
743		if (isMono && depth > 4) {
744		    strcpy(useName, colorName);
745		    found = 1; goto gotcolor;
746		}
747		break;
748	      case XPM_COLOR:
749		if (!isMono) {
750		    strcpy(useName, colorName);
751		    found = 1; goto gotcolor;
752		}
753		break;
754	    }
755	    if (type != XPM_SYMBOLIC && type != XPM_UNKNOWN) {
756		if (!found) {			/* use this color as default */
757		    strcpy(useName, colorName);
758		    found = 1;
759		}
760	    }
761	}
762
763      gotcolor:
764	if (masterPtr->cpp == 1) {
765	    colors[i].c = masterPtr->data[i+lOffset][0];
766	} else {
767	    strncpy(colors[i].cstring, masterPtr->data[i+lOffset],
768		(size_t)masterPtr->cpp);
769	}
770
771	if (found) {
772	    if (strncasecmp(useName, "none", 5) != 0) {
773		colors[i].colorPtr = Tk_GetColor(interp,
774		    instancePtr->tkwin, Tk_GetUid(useName));
775		if (colors[i].colorPtr == NULL) {
776		    colors[i].colorPtr = Tk_GetColor(interp,
777			instancePtr->tkwin, Tk_GetUid("black"));
778		}
779	    }
780	} else {
781	    colors[i].colorPtr = Tk_GetColor(interp,
782		instancePtr->tkwin, Tk_GetUid("black"));
783	}
784
785	ckfree(colorName);
786	ckfree(useName);
787    }
788
789    lOffset += masterPtr->ncolors;
790
791    /*
792     * Parse the main body of the image
793     */
794    for (i=0; i<masterPtr->size[1]; i++) {
795	char * p = masterPtr->data[i+lOffset];
796
797	for (j=0; j<masterPtr->size[0]; j++) {
798	    if (masterPtr->cpp == 1) {
799		for (k=0; k<masterPtr->ncolors; k++) {
800		    if (*p == colors[k].c) {
801			TkimgXpmSetPixel(instancePtr, image, mask, j, i,
802			        colors[k].colorPtr, &isTransp);
803			break;
804		    }
805		}
806		if (*p) {
807		    p++;
808		}
809	    } else {
810		for (k=0; k<masterPtr->ncolors; k++) {
811		    if (strncmp(p, colors[k].cstring,
812			    (size_t)masterPtr->cpp) == 0) {
813			TkimgXpmSetPixel(instancePtr, image, mask, j, i,
814			        colors[k].colorPtr, &isTransp);
815			break;
816		    }
817		}
818		for (k=0; *p && k<masterPtr->cpp; k++) {
819		    p++;
820		}
821	    }
822	}
823    }
824
825    instancePtr->colors = colors;
826
827    TkimgXpmRealizePixmap(masterPtr, instancePtr, image, mask, isTransp);
828    TkimgXpmFreeTmpBuffer(masterPtr, instancePtr, image, mask);
829}
830
831/*
832 *----------------------------------------------------------------------
833 *
834 * TkimgXpmConfigureInstance --
835 *
836 *	This procedure is called to create displaying information for
837 *	a pixmap image instance based on the configuration information
838 *	in the master.  It is invoked both when new instances are
839 *	created and when the master is reconfigured.
840 *
841 * Results:
842 *	None.
843 *
844 * Side effects:
845 *	Generates errors via Tk_BackgroundError if there are problems
846 *	in setting up the instance.
847 *
848 *----------------------------------------------------------------------
849 */
850
851static void
852TkimgXpmConfigureInstance(instancePtr)
853    PixmapInstance *instancePtr;	/* Instance to reconfigure. */
854{
855    PixmapMaster *masterPtr = instancePtr->masterPtr;
856
857    if (instancePtr->pixmap != None) {
858	Tk_FreePixmap(Tk_Display(instancePtr->tkwin), instancePtr->pixmap);
859    }
860    TkimgXpmFreeInstanceData(instancePtr, 0);
861
862    if (instancePtr->colors != NULL) {
863	int i;
864	for (i=0; i<masterPtr->ncolors; i++) {
865	    if (instancePtr->colors[i].colorPtr != NULL) {
866		Tk_FreeColor(instancePtr->colors[i].colorPtr);
867	    }
868	    if (masterPtr->cpp != 1) {
869		ckfree(instancePtr->colors[i].cstring);
870	    }
871	}
872	ckfree((char*)instancePtr->colors);
873    }
874
875    if (Tk_WindowId(instancePtr->tkwin) == None) {
876	Tk_MakeWindowExist(instancePtr->tkwin);
877    }
878
879    /*
880     * Assumption: masterPtr->data is always non NULL (enfored by
881     * TkimgXpmConfigureMaster()). Also, the data must be in a valid
882     * format (partially enforced by TkimgXpmConfigureMaster(), see comments
883     * inside that function).
884     */
885    TkimgXpmGetPixmapFromData(masterPtr->interp, masterPtr, instancePtr);
886}
887
888/*
889 *--------------------------------------------------------------
890 *
891 * TkimgXpmCmd --
892 *
893 *	This procedure is invoked to process the Tcl command
894 *	that corresponds to an image managed by this module.
895 *	See the user documentation for details on what it does.
896 *
897 * Results:
898 *	A standard Tcl result.
899 *
900 * Side effects:
901 *	See the user documentation.
902 *
903 *--------------------------------------------------------------
904 */
905
906static int
907TkimgXpmCmd(clientData, interp, argc, argv)
908    ClientData clientData;	/* Information about button widget. */
909    Tcl_Interp *interp;		/* Current interpreter. */
910    int argc;			/* Number of arguments. */
911    CONST84 char **argv;	/* Argument strings. */
912{
913    PixmapMaster *masterPtr = (PixmapMaster *) clientData;
914    int c, code;
915    size_t length;
916
917    if (argc < 2) {
918	Tcl_AppendResult(interp, "wrong # args: should be \"",
919		argv[0], " option ?arg arg ...?\"",
920		(char *) NULL);
921	return TCL_ERROR;
922    }
923    c = argv[1][0];
924    length = strlen(argv[1]);
925
926    if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
927	    && (length >= 2)) {
928	if (argc != 3) {
929	    Tcl_AppendResult(interp, "wrong # args: should be \"",
930		    argv[0], " cget option\"",
931		    (char *) NULL);
932	    return TCL_ERROR;
933	}
934	return Tk_ConfigureValue(interp, Tk_MainWindow(interp), configSpecs,
935		(char *) masterPtr, argv[2], 0);
936    } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
937	    && (length >= 2)) {
938	if (argc == 2) {
939	    code = Tk_ConfigureInfo(interp, Tk_MainWindow(interp),
940		    configSpecs, (char *) masterPtr, (char *) NULL, 0);
941	} else if (argc == 3) {
942	    code = Tk_ConfigureInfo(interp, Tk_MainWindow(interp),
943		    configSpecs, (char *) masterPtr, argv[2], 0);
944	} else {
945	    code = TkimgXpmConfigureMaster(masterPtr, argc-2, argv+2,
946		    TK_CONFIG_ARGV_ONLY);
947	}
948	return code;
949    } else if ((c == 'r') && (strncmp(argv[1], "refcount", length) == 0)) {
950	/*
951	 * The "refcount" command is for debugging only
952	 */
953	PixmapInstance *instancePtr;
954	int count = 0;
955	char buff[30];
956
957	if (argc != 1) {
958	    Tcl_AppendResult(interp, "wrong # args: should be \"",
959		    argv[0], "\"", (char *) NULL);
960	    return TCL_ERROR;
961	}
962	for (instancePtr=masterPtr->instancePtr; instancePtr;
963	     instancePtr = instancePtr->nextPtr) {
964	    count += instancePtr->refCount;
965	}
966	sprintf(buff, "%d", count);
967	Tcl_AppendResult(interp, buff, (char *) NULL);
968	return TCL_OK;
969    } else {
970	Tcl_AppendResult(interp, "bad option \"", argv[1],
971	    "\": must be cget, configure or refcount", (char *) NULL);
972	return TCL_ERROR;
973    }
974}
975
976/*
977 *----------------------------------------------------------------------
978 *
979 * TkimgXpmGet --
980 *
981 *	This procedure is called for each use of a pixmap image in a
982 *	widget.
983 *
984 * Results:
985 *	The return value is a token for the instance, which is passed
986 *	back to us in calls to TkimgXpmDisplay and TkimgXpmFre.
987 *
988 * Side effects:
989 *	A data structure is set up for the instance (or, an existing
990 *	instance is re-used for the new one).
991 *
992 *----------------------------------------------------------------------
993 */
994
995static ClientData
996TkimgXpmGet(tkwin, masterData)
997    Tk_Window tkwin;		/* Window in which the instance will be
998				 * used. */
999    ClientData masterData;	/* Pointer to our master structure for the
1000				 * image. */
1001{
1002    PixmapMaster *masterPtr = (PixmapMaster *) masterData;
1003    PixmapInstance *instancePtr;
1004
1005    /*
1006     * See if there is already an instance for this window.  If so
1007     * then just re-use it.
1008     */
1009
1010    for (instancePtr = masterPtr->instancePtr; instancePtr != NULL;
1011	    instancePtr = instancePtr->nextPtr) {
1012	if (instancePtr->tkwin == tkwin) {
1013	    instancePtr->refCount++;
1014	    return (ClientData) instancePtr;
1015	}
1016    }
1017
1018    /*
1019     * The image isn't already in use in this window.  Make a new
1020     * instance of the image.
1021     */
1022    instancePtr = (PixmapInstance *) ckalloc(sizeof(PixmapInstance));
1023    instancePtr->refCount = 1;
1024    instancePtr->masterPtr = masterPtr;
1025    instancePtr->tkwin = tkwin;
1026    instancePtr->pixmap = None;
1027    instancePtr->nextPtr = masterPtr->instancePtr;
1028    instancePtr->colors = NULL;
1029    masterPtr->instancePtr = instancePtr;
1030
1031    TkimgInitPixmapInstance(masterPtr, instancePtr);
1032    TkimgXpmConfigureInstance(instancePtr);
1033
1034    /*
1035     * If this is the first instance, must set the size of the image.
1036     */
1037    if (instancePtr->nextPtr == NULL) {
1038	if (masterPtr->data) {
1039	    Tk_ImageChanged(masterPtr->tkMaster, 0, 0,
1040	        masterPtr->size[0], masterPtr->size[1],
1041	        masterPtr->size[0], masterPtr->size[1]);
1042	} else {
1043	    Tk_ImageChanged(masterPtr->tkMaster, 0, 0, 0, 0, 0, 0);
1044	}
1045    }
1046
1047    return (ClientData) instancePtr;
1048}
1049
1050/*
1051 *----------------------------------------------------------------------
1052 *
1053 * TkimgXpmDisplay --
1054 *
1055 *	This procedure is invoked to draw a pixmap image.
1056 *
1057 * Results:
1058 *	None.
1059 *
1060 * Side effects:
1061 *	A portion of the image gets rendered in a pixmap or window.
1062 *
1063 *----------------------------------------------------------------------
1064 */
1065
1066static void
1067TkimgXpmDisplay(clientData, display, drawable, imageX, imageY, width,
1068	height, drawableX, drawableY)
1069    ClientData clientData;	/* Pointer to PixmapInstance structure for
1070				 * for instance to be displayed. */
1071    Display *display;		/* Display on which to draw image. */
1072    Drawable drawable;		/* Pixmap or window in which to draw image. */
1073    int imageX, imageY;		/* Upper-left corner of region within image
1074				 * to draw. */
1075    int width, height;		/* Dimensions of region within image to draw.*/
1076    int drawableX, drawableY;	/* Coordinates within drawable that
1077				 * correspond to imageX and imageY. */
1078{
1079    TkimgpXpmDisplay(clientData, display, drawable, imageX, imageY, width,
1080	height, drawableX, drawableY);
1081}
1082
1083/*
1084 *----------------------------------------------------------------------
1085 *
1086 * TkimgXpmFree --
1087 *
1088 *	This procedure is called when a widget ceases to use a
1089 *	particular instance of an image.
1090 *
1091 * Results:
1092 *	None.
1093 *
1094 * Side effects:
1095 *	Internal data structures get cleaned up.
1096 *
1097 *----------------------------------------------------------------------
1098 */
1099
1100static void
1101TkimgXpmFree(clientData, display)
1102    ClientData clientData;	/* Pointer to PixmapInstance structure for
1103				 * for instance to be displayed. */
1104    Display *display;		/* Display containing window that used image.*/
1105{
1106    PixmapInstance *instancePtr = (PixmapInstance *) clientData;
1107    PixmapInstance *prevPtr;
1108
1109    instancePtr->refCount--;
1110    if (instancePtr->refCount > 0) {
1111	return;
1112    }
1113
1114    /*
1115     * There are no more uses of the image within this widget.  Free
1116     * the instance structure.
1117     */
1118    if (instancePtr->pixmap != None) {
1119	Tk_FreePixmap(display, instancePtr->pixmap);
1120    }
1121    TkimgXpmFreeInstanceData(instancePtr, 1);
1122
1123    if (instancePtr->colors != NULL) {
1124	int i;
1125	for (i=0; i<instancePtr->masterPtr->ncolors; i++) {
1126	    if (instancePtr->colors[i].colorPtr != NULL) {
1127		Tk_FreeColor(instancePtr->colors[i].colorPtr);
1128	    }
1129	    if (instancePtr->masterPtr->cpp != 1) {
1130		ckfree(instancePtr->colors[i].cstring);
1131	    }
1132	}
1133	ckfree((char*)instancePtr->colors);
1134    }
1135
1136    if (instancePtr->masterPtr->instancePtr == instancePtr) {
1137	instancePtr->masterPtr->instancePtr = instancePtr->nextPtr;
1138    } else {
1139	for (prevPtr = instancePtr->masterPtr->instancePtr;
1140		prevPtr->nextPtr != instancePtr; prevPtr = prevPtr->nextPtr) {
1141	    /* Empty loop body */
1142	}
1143	prevPtr->nextPtr = instancePtr->nextPtr;
1144    }
1145    ckfree((char *) instancePtr);
1146}
1147
1148/*
1149 *----------------------------------------------------------------------
1150 *
1151 * TkimgXpmDelete --
1152 *
1153 *	This procedure is called by the image code to delete the
1154 *	master structure for an image.
1155 *
1156 * Results:
1157 *	None.
1158 *
1159 * Side effects:
1160 *	Resources associated with the image get freed.
1161 *
1162 *----------------------------------------------------------------------
1163 */
1164
1165static void
1166TkimgXpmDelete(masterData)
1167    ClientData masterData;	/* Pointer to PixmapMaster structure for
1168				 * image.  Must not have any more instances. */
1169{
1170    PixmapMaster *masterPtr = (PixmapMaster *) masterData;
1171
1172    if (masterPtr->instancePtr != NULL) {
1173	Tcl_Panic("tried to delete pixmap image when instances still exist");
1174    }
1175    masterPtr->tkMaster = NULL;
1176    if (masterPtr->imageCmd != NULL) {
1177	Tcl_DeleteCommand(masterPtr->interp,
1178		Tcl_GetCommandName(masterPtr->interp, masterPtr->imageCmd));
1179    }
1180    if (masterPtr->isDataAlloced && masterPtr->data != NULL) {
1181	ckfree((char*)masterPtr->data);
1182	masterPtr->data = NULL;
1183    }
1184
1185    Tk_FreeOptions(configSpecs, (char *) masterPtr, (Display *) NULL, 0);
1186    ckfree((char *) masterPtr);
1187}
1188
1189/*
1190 *----------------------------------------------------------------------
1191 *
1192 * TkimgXpmCmdDeletedProc --
1193 *
1194 *	This procedure is invoked when the image command for an image
1195 *	is deleted.  It deletes the image.
1196 *
1197 * Results:
1198 *	None.
1199 *
1200 * Side effects:
1201 *	The image is deleted.
1202 *
1203 *----------------------------------------------------------------------
1204 */
1205
1206static void
1207TkimgXpmCmdDeletedProc(clientData)
1208    ClientData clientData;	/* Pointer to PixmapMaster structure for
1209				 * image. */
1210{
1211    PixmapMaster *masterPtr = (PixmapMaster *) clientData;
1212
1213    masterPtr->imageCmd = NULL;
1214    if (masterPtr->tkMaster != NULL) {
1215	Tk_DeleteImage(masterPtr->interp, Tk_NameOfImage(masterPtr->tkMaster));
1216    }
1217}
1218
1219/*
1220 * Package management. Initialization of stub information.
1221 */
1222
1223/*
1224 *----------------------------------------------------------------------------
1225 *
1226 * Tkimgpixmap_Init --
1227 *
1228 *  Initialisation routine for loadable module
1229 *
1230 * Results:
1231 *  None.
1232 *
1233 * Side effects:
1234 *  Creates commands in the interpreter, loads package.
1235 *
1236 *----------------------------------------------------------------------------
1237 */
1238
1239int
1240Tkimgpixmap_Init (interp)
1241      Tcl_Interp *interp; /* Interpreter to initialise. */
1242{
1243    static int initialized = 0;
1244
1245    if (Tcl_InitStubs(interp, "8.3", 0) == NULL) {
1246        return TCL_ERROR;
1247    }
1248    if (Tk_InitStubs(interp, "8.3", 0) == NULL) {
1249        return TCL_ERROR;
1250    }
1251    if (Tkimg_InitStubs(interp, TKIMG_VERSION, 0) == NULL) {
1252        return TCL_ERROR;
1253    }
1254
1255#ifndef TCL_MAC
1256    if (!initialized) {
1257	Tk_CreateImageType(&imgPixmapImageType);
1258	initialized = 1;
1259    }
1260#endif
1261
1262    /*
1263     * At last provide the package ...
1264     */
1265
1266    if (Tcl_PkgProvide(interp, PACKAGE_TCLNAME, PACKAGE_VERSION) != TCL_OK) {
1267        return TCL_ERROR;
1268    }
1269    return TCL_OK;
1270}
1271
1272/*
1273 *----------------------------------------------------------------------------
1274 *
1275 * Tkimgpixmap_SafeInit --
1276 *
1277 *  Initialisation routine for loadable module in a safe interpreter.
1278 *
1279 * Results:
1280 *  None.
1281 *
1282 * Side effects:
1283 *  Creates commands in the interpreter,
1284 *  loads xml package.
1285 *
1286 *----------------------------------------------------------------------------
1287 */
1288
1289int
1290Tkimgpixmap_SafeInit (interp)
1291      Tcl_Interp *interp; /* Interpreter to initialise. */
1292{
1293    return Tkimgpixmap_Init (interp);
1294}
1295