]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/libsdl-image/contrib/IMG_xpm.c
acab8bb9bca1c853e2e3fbbdf6444966b109d900
[l4.git] / l4 / pkg / libsdl-image / contrib / IMG_xpm.c
1 /*
2     SDL_image:  An example image loading library for use with SDL
3     Copyright (C) 1997-2009 Sam Lantinga
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22
23 /*
24  * XPM (X PixMap) image loader:
25  *
26  * Supports the XPMv3 format, EXCEPT:
27  * - hotspot coordinates are ignored
28  * - only colour ('c') colour symbols are used
29  * - rgb.txt is not used (for portability), so only RGB colours
30  *   are recognized (#rrggbb etc) - only a few basic colour names are
31  *   handled
32  *
33  * The result is an 8bpp indexed surface if possible, otherwise 32bpp.
34  * The colourkey is correctly set if transparency is used.
35  * 
36  * Besides the standard API, also provides
37  *
38  *     SDL_Surface *IMG_ReadXPMFromArray(char **xpm)
39  *
40  * that reads the image data from an XPM file included in the C source.
41  *
42  * TODO: include rgb.txt here. The full table (from solaris 2.6) only
43  * requires about 13K in binary form.
44  */
45
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <ctype.h>
50
51 #include "SDL_image.h"
52
53 #ifdef LOAD_XPM
54
55 /* See if an image is contained in a data source */
56 int IMG_isXPM(SDL_RWops *src)
57 {
58         int start;
59         int is_XPM;
60         char magic[9];
61
62         if ( !src )
63                 return 0;
64         start = SDL_RWtell(src);
65         is_XPM = 0;
66         if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
67                 if ( memcmp(magic, "/* XPM */", sizeof(magic)) == 0 ) {
68                         is_XPM = 1;
69                 }
70         }
71         SDL_RWseek(src, start, SEEK_SET);
72         return(is_XPM);
73 }
74
75 /* Hash table to look up colors from pixel strings */
76 #define STARTING_HASH_SIZE 256
77
78 struct hash_entry {
79         char *key;
80         Uint32 color;
81         struct hash_entry *next;
82 };
83
84 struct color_hash {
85         struct hash_entry **table;
86         struct hash_entry *entries; /* array of all entries */
87         struct hash_entry *next_free;
88         int size;
89         int maxnum;
90 };
91
92 static int hash_key(const char *key, int cpp, int size)
93 {
94         int hash;
95
96         hash = 0;
97         while ( cpp-- > 0 ) {
98                 hash = hash * 33 + *key++;
99         }
100         return hash & (size - 1);
101 }
102
103 static struct color_hash *create_colorhash(int maxnum)
104 {
105         int bytes, s;
106         struct color_hash *hash;
107
108         /* we know how many entries we need, so we can allocate
109            everything here */
110         hash = malloc(sizeof *hash);
111         if(!hash)
112                 return NULL;
113
114         /* use power-of-2 sized hash table for decoding speed */
115         for(s = STARTING_HASH_SIZE; s < maxnum; s <<= 1)
116                 ;
117         hash->size = s;
118         hash->maxnum = maxnum;
119         bytes = hash->size * sizeof(struct hash_entry **);
120         hash->entries = NULL;   /* in case malloc fails */
121         hash->table = malloc(bytes);
122         if(!hash->table)
123                 return NULL;
124         memset(hash->table, 0, bytes);
125         hash->entries = malloc(maxnum * sizeof(struct hash_entry));
126         if(!hash->entries)
127                 return NULL;
128         hash->next_free = hash->entries;
129         return hash;
130 }
131
132 static int add_colorhash(struct color_hash *hash,
133                          char *key, int cpp, Uint32 color)
134 {
135         int index = hash_key(key, cpp, hash->size);
136         struct hash_entry *e = hash->next_free++;
137         e->color = color;
138         e->key = key;
139         e->next = hash->table[index];
140         hash->table[index] = e;
141         return 1;
142 }
143
144 /* fast lookup that works if cpp == 1 */
145 #define QUICK_COLORHASH(hash, key) ((hash)->table[*(Uint8 *)(key)]->color)
146
147 static Uint32 get_colorhash(struct color_hash *hash, const char *key, int cpp)
148 {
149         struct hash_entry *entry = hash->table[hash_key(key, cpp, hash->size)];
150         while(entry) {
151                 if(memcmp(key, entry->key, cpp) == 0)
152                         return entry->color;
153                 entry = entry->next;
154         }
155         return 0;               /* garbage in - garbage out */
156 }
157
158 static void free_colorhash(struct color_hash *hash)
159 {
160         if(hash && hash->table) {
161                 free(hash->table);
162                 free(hash->entries);
163                 free(hash);
164         }
165 }
166
167 /* portable case-insensitive string comparison */
168 static int string_equal(const char *a, const char *b, int n)
169 {
170         while(*a && *b && n) {
171                 if(toupper((unsigned char)*a) != toupper((unsigned char)*b))
172                         return 0;
173                 a++;
174                 b++;
175                 n--;
176         }
177         return *a == *b;
178 }
179
180 #define ARRAYSIZE(a) (int)(sizeof(a) / sizeof((a)[0]))
181
182 /*
183  * convert colour spec to RGB (in 0xrrggbb format).
184  * return 1 if successful.
185  */
186 static int color_to_rgb(char *spec, int speclen, Uint32 *rgb)
187 {
188         /* poor man's rgb.txt */
189         static struct { char *name; Uint32 rgb; } known[] = {
190                 {"none",  0xffffffff},
191                 {"black", 0x00000000},
192                 {"white", 0x00ffffff},
193                 {"red",   0x00ff0000},
194                 {"green", 0x0000ff00},
195                 {"blue",  0x000000ff}
196         };
197
198         if(spec[0] == '#') {
199                 char buf[7];
200                 switch(speclen) {
201                 case 4:
202                         buf[0] = buf[1] = spec[1];
203                         buf[2] = buf[3] = spec[2];
204                         buf[4] = buf[5] = spec[3];
205                         break;
206                 case 7:
207                         memcpy(buf, spec + 1, 6);
208                         break;
209                 case 13:
210                         buf[0] = spec[1];
211                         buf[1] = spec[2];
212                         buf[2] = spec[5];
213                         buf[3] = spec[6];
214                         buf[4] = spec[9];
215                         buf[5] = spec[10];
216                         break;
217                 }
218                 buf[6] = '\0';
219                 *rgb = strtol(buf, NULL, 16);
220                 return 1;
221         } else {
222                 int i;
223                 for(i = 0; i < ARRAYSIZE(known); i++)
224                         if(string_equal(known[i].name, spec, speclen)) {
225                                 *rgb = known[i].rgb;
226                                 return 1;
227                         }
228                 return 0;
229         }
230 }
231
232 #ifndef MAX
233 #define MAX(a, b) ((a) > (b) ? (a) : (b))
234 #endif
235
236 static char *linebuf;
237 static int buflen;
238 static char *error;
239
240 /*
241  * Read next line from the source.
242  * If len > 0, it's assumed to be at least len chars (for efficiency).
243  * Return NULL and set error upon EOF or parse error.
244  */
245 static char *get_next_line(char ***lines, SDL_RWops *src, int len)
246 {
247         if(lines) {
248                 return *(*lines)++;
249         } else {
250                 char c;
251                 int n;
252                 do {
253                         if(SDL_RWread(src, &c, 1, 1) <= 0) {
254                                 error = "Premature end of data";
255                                 return NULL;
256                         }
257                 } while(c != '"');
258                 if(len) {
259                         len += 4;       /* "\",\n\0" */
260                         if(len > buflen){
261                                 buflen = len;
262                                 linebuf = realloc(linebuf, buflen);
263                                 if(!linebuf) {
264                                         error = "Out of memory";
265                                         return NULL;
266                                 }
267                         }
268                         if(SDL_RWread(src, linebuf, len - 1, 1) <= 0) {
269                                 error = "Premature end of data";
270                                 return NULL;
271                         }
272                         n = len - 2;
273                 } else {
274                         n = 0;
275                         do {
276                                 if(n >= buflen - 1) {
277                                         if(buflen == 0)
278                                                 buflen = 16;
279                                         buflen *= 2;
280                                         linebuf = realloc(linebuf, buflen);
281                                         if(!linebuf) {
282                                                 error = "Out of memory";
283                                                 return NULL;
284                                         }
285                                 }
286                                 if(SDL_RWread(src, linebuf + n, 1, 1) <= 0) {
287                                         error = "Premature end of data";
288                                         return NULL;
289                                 }
290                         } while(linebuf[n++] != '"');
291                         n--;
292                 }
293                 linebuf[n] = '\0';
294                 return linebuf;
295         }
296 }
297
298 #define SKIPSPACE(p)                            \
299 do {                                            \
300         while(isspace((unsigned char)*(p)))     \
301               ++(p);                            \
302 } while(0)
303
304 #define SKIPNONSPACE(p)                                 \
305 do {                                                    \
306         while(!isspace((unsigned char)*(p)) && *p)      \
307               ++(p);                                    \
308 } while(0)
309
310 /* read XPM from either array or RWops */
311 static SDL_Surface *load_xpm(char **xpm, SDL_RWops *src)
312 {
313         int start = 0;
314         SDL_Surface *image = NULL;
315         int index;
316         int x, y;
317         int w, h, ncolors, cpp;
318         int indexed;
319         Uint8 *dst;
320         struct color_hash *colors = NULL;
321         SDL_Color *im_colors = NULL;
322         char *keystrings = NULL, *nextkey;
323         char *line;
324         char ***xpmlines = NULL;
325         int pixels_len;
326
327         error = NULL;
328         linebuf = NULL;
329         buflen = 0;
330
331         if ( src ) 
332                 start = SDL_RWtell(src);
333
334         if(xpm)
335                 xpmlines = &xpm;
336
337         line = get_next_line(xpmlines, src, 0);
338         if(!line)
339                 goto done;
340         /*
341          * The header string of an XPMv3 image has the format
342          *
343          * <width> <height> <ncolors> <cpp> [ <hotspot_x> <hotspot_y> ]
344          *
345          * where the hotspot coords are intended for mouse cursors.
346          * Right now we don't use the hotspots but it should be handled
347          * one day.
348          */
349         if(sscanf(line, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4
350            || w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) {
351                 error = "Invalid format description";
352                 goto done;
353         }
354
355         keystrings = malloc(ncolors * cpp);
356         if(!keystrings) {
357                 error = "Out of memory";
358                 goto done;
359         }
360         nextkey = keystrings;
361
362         /* Create the new surface */
363         if(ncolors <= 256) {
364                 indexed = 1;
365                 image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 8,
366                                              0, 0, 0, 0);
367                 im_colors = image->format->palette->colors;
368                 image->format->palette->ncolors = ncolors;
369         } else {
370                 indexed = 0;
371                 image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32,
372                                              0xff0000, 0x00ff00, 0x0000ff, 0);
373         }
374         if(!image) {
375                 /* Hmm, some SDL error (out of memory?) */
376                 goto done;
377         }
378
379         /* Read the colors */
380         colors = create_colorhash(ncolors);
381         if (!colors) {
382                 error = "Out of memory";
383                 goto done;
384         }
385         for(index = 0; index < ncolors; ++index ) {
386                 char *p;
387                 line = get_next_line(xpmlines, src, 0);
388                 if(!line)
389                         goto done;
390
391                 p = line + cpp + 1;
392
393                 /* parse a colour definition */
394                 for(;;) {
395                         char nametype;
396                         char *colname;
397                         Uint32 rgb, pixel;
398
399                         SKIPSPACE(p);
400                         if(!*p) {
401                                 error = "colour parse error";
402                                 goto done;
403                         }
404                         nametype = *p;
405                         SKIPNONSPACE(p);
406                         SKIPSPACE(p);
407                         colname = p;
408                         SKIPNONSPACE(p);
409                         if(nametype == 's')
410                                 continue;      /* skip symbolic colour names */
411
412                         if(!color_to_rgb(colname, p - colname, &rgb))
413                                 continue;
414
415                         memcpy(nextkey, line, cpp);
416                         if(indexed) {
417                                 SDL_Color *c = im_colors + index;
418                                 c->r = (Uint8)(rgb >> 16);
419                                 c->g = (Uint8)(rgb >> 8);
420                                 c->b = (Uint8)(rgb);
421                                 pixel = index;
422                         } else
423                                 pixel = rgb;
424                         add_colorhash(colors, nextkey, cpp, pixel);
425                         nextkey += cpp;
426                         if(rgb == 0xffffffff)
427                                 SDL_SetColorKey(image, SDL_SRCCOLORKEY, pixel);
428                         break;
429                 }
430         }
431
432         /* Read the pixels */
433         pixels_len = w * cpp;
434         dst = image->pixels;
435         for(y = 0; y < h; y++) {
436                 line = get_next_line(xpmlines, src, pixels_len);
437                 if(indexed) {
438                         /* optimization for some common cases */
439                         if(cpp == 1)
440                                 for(x = 0; x < w; x++)
441                                         dst[x] = (Uint8)QUICK_COLORHASH(colors,
442                                                                  line + x);
443                         else
444                                 for(x = 0; x < w; x++)
445                                         dst[x] = (Uint8)get_colorhash(colors,
446                                                                line + x * cpp,
447                                                                cpp);
448                 } else {
449                         for (x = 0; x < w; x++)
450                                 ((Uint32*)dst)[x] = get_colorhash(colors,
451                                                                 line + x * cpp,
452                                                                   cpp);
453                 }
454                 dst += image->pitch;
455         }
456
457 done:
458         if(error) {
459                 if ( src )
460                         SDL_RWseek(src, start, SEEK_SET);
461                 if ( image ) {
462                         SDL_FreeSurface(image);
463                         image = NULL;
464                 }
465                 IMG_SetError(error);
466         }
467         free(keystrings);
468         free_colorhash(colors);
469         free(linebuf);
470         return(image);
471 }
472
473 /* Load a XPM type image from an RWops datasource */
474 SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src)
475 {
476         if ( !src ) {
477                 /* The error message has been set in SDL_RWFromFile */
478                 return NULL;
479         }
480         return load_xpm(NULL, src);
481 }
482
483 SDL_Surface *IMG_ReadXPMFromArray(char **xpm)
484 {
485         return load_xpm(xpm, NULL);
486 }
487
488 #else  /* not LOAD_XPM */
489
490 /* See if an image is contained in a data source */
491 int IMG_isXPM(SDL_RWops *src)
492 {
493         return(0);
494 }
495
496
497 /* Load a XPM type image from an SDL datasource */
498 SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src)
499 {
500         return(NULL);
501 }
502
503 SDL_Surface *IMG_ReadXPMFromArray(char **xpm)
504 {
505     return NULL;
506 }
507 #endif /* not LOAD_XPM */