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