Skip to content

Latest commit

 

History

History
523 lines (470 loc) · 11.3 KB

IMG_xpm.c

File metadata and controls

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