Skip to content

Latest commit

 

History

History
514 lines (461 loc) · 11.1 KB

IMG_xpm.c

File metadata and controls

514 lines (461 loc) · 11.1 KB
 
Nov 29, 2000
Nov 29, 2000
1
/*
Dec 31, 2011
Dec 31, 2011
2
3
SDL_image: An example image loading library for use with SDL
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
Nov 29, 2000
Nov 29, 2000
4
Dec 31, 2011
Dec 31, 2011
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.
Nov 29, 2000
Nov 29, 2000
8
Dec 31, 2011
Dec 31, 2011
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:
Nov 29, 2000
Nov 29, 2000
12
Dec 31, 2011
Dec 31, 2011
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.
Nov 29, 2000
Nov 29, 2000
20
21
*/
Apr 28, 2001
Apr 28, 2001
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.
*/
Nov 29, 2000
Nov 29, 2000
44
Nov 30, 2000
Nov 30, 2000
45
#include <stdlib.h>
Nov 29, 2000
Nov 29, 2000
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)
{
Feb 4, 2006
Feb 4, 2006
57
58
int start;
int is_XPM;
Apr 28, 2001
Apr 28, 2001
59
char magic[9];
Nov 29, 2000
Nov 29, 2000
60
Feb 13, 2007
Feb 13, 2007
61
62
if ( !src )
return 0;
Feb 4, 2006
Feb 4, 2006
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;
}
}
Nov 8, 2009
Nov 8, 2009
70
SDL_RWseek(src, start, RW_SEEK_SET);
Feb 4, 2006
Feb 4, 2006
71
return(is_XPM);
Nov 29, 2000
Nov 29, 2000
72
73
74
}
/* Hash table to look up colors from pixel strings */
Dec 6, 2000
Dec 6, 2000
75
76
77
78
79
80
81
82
#define STARTING_HASH_SIZE 256
struct hash_entry {
char *key;
Uint32 color;
struct hash_entry *next;
};
Nov 29, 2000
Nov 29, 2000
83
struct color_hash {
Dec 6, 2000
Dec 6, 2000
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;
Nov 29, 2000
Nov 29, 2000
89
90
};
Dec 6, 2000
Dec 6, 2000
91
static int hash_key(const char *key, int cpp, int size)
Nov 29, 2000
Nov 29, 2000
92
93
94
95
96
{
int hash;
hash = 0;
while ( cpp-- > 0 ) {
Dec 6, 2000
Dec 6, 2000
97
hash = hash * 33 + *key++;
Nov 29, 2000
Nov 29, 2000
98
}
Dec 6, 2000
Dec 6, 2000
99
return hash & (size - 1);
Nov 29, 2000
Nov 29, 2000
100
101
}
Dec 6, 2000
Dec 6, 2000
102
static struct color_hash *create_colorhash(int maxnum)
Nov 29, 2000
Nov 29, 2000
103
{
Dec 6, 2000
Dec 6, 2000
104
int bytes, s;
Nov 29, 2000
Nov 29, 2000
105
106
struct color_hash *hash;
Dec 6, 2000
Dec 6, 2000
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/* we know how many entries we need, so we can allocate
everything here */
hash = malloc(sizeof *hash);
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;
bytes = hash->size * sizeof(struct hash_entry **);
hash->entries = NULL; /* in case malloc fails */
hash->table = malloc(bytes);
if(!hash->table)
return NULL;
memset(hash->table, 0, bytes);
hash->entries = malloc(maxnum * sizeof(struct hash_entry));
Aug 25, 2011
Aug 25, 2011
125
126
if(!hash->entries) {
free(hash->table);
Dec 6, 2000
Dec 6, 2000
127
return NULL;
Aug 25, 2011
Aug 25, 2011
128
}
Dec 6, 2000
Dec 6, 2000
129
130
hash->next_free = hash->entries;
return hash;
Nov 29, 2000
Nov 29, 2000
131
132
133
}
static int add_colorhash(struct color_hash *hash,
Dec 6, 2000
Dec 6, 2000
134
char *key, int cpp, Uint32 color)
Nov 29, 2000
Nov 29, 2000
135
{
Dec 6, 2000
Dec 6, 2000
136
137
138
139
140
141
142
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;
Nov 29, 2000
Nov 29, 2000
143
144
}
Dec 6, 2000
Dec 6, 2000
145
/* fast lookup that works if cpp == 1 */
Mar 2, 2001
Mar 2, 2001
146
#define QUICK_COLORHASH(hash, key) ((hash)->table[*(Uint8 *)(key)]->color)
Dec 6, 2000
Dec 6, 2000
147
148
static Uint32 get_colorhash(struct color_hash *hash, const char *key, int cpp)
Nov 29, 2000
Nov 29, 2000
149
{
Dec 6, 2000
Dec 6, 2000
150
151
152
153
154
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;
Nov 29, 2000
Nov 29, 2000
155
}
Dec 6, 2000
Dec 6, 2000
156
return 0; /* garbage in - garbage out */
Nov 29, 2000
Nov 29, 2000
157
158
159
160
}
static void free_colorhash(struct color_hash *hash)
{
Dec 6, 2000
Dec 6, 2000
161
162
163
164
if(hash && hash->table) {
free(hash->table);
free(hash->entries);
free(hash);
Nov 29, 2000
Nov 29, 2000
165
166
167
}
}
Apr 28, 2001
Apr 28, 2001
168
169
170
171
172
173
174
175
176
177
178
179
180
/* 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;
}
Dec 6, 2000
Dec 6, 2000
181
#define ARRAYSIZE(a) (int)(sizeof(a) / sizeof((a)[0]))
Nov 29, 2000
Nov 29, 2000
182
Dec 6, 2000
Dec 6, 2000
183
184
/*
* convert colour spec to RGB (in 0xrrggbb format).
Apr 28, 2001
Apr 28, 2001
185
* return 1 if successful.
Dec 6, 2000
Dec 6, 2000
186
*/
Apr 28, 2001
Apr 28, 2001
187
static int color_to_rgb(char *spec, int speclen, Uint32 *rgb)
Dec 6, 2000
Dec 6, 2000
188
189
190
191
192
193
194
195
196
197
198
199
200
{
/* 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];
Apr 28, 2001
Apr 28, 2001
201
202
203
204
205
switch(speclen) {
case 4:
buf[0] = buf[1] = spec[1];
buf[2] = buf[3] = spec[2];
buf[4] = buf[5] = spec[3];
Nov 29, 2000
Nov 29, 2000
206
break;
Apr 28, 2001
Apr 28, 2001
207
208
case 7:
memcpy(buf, spec + 1, 6);
Nov 29, 2000
Nov 29, 2000
209
break;
Apr 28, 2001
Apr 28, 2001
210
211
212
213
214
215
216
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];
Nov 29, 2000
Nov 29, 2000
217
break;
Dec 6, 2000
Dec 6, 2000
218
219
220
221
222
223
224
}
buf[6] = '\0';
*rgb = strtol(buf, NULL, 16);
return 1;
} else {
int i;
for(i = 0; i < ARRAYSIZE(known); i++)
Apr 28, 2001
Apr 28, 2001
225
if(string_equal(known[i].name, spec, speclen)) {
Dec 6, 2000
Dec 6, 2000
226
227
228
229
*rgb = known[i].rgb;
return 1;
}
return 0;
Nov 29, 2000
Nov 29, 2000
230
231
232
}
}
Apr 28, 2001
Apr 28, 2001
233
234
235
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
Dec 6, 2000
Dec 6, 2000
236
Apr 28, 2001
Apr 28, 2001
237
238
239
240
241
242
243
244
245
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.
*/
Apr 28, 2001
Apr 28, 2001
246
static char *get_next_line(char ***lines, SDL_RWops *src, int len)
Dec 6, 2000
Dec 6, 2000
247
{
Jan 2, 2012
Jan 2, 2012
248
249
char *linebufnew;
Apr 28, 2001
Apr 28, 2001
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
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;
Jan 2, 2012
Jan 2, 2012
265
linebufnew = realloc(linebuf, buflen);
Aug 25, 2011
Aug 25, 2011
266
267
if(!linebufnew) {
free(linebuf);
Apr 28, 2001
Apr 28, 2001
268
269
270
error = "Out of memory";
return NULL;
}
Aug 25, 2011
Aug 25, 2011
271
linebuf = linebufnew;
Apr 28, 2001
Apr 28, 2001
272
273
274
275
276
277
278
279
280
281
282
283
284
}
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;
Jan 2, 2012
Jan 2, 2012
285
linebufnew = realloc(linebuf, buflen);
Aug 25, 2011
Aug 25, 2011
286
287
if(!linebufnew) {
free(linebuf);
Apr 28, 2001
Apr 28, 2001
288
289
290
error = "Out of memory";
return NULL;
}
Aug 25, 2011
Aug 25, 2011
291
linebuf = linebufnew;
Apr 28, 2001
Apr 28, 2001
292
293
294
295
296
297
298
299
300
301
302
}
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;
}
Dec 6, 2000
Dec 6, 2000
303
304
}
Apr 28, 2001
Apr 28, 2001
305
306
307
308
309
#define SKIPSPACE(p) \
do { \
while(isspace((unsigned char)*(p))) \
++(p); \
} while(0)
Dec 6, 2000
Dec 6, 2000
310
Apr 28, 2001
Apr 28, 2001
311
312
313
314
315
316
317
318
#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)
Nov 29, 2000
Nov 29, 2000
319
{
Jul 21, 2007
Jul 21, 2007
320
int start = 0;
Apr 28, 2001
Apr 28, 2001
321
SDL_Surface *image = NULL;
Nov 29, 2000
Nov 29, 2000
322
int index;
Dec 6, 2000
Dec 6, 2000
323
int x, y;
Nov 29, 2000
Nov 29, 2000
324
325
326
int w, h, ncolors, cpp;
int indexed;
Uint8 *dst;
Apr 28, 2001
Apr 28, 2001
327
struct color_hash *colors = NULL;
Dec 6, 2000
Dec 6, 2000
328
SDL_Color *im_colors = NULL;
Apr 28, 2001
Apr 28, 2001
329
330
331
332
333
char *keystrings = NULL, *nextkey;
char *line;
char ***xpmlines = NULL;
int pixels_len;
May 11, 2001
May 11, 2001
334
335
336
337
error = NULL;
linebuf = NULL;
buflen = 0;
May 27, 2015
May 27, 2015
338
if ( src )
Feb 13, 2007
Feb 13, 2007
339
start = SDL_RWtell(src);
Feb 4, 2006
Feb 4, 2006
340
Apr 28, 2001
Apr 28, 2001
341
342
343
344
345
346
if(xpm)
xpmlines = &xpm;
line = get_next_line(xpmlines, src, 0);
if(!line)
goto done;
Dec 6, 2000
Dec 6, 2000
347
348
349
350
351
352
353
354
355
/*
* 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.
*/
Apr 28, 2001
Apr 28, 2001
356
if(sscanf(line, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4
Dec 6, 2000
Dec 6, 2000
357
|| w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) {
Apr 28, 2001
Apr 28, 2001
358
359
error = "Invalid format description";
goto done;
Dec 6, 2000
Dec 6, 2000
360
}
Nov 29, 2000
Nov 29, 2000
361
Dec 6, 2000
Dec 6, 2000
362
363
keystrings = malloc(ncolors * cpp);
if(!keystrings) {
Apr 28, 2001
Apr 28, 2001
364
365
error = "Out of memory";
goto done;
Dec 6, 2000
Dec 6, 2000
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
}
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?) */
Apr 28, 2001
Apr 28, 2001
383
goto done;
Dec 6, 2000
Dec 6, 2000
384
}
Nov 29, 2000
Nov 29, 2000
385
386
/* Read the colors */
Dec 6, 2000
Dec 6, 2000
387
colors = create_colorhash(ncolors);
Apr 28, 2001
Apr 28, 2001
388
if (!colors) {
Dec 6, 2000
Dec 6, 2000
389
390
error = "Out of memory";
goto done;
Nov 29, 2000
Nov 29, 2000
391
}
Dec 6, 2000
Dec 6, 2000
392
for(index = 0; index < ncolors; ++index ) {
Apr 28, 2001
Apr 28, 2001
393
394
395
396
char *p;
line = get_next_line(xpmlines, src, 0);
if(!line)
goto done;
Dec 6, 2000
Dec 6, 2000
397
Apr 28, 2001
Apr 28, 2001
398
p = line + cpp + 1;
Dec 6, 2000
Dec 6, 2000
399
400
401
402
403
/* parse a colour definition */
for(;;) {
char nametype;
char *colname;
Apr 28, 2001
Apr 28, 2001
404
405
406
407
408
Uint32 rgb, pixel;
SKIPSPACE(p);
if(!*p) {
error = "colour parse error";
Dec 6, 2000
Dec 6, 2000
409
goto done;
Nov 29, 2000
Nov 29, 2000
410
}
Apr 28, 2001
Apr 28, 2001
411
412
413
414
415
nametype = *p;
SKIPNONSPACE(p);
SKIPSPACE(p);
colname = p;
SKIPNONSPACE(p);
Dec 6, 2000
Dec 6, 2000
416
417
418
if(nametype == 's')
continue; /* skip symbolic colour names */
Apr 28, 2001
Apr 28, 2001
419
if(!color_to_rgb(colname, p - colname, &rgb))
Dec 6, 2000
Dec 6, 2000
420
421
continue;
Apr 28, 2001
Apr 28, 2001
422
memcpy(nextkey, line, cpp);
Dec 6, 2000
Dec 6, 2000
423
424
if(indexed) {
SDL_Color *c = im_colors + index;
May 11, 2006
May 11, 2006
425
426
427
c->r = (Uint8)(rgb >> 16);
c->g = (Uint8)(rgb >> 8);
c->b = (Uint8)(rgb);
Apr 28, 2001
Apr 28, 2001
428
pixel = index;
Dec 6, 2000
Dec 6, 2000
429
} else
Apr 28, 2001
Apr 28, 2001
430
431
pixel = rgb;
add_colorhash(colors, nextkey, cpp, pixel);
Dec 6, 2000
Dec 6, 2000
432
433
nextkey += cpp;
if(rgb == 0xffffffff)
Apr 28, 2001
Apr 28, 2001
434
SDL_SetColorKey(image, SDL_SRCCOLORKEY, pixel);
Dec 6, 2000
Dec 6, 2000
435
break;
Nov 29, 2000
Nov 29, 2000
436
437
438
439
}
}
/* Read the pixels */
Dec 6, 2000
Dec 6, 2000
440
441
pixels_len = w * cpp;
dst = image->pixels;
Apr 28, 2001
Apr 28, 2001
442
443
for(y = 0; y < h; y++) {
line = get_next_line(xpmlines, src, pixels_len);
Dec 6, 2000
Dec 6, 2000
444
445
446
447
if(indexed) {
/* optimization for some common cases */
if(cpp == 1)
for(x = 0; x < w; x++)
May 11, 2006
May 11, 2006
448
dst[x] = (Uint8)QUICK_COLORHASH(colors,
Apr 28, 2001
Apr 28, 2001
449
line + x);
Dec 6, 2000
Dec 6, 2000
450
451
else
for(x = 0; x < w; x++)
May 11, 2006
May 11, 2006
452
dst[x] = (Uint8)get_colorhash(colors,
Apr 28, 2001
Apr 28, 2001
453
line + x * cpp,
Dec 6, 2000
Dec 6, 2000
454
455
456
457
cpp);
} else {
for (x = 0; x < w; x++)
((Uint32*)dst)[x] = get_colorhash(colors,
Apr 28, 2001
Apr 28, 2001
458
line + x * cpp,
Dec 6, 2000
Dec 6, 2000
459
460
461
cpp);
}
dst += image->pitch;
Nov 29, 2000
Nov 29, 2000
462
}
Dec 6, 2000
Dec 6, 2000
463
Nov 29, 2000
Nov 29, 2000
464
done:
Dec 6, 2000
Dec 6, 2000
465
if(error) {
Feb 13, 2007
Feb 13, 2007
466
if ( src )
Nov 8, 2009
Nov 8, 2009
467
SDL_RWseek(src, start, RW_SEEK_SET);
Feb 4, 2006
Feb 4, 2006
468
469
470
471
if ( image ) {
SDL_FreeSurface(image);
image = NULL;
}
Dec 6, 2000
Dec 6, 2000
472
473
474
IMG_SetError(error);
}
free(keystrings);
Nov 29, 2000
Nov 29, 2000
475
free_colorhash(colors);
Apr 28, 2001
Apr 28, 2001
476
free(linebuf);
Nov 29, 2000
Nov 29, 2000
477
478
479
return(image);
}
Apr 28, 2001
Apr 28, 2001
480
481
482
/* Load a XPM type image from an RWops datasource */
SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src)
{
Jan 4, 2004
Jan 4, 2004
483
484
485
486
if ( !src ) {
/* The error message has been set in SDL_RWFromFile */
return NULL;
}
Apr 28, 2001
Apr 28, 2001
487
488
489
490
491
492
493
494
495
return load_xpm(NULL, src);
}
SDL_Surface *IMG_ReadXPMFromArray(char **xpm)
{
return load_xpm(xpm, NULL);
}
#else /* not LOAD_XPM */
Nov 29, 2000
Nov 29, 2000
496
497
498
499
500
501
502
/* See if an image is contained in a data source */
int IMG_isXPM(SDL_RWops *src)
{
return(0);
}
Apr 28, 2001
Apr 28, 2001
503
Nov 29, 2000
Nov 29, 2000
504
505
506
507
508
509
/* Load a XPM type image from an SDL datasource */
SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src)
{
return(NULL);
}
Apr 28, 2001
Apr 28, 2001
510
511
512
513
514
SDL_Surface *IMG_ReadXPMFromArray(char **xpm)
{
return NULL;
}
#endif /* not LOAD_XPM */