1 /*
2 * xvxpm.c - load routine for X11 XPM v3 format pictures
3 *
4 * LoadXPM(fname,pinfo)
5 * WriteXPM(fp,pic,ptype,w,h,rp,gp,bp,nc,col,name)
6 */
7
8 /*
9 * Written by Chris P. Ross (cross@eng.umd.edu) to support XPM v3
10 * format images.
11 *
12 * Thanks go to Sam Yates (syates@spam.maths.adelaide.edu.au) for
13 * providing inspiration.
14 */
15
16 #define VALUES_LEN 80 /* Max length of values line */
17 #define TOKEN_LEN 8 /* Max length of color token */
18
19 #include "xv.h"
20
21
22 /*
23 * File format:
24 * (A file format def should go here)
25 *
26 */
27
28 typedef struct hent {
29 char token[TOKEN_LEN];
30 union {
31 byte index;
32 byte rgb[3];
33 } color_val;
34 struct hent *next;
35 } hentry;
36
37 #define cv_index color_val.index
38 #define cv_rgb color_val.rgb
39
40
41
42 /* Local (Global) Variables */
43 static byte hex[256];
44 static hentry **hashtab; /* Hash table */
45 static int hash_len; /* number of hash buckets */
46 static int bufchar; /* Buffered character from XpmGetc */
47 static short in_quote; /* Is the current point in the file in */
48 /* a quoted string? */
49
50 /* Local Functions */
51 static int XpmLoadError PARM((const char *, const char *));
52 static int XpmGetc PARM((FILE *));
53 static int hash PARM((char *));
54 static int hash_init PARM((int));
55 static int hash_insert PARM((hentry *));
56 static hentry *hash_search PARM((char *));
57 static void hash_destroy PARM((void));
58
59
60 /**************************************/
LoadXPM(fname,pinfo)61 int LoadXPM(fname, pinfo)
62 char *fname;
63 PICINFO *pinfo;
64 {
65 /* returns '1' on success */
66
67 FILE *fp;
68 hentry item;
69 int c;
70 const char *bname;
71 char values[VALUES_LEN];
72 byte *pic;
73 byte *i_sptr; /* image search pointer */
74 long filesize;
75 int w, h, nc, cpp, line_pos;
76 int npixels;
77 short i, j, k; /* for() loop indexes */
78 hentry *clmp; /* colormap hash-table */
79 hentry *c_sptr; /* cmap hash-table search pointer*/
80 XColor col;
81
82 bname = BaseName(fname);
83 fp = fopen(fname, "r");
84 if (!fp)
85 return (XpmLoadError(bname, "couldn't open file"));
86
87 if (DEBUG)
88 printf("LoadXPM(): Loading xpm from %s\n", fname);
89
90 fseek(fp, 0L, 2);
91 filesize = ftell(fp);
92 fseek(fp, 0L, 0);
93
94 bufchar = -2;
95 in_quote = FALSE;
96
97 /* Read in the values line. It is the first string in the
98 * xpm, and contains four numbers. w, h, num_colors, and
99 * chars_per_pixel. */
100
101 /* First, get to the first string */
102 while (((c = XpmGetc(fp))!=EOF) && (c != '"')) ;
103 line_pos = 0;
104
105 /* Now, read in the string */
106 while (((c = XpmGetc(fp))!=EOF) && (line_pos < VALUES_LEN) && (c != '"')) {
107 values[line_pos++] = c;
108 }
109 if (c != '"')
110 return (XpmLoadError(bname, "error parsing values line"));
111
112 values[line_pos] = '\0';
113 sscanf(values, "%d%d%d%d", &w, &h, &nc, &cpp);
114 if (nc <= 0 || cpp <= 0)
115 return (XpmLoadError(bname, "No colours in Xpm?"));
116
117 npixels = w * h;
118 if (w <= 0 || h <= 0 || npixels/w != h)
119 return (XpmLoadError(bname, "Image dimensions out of range"));
120
121 if (nc > 256)
122 pinfo->type = PIC24;
123 else
124 pinfo->type = PIC8;
125
126 if (DEBUG)
127 printf("LoadXPM(): reading a %dx%d image (%d colors)\n", w, h, nc);
128
129 /* We got this far... */
130 WaitCursor();
131
132 if (!hash_init(nc))
133 return (XpmLoadError(bname, "Not enough memory to hash colormap"));
134
135 clmp = (hentry *) malloc(nc * sizeof(hentry)); /* Holds the colormap */
136 if (pinfo->type == PIC8)
137 pic = (byte *) malloc((size_t) npixels);
138 else {
139 int bufsize = 3*npixels;
140 if (bufsize/3 != npixels)
141 return (XpmLoadError(bname, "Image dimensions out of range"));
142 pic = (byte *) malloc((size_t) bufsize);
143 }
144
145 if (!clmp || !pic)
146 return (XpmLoadError(bname, "Not enough memory to load pixmap"));
147
148 c_sptr = clmp;
149 i_sptr = pic;
150
151 /* initialize the 'hex' array for zippy ASCII-hex -> int conversion */
152
153 for (i = 0 ; i < 256 ; i++) hex[i] = 0;
154 for (i = '0'; i <= '9' ; i++) hex[i] = i - '0';
155 for (i = 'a'; i <= 'f' ; i++) hex[i] = i - 'a' + 10;
156 for (i = 'A'; i <= 'F' ; i++) hex[i] = i - 'A' + 10;
157
158 /* Again, we've made progress. */
159 WaitCursor();
160
161 /* Now, we need to read the colormap. */
162 pinfo->colType = F_BWDITHER;
163 for (i = 0 ; i < nc ; i++) {
164 while (((c = XpmGetc(fp))!=EOF) && (c != '"')) ;
165 if (c != '"')
166 return (XpmLoadError(bname, "Error reading colormap"));
167
168 for (j = 0 ; j < cpp ; j++)
169 c_sptr->token[j] = XpmGetc(fp);
170 c_sptr->token[j] = '\0';
171
172 while (((c = XpmGetc(fp))!=EOF) && ((c == ' ') || (c == '\t'))) ;
173 if (c == EOF) /* The failure condition of getc() */
174 return (XpmLoadError(bname, "Error parsing colormap line"));
175
176 do {
177 char key[3];
178 char color[80]; /* Need to figure a good size for this... */
179
180 /*
181 * Problem with spaces in color names
182 *
183 * X s Color Name m Other Name c Last Name
184 *
185 * ... this parser doesn't find `Any Name'
186 */
187
188 for (j=0; j<2 && (c != ' ') && (c != '\t') && (c != EOF); j++) {
189 key[j] = c;
190 c = XpmGetc(fp);
191 }
192 key[j] = '\0';
193
194 while (((c = XpmGetc(fp))!=EOF) && ((c == ' ') || (c == '\t'))) ;
195 if (c == EOF) /* The failure condition of getc() */
196 return (XpmLoadError(bname, "Error parsing colormap line"));
197
198 for (j=0; j<79 && (c!=' ') && (c!='\t') && (c!='"') && c!=EOF; j++) {
199 color[j] = c;
200 c = XpmGetc(fp);
201 }
202 color[j]='\0';
203
204 while ((c == ' ') || (c == '\t'))
205 c = XpmGetc(fp);
206
207 if (DEBUG > 1)
208 printf("LoadXPM(): Got color key '%s', color '%s'\n",
209 key, color);
210
211 if (key[0] == 's') /* Don't find a color for a symbolic name */
212 continue;
213
214 if (XParseColor(theDisp,theCmap,color,&col)) {
215 if (pinfo->type == PIC8) {
216 pinfo->r[i] = col.red >> 8;
217 pinfo->g[i] = col.green >> 8;
218 pinfo->b[i] = col.blue >> 8;
219 c_sptr->cv_index = i;
220
221 /* Is there a better way to do this? */
222 if (pinfo->colType != F_FULLCOLOR) {
223 if (pinfo->colType == F_GREYSCALE) {
224 if (pinfo->r[i] == pinfo->g[i] &&
225 pinfo->g[i] == pinfo->b[i])
226 /* Still greyscale... */
227 ;
228 else
229 /* It's color */
230 pinfo->colType = F_FULLCOLOR;
231 } else {
232 if (pinfo->r[i] == pinfo->g[i] &&
233 pinfo->g[i] == pinfo->b[i]) {
234 if ((pinfo->r[i] == 0 || pinfo->r[i] == 0xff) &&
235 (pinfo->g[i] == 0 || pinfo->g[i] == 0xff) &&
236 (pinfo->b[i] == 0 || pinfo->b[i] == 0xff))
237 /* It's B/W */
238 ;
239 else
240 /* It's greyscale */
241 pinfo->colType = F_GREYSCALE;
242 } else
243 /* It's color */
244 pinfo->colType = F_FULLCOLOR;
245 }
246 }
247
248 }
249 else { /* PIC24 */
250 c_sptr->cv_rgb[0] = col.red >> 8;
251 c_sptr->cv_rgb[1] = col.green >> 8;
252 c_sptr->cv_rgb[2] = col.blue >> 8;
253 }
254 }
255
256 else { /* 'None' or unrecognized color spec */
257 int rgb;
258
259 if (strcasecmp(color, "None") == 0) rgb = 0xb2c0dc; /* infobg */
260 else {
261 SetISTR(ISTR_INFO, "%s: unknown color spec '%s'", bname, color);
262 Timer(1000);
263 rgb = 0x808080;
264 }
265
266 if (pinfo->type == PIC8) {
267 pinfo->r[i] = (rgb>>16) & 0xff;
268 pinfo->g[i] = (rgb>> 8) & 0xff;
269 pinfo->b[i] = (rgb>> 0) & 0xff;
270 c_sptr->cv_index = i;
271 }
272 else {
273 c_sptr->cv_rgb[0] = (rgb>>16) & 0xff;
274 c_sptr->cv_rgb[1] = (rgb>> 8) & 0xff;
275 c_sptr->cv_rgb[2] = (rgb>> 0) & 0xff;
276 }
277 }
278
279
280 xvbcopy((char *) c_sptr, (char *) &item, sizeof(item));
281 hash_insert(&item);
282
283 if (DEBUG > 1)
284 printf("LoadXPM(): Cmap entry %d, 0x%02x 0x%02x 0x%02x, token '%s'\n",
285 i, pinfo->r[i], pinfo->g[i], pinfo->b[i], c_sptr->token);
286
287 if (*key == 'c') { /* This is the color entry, keep it. */
288 while (c!='"' && c!=EOF) c = XpmGetc(fp);
289 break;
290 }
291
292 } while (c != '"');
293 c_sptr++;
294
295 if (!(i%13)) WaitCursor();
296 } /* for */
297
298
299 if (DEBUG)
300 printf("LoadXPM(): Read and stored colormap.\n");
301
302 /* Now, read the pixmap. */
303 for (i = 0 ; i < h ; i++) {
304 while (((c = XpmGetc(fp))!=EOF) && (c != '"')) ;
305 if (c != '"')
306 return (XpmLoadError(bname, "Error reading colormap"));
307
308 for (j = 0 ; j < w ; j++) {
309 char pixel[TOKEN_LEN];
310 hentry *mapentry;
311
312 for (k = 0 ; k < cpp ; k++)
313 pixel[k] = XpmGetc(fp);
314 pixel[k] = '\0';
315
316 if (!(mapentry = (hentry *) hash_search(pixel))) {
317 /* No colormap entry found. What do we do? Bail for now */
318 if (DEBUG)
319 printf("LoadXPM(): Found token '%s', can't find entry in colormap\n",
320 pixel);
321 return (XpmLoadError(bname, "Can't map resolve into colormap"));
322 }
323
324 if (pinfo->type == PIC8)
325 *i_sptr++ = mapentry->cv_index;
326 else {
327 *i_sptr++ = mapentry->cv_rgb[0];
328 *i_sptr++ = mapentry->cv_rgb[1];
329 *i_sptr++ = mapentry->cv_rgb[2];
330 }
331 } /* for ( j < w ) */
332 while (((c = XpmGetc(fp))!=EOF) && /* Throw away the close " and */
333 (c != '"')); /* erase all remaining pixels */
334
335 if (!(i%7)) WaitCursor();
336 } /* for ( i < h ) */
337
338 pinfo->pic = pic;
339 pinfo->normw = pinfo->w = w;
340 pinfo->normh = pinfo->h = h;
341 pinfo->frmType = F_XPM;
342
343 if (DEBUG) printf("LoadXPM(): pinfo->colType is %d\n", pinfo->colType);
344
345 sprintf(pinfo->fullInfo, "Xpm v3 Pixmap (%ld bytes)", filesize);
346 sprintf(pinfo->shrtInfo, "%dx%d Xpm.", w, h);
347 pinfo->comment = (char *)NULL;
348
349 hash_destroy();
350 free(clmp);
351
352 if (fp != stdin)
353 fclose(fp);
354
355 return(1);
356 }
357
358
359 /***************************************/
XpmLoadError(fname,st)360 static int XpmLoadError(fname, st)
361 const char *fname, *st;
362 {
363 SetISTR(ISTR_WARNING, "%s: %s", fname, st);
364 return 0;
365 }
366
367
368 /***************************************/
XpmGetc(f)369 static int XpmGetc(f)
370 FILE *f;
371 {
372 int c, d, lastc;
373
374 if (bufchar != -2) {
375 /* The last invocation of this routine read the character... */
376 c = bufchar;
377 bufchar = -2;
378 return(c);
379 }
380
381 if ((c = getc(f)) == EOF)
382 return(EOF);
383
384 if (c == '"')
385 in_quote = !in_quote;
386 else if (!in_quote && c == '/') { /* might be a C-style comment */
387 if ((d = getc(f)) == EOF)
388 return(EOF);
389 if (d == '*') { /* yup, it *is* a comment */
390 if ((lastc = getc(f)) == EOF)
391 return(EOF);
392 do { /* skip through to the end */
393 if ((c = getc(f)) == EOF)
394 return(EOF);
395 if (lastc != '*' || c != '/') /* end of comment */
396 lastc = c;
397 } while (lastc != '*' || c != '/');
398 if ((c = getc(f)) == EOF)
399 return(EOF);
400 } else /* nope, not a comment */
401 bufchar = d;
402 }
403 return(c);
404 }
405
406
407 /***************************************/
408 /* hashing functions */
409 /***************************************/
410
411
412 /***************************************/
hash(token)413 static int hash(token)
414 char *token;
415 {
416 int i, sum;
417
418 for (i=sum=0; token[i] != '\0'; i++)
419 sum += token[i];
420
421 sum = sum % hash_len;
422 return (sum);
423 }
424
425
426 /***************************************/
hash_init(hsize)427 static int hash_init(hsize)
428 int hsize;
429 {
430 /*
431 * hash_init() - This function takes an arg, but doesn't do anything with
432 * it. It could easily be expanded to figure out the "optimal" number of
433 * buckets. But, for now, a hard-coded 257 will do. (Until I finish the
434 * 24-bit image writing code. :-)
435 */
436
437 int i;
438
439 hash_len = 257;
440
441 hashtab = (hentry **) malloc(sizeof(hentry *) * hash_len);
442 if (!hashtab) {
443 SetISTR(ISTR_WARNING, "Couldn't malloc hashtable in LoadXPM()!\n");
444 return 0;
445 }
446
447 for (i = 0 ; i < hash_len ; i++)
448 hashtab[i] = NULL;
449
450 return 1;
451 }
452
453
454 /***************************************/
hash_insert(entry)455 static int hash_insert(entry)
456 hentry *entry;
457 {
458 int key;
459 hentry *tmp;
460
461 key = hash(entry->token);
462
463 tmp = (hentry *) malloc(sizeof(hentry));
464 if (!tmp) {
465 SetISTR(ISTR_WARNING, "Couldn't malloc hash entry in LoadXPM()!\n");
466 return 0;
467 }
468
469 xvbcopy((char *)entry, (char *)tmp, sizeof(hentry));
470
471 if (hashtab[key]) tmp->next = hashtab[key];
472 else tmp->next = NULL;
473
474 hashtab[key] = tmp;
475
476 return 1;
477 }
478
479
480 /***************************************/
hash_search(token)481 static hentry *hash_search(token)
482 char *token;
483 {
484 int key;
485 hentry *tmp;
486
487 key = hash(token);
488
489 tmp = hashtab[key];
490 while (tmp && strcmp(token, tmp->token)) {
491 tmp = tmp->next;
492 }
493
494 return tmp;
495 }
496
497
498 /***************************************/
hash_destroy()499 static void hash_destroy()
500 {
501 int i;
502 hentry *tmp;
503
504 for (i=0; i<hash_len; i++) {
505 while (hashtab[i]) {
506 tmp = hashtab[i]->next;
507 free(hashtab[i]);
508 hashtab[i] = tmp;
509 }
510 }
511
512 free(hashtab);
513 return;
514 }
515
516
517
518 /**************************************************************************/
WriteXPM(fp,pic,ptype,w,h,rp,gp,bp,nc,col,name,comments)519 int WriteXPM(fp, pic, ptype, w, h, rp, gp, bp, nc, col, name, comments)
520 FILE *fp; /* File to write to */
521 byte *pic; /* Image data */
522 int ptype; /* PIC8 or PIC24 */
523 int w, h; /* width, & height */
524 byte *rp, *gp, *bp; /* Colormap pointers */
525 int nc, col; /* number of colors, & colorstyle */
526 char *name; /* name of file (/image) */
527 char *comments; /* image comments (not currently used */
528 {
529 /* Note here, that tokenchars is assumed to contain 64 valid token */
530 /* characters. It's hardcoded to assume this for benefit of generating */
531 /* tokens, when there are more than 64^2 colors. */
532
533 short i, imax, j; /* for() loop indices */
534 short cpp = 0;
535 const char *tokenchars =
536 ".#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
537 char *tokens;
538 char image_name[256], *foo;
539 byte grey;
540
541 #ifndef USE_UNFINISHED_24BIT_WRITING_CODE
542 byte *pic8, *sptr;
543 byte rtemp[256], gtemp[256], btemp[256], hist[256], trans[256];
544 long li; /* for() loop index */
545 int numcol;
546 #endif
547
548 if (DEBUG) {
549 if (ptype == PIC8)
550 printf("WriteXPM(): Write a %d color, colortype %d, PIC8 image.\n",
551 nc, col);
552 else
553 printf("WriteXPM(): Write a colortype %d, PIC24 image.\n", col);
554 }
555
556 strcpy(image_name, BaseName(name));
557 foo = (char *)strchr(image_name, '.');
558 if (foo)
559 *foo = '\0'; /* Truncate name at first '.' */
560
561 #ifdef USE_UNFINISHED_24BIT_WRITING_CODE
562 if (ptype == PIC24)
563 return -1;
564 #else
565 /* XXX - The colormap returned from Conv24to8 is not a specific */
566 /* size (and Conv24to8 doesn't tell me how many entries there */
567 /* are, or not in this rev of code at least) and not necessarily */
568 /* 'packed'. Code in here to do that should be removed if */
569 /* Conv24to8 is "fixed" to do this... */
570 /* Chris P. Ross (cross@eng.umd.edu) 28-Sept-94 */
571
572 numcol = 0;
573
574 if (ptype == PIC24) {
575 /* Reduce to an 8-bit image. Would be nice to actually write */
576 /* the 24-bit image. I'll have to code that someday... */
577 pic8 = Conv24to8(pic, w, h, 256, rtemp, gtemp, btemp);
578 if (!pic8) {
579 SetISTR(ISTR_WARNING,
580 "%s: Unable to convert to 8-bit image in WriteXPM()",
581 image_name);
582 return 1;
583 }
584 if (DEBUG) printf("WriteXPM(): Converted 24bit image.\n");
585
586 /* This will count the number of colors in use, and build an */
587 /* index array into the colormap (which may not be 'packed') */
588 /* Thanks to John Bradley for this code.. */
589
590 xvbzero((char *) hist, sizeof(hist));
591 numcol = 0;
592
593 if (DEBUG)
594 printf("WriteXPM(): Counting colors (width: %d, height: %d)...\n", w, h);
595
596 for (li = 0, sptr = pic8 ; li < (w*h) ; li++, sptr++) {
597 hist[*sptr] = 1;
598 }
599
600 if (DEBUG)
601 printf("WriteXPM(): building translation table...\n");
602 for (i = 0 ; i < 256 ; i++) {
603 trans[i] = numcol;
604 rtemp[numcol] = rtemp[i];
605 gtemp[numcol] = gtemp[i];
606 btemp[numcol] = btemp[i];
607 numcol += hist[i];
608 }
609 rp=rtemp; gp=gtemp; bp=btemp;
610 if (DEBUG)
611 printf("WriteXPM(): Converted 24bit image now has %d colors.\n", numcol);
612 } else {
613 pic8 = pic;
614 numcol = nc;
615 }
616 #endif
617
618
619 #ifdef USE_UNFINISHED_24BIT_WRITING_CODE
620 if (ptype == PIC24) cpp = 4;
621 else if (numcol > 64) cpp = 2;
622 else cpp = 1;
623 #else
624 if (numcol > 64) cpp = 2;
625 else cpp = 1;
626 #endif
627
628 fprintf(fp, "/* XPM */\n");
629 fprintf(fp, "static char *%s[] = {\n", image_name);
630 fprintf(fp, "/* width height num_colors chars_per_pixel */\n");
631 fprintf(fp, "\" %3d %3d %6d %1d\",\n", w, h, numcol, cpp);
632 fprintf(fp, "/* colors */\n");
633
634 switch (cpp) {
635
636 case 1: /* <= 64 colors; index into tokenchars */
637 if (col == F_GREYSCALE)
638 for (i = 0 ; i < numcol ; i ++) {
639 grey = MONO(rp[i], gp[i], bp[i]);
640 fprintf(fp, "\"%c c #%02x%02x%02x\",\n", tokenchars[i],
641 grey, grey, grey);
642 }
643 else
644 for (i = 0 ; i < numcol ; i ++)
645 fprintf(fp, "\"%c c #%02x%02x%02x\",\n", tokenchars[i],
646 rp[i], gp[i], bp[i]);
647 fprintf(fp, "/* pixels */\n");
648 for (i = 0 ; i < h ; i ++) {
649 fprintf(fp, "\"");
650 if (!(i%20))
651 WaitCursor();
652 for (j = 0 ; j < w ; j++) {
653 if (rp == rtemp)
654 fprintf(fp, "%c", tokenchars[trans[*pic8++]]);
655 else
656 fprintf(fp, "%c", tokenchars[*pic8++]);
657 }
658 if (i < h-1)
659 fprintf(fp, "\",\n");
660 }
661 break;
662
663 case 2: /* 64 < colors < 64^2; build new token list */
664 tokens = (char *) malloc((size_t) ((2*numcol) + 1) );
665 if (numcol & 0x3f)
666 imax = (numcol >> 6) + 1;
667 else
668 imax = (numcol >> 6);
669 for (i = 0 ; i < imax ; i++)
670 for (j = 0 ; j < 64 && ((i<<6)+j) < numcol ; j++) {
671 *(tokens+((i<<6)+j)*2) = tokenchars[i];
672 *(tokens+((i<<6)+j)*2+1) = tokenchars[j];
673 }
674 if (col == F_GREYSCALE)
675 for (i = 0 ; i < numcol ; i++) {
676 grey = MONO(rp[i], gp[i], bp[i]);
677 fprintf(fp, "\"%c%c c #%02x%02x%02x\",\n", tokens[i*2],
678 tokens[i*2+1], grey, grey, grey);
679 }
680 else
681 for (i = 0 ; i < numcol ; i++)
682 fprintf(fp, "\"%c%c c #%02x%02x%02x\",\n", tokens[i*2],
683 tokens[i*2+1], rp[i], gp[i], bp[i]);
684 fprintf(fp, "/* pixels */\n");
685 for (i = 0 ; i < h ; i++) {
686 fprintf(fp, "\"");
687 if (!(i%13))
688 WaitCursor();
689 for (j = 0 ; j < w ; j++) {
690 if (rp == rtemp)
691 fprintf(fp, "%c%c", tokens[trans[*pic8]*2],
692 tokens[(trans[*pic8]*2)+1]);
693 else
694 fprintf(fp, "%c%c", tokens[(*pic8*2)],
695 tokens[(*pic8*2)+1]);
696 pic8++;
697 }
698 if (i < h-1)
699 fprintf(fp, "\",\n");
700 }
701 break;
702
703 case 4:
704 /* Generate a colormap */
705
706 break;
707 default:
708 break;
709 }
710
711 if (fprintf(fp, "\"\n};\n") == EOF) {
712 return 1;
713 } else
714 return 0;
715 }
716
717
718
719
720