1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 01/04/2009
6 //
7 // Filename: src-IL/src/il_xpm.c
8 //
9 // Description: Reads from an .xpm file.
10 //
11 //-----------------------------------------------------------------------------
12
13
14 #include "il_internal.h"
15 #ifndef IL_NO_XPM
16 #include <ctype.h>
17
18
19 //If this is defined, only xpm files with 1 char/pixel
20 //can be loaded. They load somewhat faster then, though
21 //(not much).
22 //#define XPM_DONT_USE_HASHTABLE
23
24 ILboolean iIsValidXpm(void);
25 ILboolean iLoadXpmInternal(void);
26 ILint XpmGetsInternal(ILubyte *Buffer, ILint MaxLen);
27
28 //! Checks if the file specified in FileName is a valid XPM file.
ilIsValidXpm(ILconst_string FileName)29 ILboolean ilIsValidXpm(ILconst_string FileName)
30 {
31 ILHANDLE XpmFile;
32 ILboolean bXpm = IL_FALSE;
33
34 if (!iCheckExtension(FileName, IL_TEXT("xpm"))) {
35 ilSetError(IL_INVALID_EXTENSION);
36 return bXpm;
37 }
38
39 XpmFile = iopenr(FileName);
40 if (XpmFile == NULL) {
41 ilSetError(IL_COULD_NOT_OPEN_FILE);
42 return bXpm;
43 }
44
45 bXpm = ilIsValidXpmF(XpmFile);
46 icloser(XpmFile);
47
48 return bXpm;
49 }
50
51
52 //! Checks if the ILHANDLE contains a valid XPM file at the current position.
ilIsValidXpmF(ILHANDLE File)53 ILboolean ilIsValidXpmF(ILHANDLE File)
54 {
55 ILuint FirstPos;
56 ILboolean bRet;
57
58 iSetInputFile(File);
59 FirstPos = itell();
60 bRet = iIsValidXpm();
61 iseek(FirstPos, IL_SEEK_SET);
62
63 return bRet;
64 }
65
66
67 //! Checks if Lump is a valid XPM lump.
ilIsValidXpmL(const void * Lump,ILuint Size)68 ILboolean ilIsValidXpmL(const void *Lump, ILuint Size)
69 {
70 iSetInputLump(Lump, Size);
71 return iIsValidXpm();
72 }
73
74
75 // Internal function to get the header and check it.
iIsValidXpm(void)76 ILboolean iIsValidXpm(void)
77 {
78 ILubyte Buffer[10];
79 ILuint Pos = itell();
80
81 XpmGetsInternal(Buffer, 10);
82 iseek(Pos, IL_SEEK_SET); // Restore position
83
84 if (strncmp("/* XPM */", (char*)Buffer, strlen("/* XPM */")))
85 return IL_FALSE;
86 return IL_TRUE;
87 }
88
89
90 // Reads an .xpm file
ilLoadXpm(ILconst_string FileName)91 ILboolean ilLoadXpm(ILconst_string FileName)
92 {
93 ILHANDLE XpmFile;
94 ILboolean bXpm = IL_FALSE;
95
96 XpmFile = iopenr(FileName);
97 if (XpmFile == NULL) {
98 ilSetError(IL_COULD_NOT_OPEN_FILE);
99 return bXpm;
100 }
101
102 iSetInputFile(XpmFile);
103 bXpm = ilLoadXpmF(XpmFile);
104 icloser(XpmFile);
105
106 return bXpm;
107 }
108
109
110 //! Reads an already-opened .xpm file
ilLoadXpmF(ILHANDLE File)111 ILboolean ilLoadXpmF(ILHANDLE File)
112 {
113 ILuint FirstPos;
114 ILboolean bRet;
115
116 iSetInputFile(File);
117 FirstPos = itell();
118 bRet = iLoadXpmInternal();
119 iseek(FirstPos, IL_SEEK_SET);
120
121 return bRet;
122 }
123
124
125 //! Reads from a memory "lump" that contains an .xpm
ilLoadXpmL(const void * Lump,ILuint Size)126 ILboolean ilLoadXpmL(const void *Lump, ILuint Size)
127 {
128 iSetInputLump(Lump, Size);
129 return iLoadXpmInternal();
130 }
131
132
133 typedef ILubyte XpmPixel[4];
134
135 #define XPM_MAX_CHAR_PER_PIXEL 2
136
137
138 #ifndef XPM_DONT_USE_HASHTABLE
139
140 //The following hash table code was inspired by the xpm
141 //loading code of xv, one of the best image viewers of X11
142
143 //For xpm files with more than one character/pixel, it is
144 //impractical to use a simple lookup table for the
145 //character-to-color mapping (because the table requires
146 //2^(chars/pixel) entries, this is quite big).
147 //Because of that, a hash table is used for the mapping.
148 //The hash table has 257 entries, and collisions are
149 //resolved by chaining.
150
151 //257 is the smallest prime > 256
152 #define XPM_HASH_LEN 257
153
154 typedef struct XPMHASHENTRY
155 {
156 ILubyte ColourName[XPM_MAX_CHAR_PER_PIXEL];
157 XpmPixel ColourValue;
158 struct XPMHASHENTRY *Next;
159 } XPMHASHENTRY;
160
161
XpmHash(const ILubyte * name,int len)162 static ILuint XpmHash(const ILubyte* name, int len)
163 {
164 ILint i, sum;
165 for (sum = i = 0; i < len; ++i)
166 sum += name[i];
167 return sum % XPM_HASH_LEN;
168 }
169
170
XpmCreateHashTable()171 XPMHASHENTRY** XpmCreateHashTable()
172 {
173 XPMHASHENTRY** Table =
174 (XPMHASHENTRY**)ialloc(XPM_HASH_LEN*sizeof(XPMHASHENTRY*));
175 if (Table != NULL)
176 memset(Table, 0, XPM_HASH_LEN*sizeof(XPMHASHENTRY*));
177 return Table;
178 }
179
180
XpmDestroyHashTable(XPMHASHENTRY ** Table)181 void XpmDestroyHashTable(XPMHASHENTRY **Table)
182 {
183 ILint i;
184 XPMHASHENTRY* Entry;
185
186 for (i = 0; i < XPM_HASH_LEN; ++i) {
187 while (Table[i] != NULL) {
188 Entry = Table[i]->Next;
189 ifree(Table[i]);
190 Table[i] = Entry;
191 }
192 }
193
194 ifree(Table);
195 }
196
197
XpmInsertEntry(XPMHASHENTRY ** Table,const ILubyte * Name,int Len,XpmPixel Colour)198 void XpmInsertEntry(XPMHASHENTRY **Table, const ILubyte* Name, int Len, XpmPixel Colour)
199 {
200 XPMHASHENTRY* NewEntry;
201 ILuint Index;
202 Index = XpmHash(Name, Len);
203
204 NewEntry = (XPMHASHENTRY*)ialloc(sizeof(XPMHASHENTRY));
205 if (NewEntry != NULL) {
206 NewEntry->Next = Table[Index];
207 memcpy(NewEntry->ColourName, Name, Len);
208 memcpy(NewEntry->ColourValue, Colour, sizeof(Colour));
209 Table[Index] = NewEntry;
210 }
211 }
212
213
XpmGetEntry(XPMHASHENTRY ** Table,const ILubyte * Name,int Len,XpmPixel Colour)214 void XpmGetEntry(XPMHASHENTRY **Table, const ILubyte* Name, int Len, XpmPixel Colour)
215 {
216 XPMHASHENTRY* Entry;
217 ILuint Index;
218
219 Index = XpmHash(Name, Len);
220 Entry = Table[Index];
221 while (Entry != NULL && strncmp((char*)(Entry->ColourName), (char*)Name, Len) != 0)
222 Entry = Entry->Next;
223
224 if (Entry != NULL)
225 memcpy(Colour, Entry->ColourValue, sizeof(Colour));
226 }
227
228 #endif //XPM_DONT_USE_HASHTABLE
229
230
XpmGetsInternal(ILubyte * Buffer,ILint MaxLen)231 ILint XpmGetsInternal(ILubyte *Buffer, ILint MaxLen)
232 {
233 ILint i = 0, Current;
234
235 if (ieof())
236 return IL_EOF;
237
238 while ((Current = igetc()) != IL_EOF && i < MaxLen - 1) {
239 if (Current == IL_EOF)
240 return 0;
241 if (Current == '\n') //unix line ending
242 break;
243
244 if (Current == '\r') { //dos/mac line ending
245 Current = igetc();
246 if (Current == '\n') //dos line ending
247 break;
248
249 if (Current == IL_EOF)
250 break;
251
252 Buffer[i++] = (ILubyte)Current;
253 continue;
254 }
255 Buffer[i++] = (ILubyte)Current;
256 }
257
258 Buffer[i++] = 0;
259
260 return i;
261 }
262
263
XpmGets(ILubyte * Buffer,ILint MaxLen)264 ILint XpmGets(ILubyte *Buffer, ILint MaxLen)
265 {
266 ILint Size, i, j;
267 ILboolean NotComment = IL_FALSE, InsideComment = IL_FALSE;
268
269 do {
270 Size = XpmGetsInternal(Buffer, MaxLen);
271 if (Size == IL_EOF)
272 return IL_EOF;
273
274 //skip leading whitespace (sometimes there's whitespace
275 //before a comment or before the pixel data)
276
277 for(i = 0; i < Size && isspace(Buffer[i]); ++i) ;
278 Size = Size - i;
279 for(j = 0; j < Size; ++j)
280 Buffer[j] = Buffer[j + i];
281
282 if (Size == 0)
283 continue;
284
285 if (Buffer[0] == '/' && Buffer[1] == '*') {
286 for (i = 2; i < Size; i++) {
287 if (Buffer[i] == '*' && Buffer[i+1] == '/') {
288 break;
289 }
290 }
291 if (i >= Size)
292 InsideComment = IL_TRUE;
293 }
294 else if (InsideComment) {
295 for (i = 0; i < Size; i++) {
296 if (Buffer[i] == '*' && Buffer[i+1] == '/') {
297 break;
298 }
299 }
300 if (i < Size)
301 InsideComment = IL_FALSE;
302 }
303 else {
304 NotComment = IL_TRUE;
305 }
306 } while (!NotComment);
307
308 return Size;
309 }
310
311
XpmGetInt(ILubyte * Buffer,ILint Size,ILint * Position)312 ILint XpmGetInt(ILubyte *Buffer, ILint Size, ILint *Position)
313 {
314 char Buff[1024];
315 ILint i, j;
316 ILboolean IsInNum = IL_FALSE;
317
318 for (i = *Position, j = 0; i < Size; i++) {
319 if (isdigit(Buffer[i])) {
320 IsInNum = IL_TRUE;
321 Buff[j++] = Buffer[i];
322 }
323 else {
324 if (IsInNum) {
325 Buff[j] = 0;
326 *Position = i;
327 return atoi(Buff);
328 }
329 }
330 }
331
332 return -1;
333 }
334
335
XpmPredefCol(char * Buff,XpmPixel * Colour)336 ILboolean XpmPredefCol(char *Buff, XpmPixel *Colour)
337 {
338 ILint len;
339 ILint val = 128;
340
341 if (!stricmp(Buff, "none")) {
342 (*Colour)[0] = 0;
343 (*Colour)[1] = 0;
344 (*Colour)[2] = 0;
345 (*Colour)[3] = 0;
346 return IL_TRUE;
347 }
348
349 (*Colour)[3] = 255;
350
351 if (!stricmp(Buff, "black")) {
352 (*Colour)[0] = 0;
353 (*Colour)[1] = 0;
354 (*Colour)[2] = 0;
355 return IL_TRUE;
356 }
357 if (!stricmp(Buff, "white")) {
358 (*Colour)[0] = 255;
359 (*Colour)[1] = 255;
360 (*Colour)[2] = 255;
361 return IL_TRUE;
362 }
363 if (!stricmp(Buff, "red")) {
364 (*Colour)[0] = 255;
365 (*Colour)[1] = 0;
366 (*Colour)[2] = 0;
367 return IL_TRUE;
368 }
369 if (!stricmp(Buff, "green")) {
370 (*Colour)[0] = 0;
371 (*Colour)[1] = 255;
372 (*Colour)[2] = 0;
373 return IL_TRUE;
374 }
375 if (!stricmp(Buff, "blue")) {
376 (*Colour)[0] = 0;
377 (*Colour)[1] = 0;
378 (*Colour)[2] = 255;
379 return IL_TRUE;
380 }
381 if (!stricmp(Buff, "yellow")) {
382 (*Colour)[0] = 255;
383 (*Colour)[1] = 255;
384 (*Colour)[2] = 0;
385 return IL_TRUE;
386 }
387 if (!stricmp(Buff, "cyan")) {
388 (*Colour)[0] = 0;
389 (*Colour)[1] = 255;
390 (*Colour)[2] = 255;
391 return IL_TRUE;
392 }
393 if (!stricmp(Buff, "gray")) {
394 (*Colour)[0] = 128;
395 (*Colour)[1] = 128;
396 (*Colour)[2] = 128;
397 return IL_TRUE;
398 }
399
400 //check for grayXXX codes (added 20040218)
401 len = ilCharStrLen(Buff);
402 if (len >= 4) {
403 if (Buff[0] == 'g' || Buff[0] == 'G'
404 || Buff[1] == 'r' || Buff[1] == 'R'
405 || Buff[2] == 'a' || Buff[2] == 'A'
406 || Buff[3] == 'y' || Buff[3] == 'Y') {
407 if (isdigit(Buff[4])) { // isdigit returns false on '\0'
408 val = Buff[4] - '0';
409 if (isdigit(Buff[5])) {
410 val = val*10 + Buff[5] - '0';
411 if (isdigit(Buff[6]))
412 val = val*10 + Buff[6] - '0';
413 }
414 val = (255*val)/100;
415 }
416 (*Colour)[0] = (ILubyte)val;
417 (*Colour)[1] = (ILubyte)val;
418 (*Colour)[2] = (ILubyte)val;
419 return IL_TRUE;
420 }
421 }
422
423
424 // Unknown colour string, so use black
425 // (changed 20040218)
426 (*Colour)[0] = 0;
427 (*Colour)[1] = 0;
428 (*Colour)[2] = 0;
429
430 return IL_FALSE;
431 }
432
433
434 #ifndef XPM_DONT_USE_HASHTABLE
XpmGetColour(ILubyte * Buffer,ILint Size,int Len,XPMHASHENTRY ** Table)435 ILboolean XpmGetColour(ILubyte *Buffer, ILint Size, int Len, XPMHASHENTRY **Table)
436 #else
437 ILboolean XpmGetColour(ILubyte *Buffer, ILint Size, int Len, XpmPixel* Colours)
438 #endif
439 {
440 ILint i = 0, j, strLen = 0;
441 ILubyte ColBuff[3];
442 char Buff[1024];
443
444 XpmPixel Colour;
445 ILubyte Name[XPM_MAX_CHAR_PER_PIXEL];
446
447 for ( ; i < Size; i++) {
448 if (Buffer[i] == '\"')
449 break;
450 }
451 i++; // Skip the quotes.
452
453 if (i >= Size)
454 return IL_FALSE;
455
456 // Get the characters.
457 for (j = 0; j < Len; ++j) {
458 Name[j] = Buffer[i++];
459 }
460
461 // Skip to the colour definition.
462 for ( ; i < Size; i++) {
463 if (Buffer[i] == 'c')
464 break;
465 }
466 i++; // Skip the 'c'.
467
468 if (i >= Size || Buffer[i] != ' ') { // no 'c' found...assume black
469 #ifndef XPM_DONT_USE_HASHTABLE
470 memset(Colour, 0, sizeof(Colour));
471 Colour[3] = 255;
472 XpmInsertEntry(Table, Name, Len, Colour);
473 #else
474 memset(Colours[Name[0]], 0, sizeof(Colour));
475 Colours[Name[0]][3] = 255;
476 #endif
477 return IL_TRUE;
478 }
479
480 for ( ; i < Size; i++) {
481 if (Buffer[i] != ' ')
482 break;
483 }
484
485 if (i >= Size)
486 return IL_FALSE;
487
488 if (Buffer[i] == '#') {
489 // colour string may 4 digits/color or 1 digit/color
490 // (added 20040218) TODO: is isxdigit() ANSI???
491 ++i;
492 while (i + strLen < Size && isxdigit(Buffer[i + strLen]))
493 ++strLen;
494
495 for (j = 0; j < 3; j++) {
496 if (strLen >= 10) { // 4 digits
497 ColBuff[0] = Buffer[i + j*4];
498 ColBuff[1] = Buffer[i + j*4 + 1];
499 }
500 else if (strLen >= 8) { // 3 digits
501 ColBuff[0] = Buffer[i + j*3];
502 ColBuff[1] = Buffer[i + j*3 + 1];
503 }
504 else if (strLen >= 6) { // 2 digits
505 ColBuff[0] = Buffer[i + j*2];
506 ColBuff[1] = Buffer[i + j*2 + 1];
507 }
508 else if(j < strLen) { // 1 digit, strLen >= 1
509 ColBuff[0] = Buffer[i + j];
510 ColBuff[1] = 0;
511 }
512
513 ColBuff[2] = 0; // add terminating '\0' char
514 Colour[j] = (ILubyte)strtol((char*)ColBuff, NULL, 16);
515 }
516 Colour[3] = 255; // Full alpha.
517 }
518 else {
519 for (j = 0; i < Size; i++) {
520 if (!isalnum(Buffer[i]))
521 break;
522 Buff[j++] = Buffer[i];
523 }
524 Buff[j] = 0;
525
526 if (i >= Size)
527 return IL_FALSE;
528
529 if (!XpmPredefCol(Buff, &Colour))
530
531 return IL_FALSE;
532 }
533
534
535 #ifndef XPM_DONT_USE_HASHTABLE
536 XpmInsertEntry(Table, Name, Len, Colour);
537 #else
538 memcpy(Colours[Name[0]], Colour, sizeof(Colour));
539 #endif
540 return IL_TRUE;
541 }
542
543
iLoadXpmInternal()544 ILboolean iLoadXpmInternal()
545 {
546 #define BUFFER_SIZE 2000
547 ILubyte Buffer[BUFFER_SIZE], *Data;
548 ILint Size, Pos, Width, Height, NumColours, i, x, y;
549
550 ILint CharsPerPixel;
551
552 #ifndef XPM_DONT_USE_HASHTABLE
553 XPMHASHENTRY **HashTable;
554 #else
555 XpmPixel *Colours;
556 ILint Offset;
557 #endif
558
559 Size = XpmGetsInternal(Buffer, BUFFER_SIZE);
560 if (strncmp("/* XPM */", (char*)Buffer, strlen("/* XPM */"))) {
561 ilSetError(IL_INVALID_FILE_HEADER);
562 return IL_FALSE;
563 }
564
565 Size = XpmGets(Buffer, BUFFER_SIZE);
566 // @TODO: Actually check the variable name here.
567
568 Size = XpmGets(Buffer, BUFFER_SIZE);
569 Pos = 0;
570 Width = XpmGetInt(Buffer, Size, &Pos);
571 Height = XpmGetInt(Buffer, Size, &Pos);
572 NumColours = XpmGetInt(Buffer, Size, &Pos);
573
574 CharsPerPixel = XpmGetInt(Buffer, Size, &Pos);
575
576 #ifdef XPM_DONT_USE_HASHTABLE
577 if (CharsPerPixel != 1) {
578 ilSetError(IL_FORMAT_NOT_SUPPORTED);
579 return IL_FALSE;
580 }
581 #endif
582
583 if (CharsPerPixel > XPM_MAX_CHAR_PER_PIXEL
584 || Width*CharsPerPixel > BUFFER_SIZE) {
585 ilSetError(IL_FORMAT_NOT_SUPPORTED);
586 return IL_FALSE;
587 }
588
589 #ifndef XPM_DONT_USE_HASHTABLE
590 HashTable = XpmCreateHashTable();
591 if (HashTable == NULL)
592 return IL_FALSE;
593 #else
594 Colours = ialloc(256 * sizeof(XpmPixel));
595 if (Colours == NULL)
596 return IL_FALSE;
597 #endif
598
599 for (i = 0; i < NumColours; i++) {
600 Size = XpmGets(Buffer, BUFFER_SIZE);
601 #ifndef XPM_DONT_USE_HASHTABLE
602 if (!XpmGetColour(Buffer, Size, CharsPerPixel, HashTable)) {
603 XpmDestroyHashTable(HashTable);
604 #else
605 if (!XpmGetColour(Buffer, Size, CharsPerPixel, Colours)) {
606 ifree(Colours);
607 #endif
608 return IL_FALSE;
609 }
610 }
611
612 if (!ilTexImage(Width, Height, 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL)) {
613 #ifndef XPM_DONT_USE_HASHTABLE
614 XpmDestroyHashTable(HashTable);
615 #else
616 ifree(Colours);
617 #endif
618 return IL_FALSE;
619 }
620
621 Data = iCurImage->Data;
622
623 for (y = 0; y < Height; y++) {
624 Size = XpmGets(Buffer, BUFFER_SIZE);
625 for (x = 0; x < Width; x++) {
626 #ifndef XPM_DONT_USE_HASHTABLE
627 XpmGetEntry(HashTable, &Buffer[1 + x*CharsPerPixel], CharsPerPixel, &Data[(x << 2)]);
628 #else
629 Offset = (x << 2);
630 Data[Offset + 0] = Colours[Buffer[x + 1]][0];
631 Data[Offset + 1] = Colours[Buffer[x + 1]][1];
632 Data[Offset + 2] = Colours[Buffer[x + 1]][2];
633 Data[Offset + 3] = Colours[Buffer[x + 1]][3];
634 #endif
635 }
636
637 Data += iCurImage->Bps;
638 }
639
640 //added 20040218
641 iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
642
643
644 #ifndef XPM_DONT_USE_HASHTABLE
645 XpmDestroyHashTable(HashTable);
646 #else
647 ifree(Colours);
648 #endif
649 return IL_TRUE;
650
651 #undef BUFFER_SIZE
652 }
653
654 #endif//IL_NO_XPM
655
656