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