IMG_xpm.c
author Sam Lantinga <slouken@lokigames.com>
Fri, 02 Mar 2001 22:48:41 +0000
changeset 22 5f9c7cfd0b79
parent 11 b3d1b573a542
child 35 4b1f174486fa
permissions -rw-r--r--
Mattias Engdeg�rd - Fri Mar 2 14:48:09 PST 2001
* Cleaned up some compiler warnings
slouken@7
     1
/*
slouken@7
     2
    IMGLIB:  An example image loading library for use with SDL
slouken@7
     3
    Copyright (C) 1999  Sam Lantinga
slouken@7
     4
slouken@7
     5
    This library is free software; you can redistribute it and/or
slouken@7
     6
    modify it under the terms of the GNU Library General Public
slouken@7
     7
    License as published by the Free Software Foundation; either
slouken@7
     8
    version 2 of the License, or (at your option) any later version.
slouken@7
     9
slouken@7
    10
    This library is distributed in the hope that it will be useful,
slouken@7
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@7
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@7
    13
    Library General Public License for more details.
slouken@7
    14
slouken@7
    15
    You should have received a copy of the GNU Library General Public
slouken@7
    16
    License along with this library; if not, write to the Free
slouken@7
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@7
    18
slouken@7
    19
    Sam Lantinga
slouken@7
    20
    5635-34 Springhouse Dr.
slouken@7
    21
    Pleasanton, CA 94588 (USA)
slouken@7
    22
    slouken@devolution.com
slouken@7
    23
*/
slouken@7
    24
slouken@7
    25
/* This is an XPM image file loading framework */
slouken@7
    26
slouken@9
    27
#include <stdlib.h>
slouken@7
    28
#include <stdio.h>
slouken@7
    29
#include <string.h>
slouken@7
    30
#include <ctype.h>
slouken@7
    31
slouken@7
    32
#include "SDL_image.h"
slouken@7
    33
slouken@7
    34
#ifdef LOAD_XPM
slouken@7
    35
slouken@7
    36
/* See if an image is contained in a data source */
slouken@7
    37
int IMG_isXPM(SDL_RWops *src)
slouken@7
    38
{
slouken@7
    39
	int is_XPM;
slouken@7
    40
	char magic[10];
slouken@7
    41
slouken@7
    42
	is_XPM = 0;
slouken@7
    43
	if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
slouken@11
    44
		if(memcmp(magic, "/* XPM */", 9) == 0) {
slouken@7
    45
			is_XPM = 1;
slouken@7
    46
		}
slouken@7
    47
	}
slouken@7
    48
	return(is_XPM);
slouken@7
    49
}
slouken@7
    50
slouken@7
    51
static char *SDL_RWgets(char *string, int maxlen, SDL_RWops *src)
slouken@7
    52
{
slouken@7
    53
	int i;
slouken@7
    54
slouken@7
    55
	for ( i=0; i<(maxlen-1); ++i ) {
slouken@7
    56
		if ( SDL_RWread(src, &string[i], 1, 1) <= 0 ) {
slouken@7
    57
			/* EOF or error */
slouken@7
    58
			if ( i == 0 ) {
slouken@7
    59
				/* Hmm, EOF on initial read, return NULL */
slouken@11
    60
				return NULL;
slouken@7
    61
			}
slouken@7
    62
			break;
slouken@7
    63
		}
slouken@7
    64
		/* In this case it's okay to use either '\r' or '\n'
slouken@7
    65
		   as line separators because blank lines are just
slouken@7
    66
		   ignored by the XPM format.
slouken@7
    67
		*/
slouken@11
    68
		if ( (string[i] == '\n') || (string[i] == '\r') ) {
slouken@7
    69
			break;
slouken@7
    70
		}
slouken@7
    71
	}
slouken@11
    72
	string[i] = '\0';
slouken@7
    73
	return(string);
slouken@7
    74
}
slouken@7
    75
slouken@7
    76
/* Hash table to look up colors from pixel strings */
slouken@11
    77
#define STARTING_HASH_SIZE 256
slouken@11
    78
slouken@11
    79
struct hash_entry {
slouken@11
    80
	char *key;
slouken@11
    81
	Uint32 color;
slouken@11
    82
	struct hash_entry *next;
slouken@7
    83
};
slouken@7
    84
slouken@11
    85
struct color_hash {
slouken@11
    86
	struct hash_entry **table;
slouken@11
    87
	struct hash_entry *entries; /* array of all entries */
slouken@11
    88
	struct hash_entry *next_free;
slouken@11
    89
	int size;
slouken@11
    90
	int maxnum;
slouken@11
    91
};
slouken@11
    92
slouken@11
    93
static int hash_key(const char *key, int cpp, int size)
slouken@7
    94
{
slouken@7
    95
	int hash;
slouken@7
    96
slouken@7
    97
	hash = 0;
slouken@7
    98
	while ( cpp-- > 0 ) {
slouken@11
    99
		hash = hash * 33 + *key++;
slouken@7
   100
	}
slouken@11
   101
	return hash & (size - 1);
slouken@7
   102
}
slouken@7
   103
slouken@11
   104
static struct color_hash *create_colorhash(int maxnum)
slouken@7
   105
{
slouken@11
   106
	int bytes, s;
slouken@7
   107
	struct color_hash *hash;
slouken@7
   108
slouken@11
   109
	/* we know how many entries we need, so we can allocate
slouken@11
   110
	   everything here */
slouken@11
   111
	hash = malloc(sizeof *hash);
slouken@11
   112
	if(!hash)
slouken@11
   113
		return NULL;
slouken@11
   114
slouken@11
   115
	/* use power-of-2 sized hash table for decoding speed */
slouken@11
   116
	for(s = STARTING_HASH_SIZE; s < maxnum; s <<= 1)
slouken@11
   117
		;
slouken@11
   118
	hash->size = s;
slouken@11
   119
	hash->maxnum = maxnum;
slouken@11
   120
	bytes = hash->size * sizeof(struct hash_entry **);
slouken@11
   121
	hash->entries = NULL;	/* in case malloc fails */
slouken@11
   122
	hash->table = malloc(bytes);
slouken@11
   123
	if(!hash->table)
slouken@11
   124
		return NULL;
slouken@11
   125
	memset(hash->table, 0, bytes);
slouken@11
   126
	hash->entries = malloc(maxnum * sizeof(struct hash_entry));
slouken@11
   127
	if(!hash->entries)
slouken@11
   128
		return NULL;
slouken@11
   129
	hash->next_free = hash->entries;
slouken@11
   130
	return hash;
slouken@7
   131
}
slouken@7
   132
slouken@7
   133
static int add_colorhash(struct color_hash *hash,
slouken@11
   134
                         char *key, int cpp, Uint32 color)
slouken@7
   135
{
slouken@11
   136
	int index = hash_key(key, cpp, hash->size);
slouken@11
   137
	struct hash_entry *e = hash->next_free++;
slouken@11
   138
	e->color = color;
slouken@11
   139
	e->key = key;
slouken@11
   140
	e->next = hash->table[index];
slouken@11
   141
	hash->table[index] = e;
slouken@11
   142
	return 1;
slouken@7
   143
}
slouken@7
   144
slouken@11
   145
/* fast lookup that works if cpp == 1 */
slouken@22
   146
#define QUICK_COLORHASH(hash, key) ((hash)->table[*(Uint8 *)(key)]->color)
slouken@11
   147
slouken@11
   148
static Uint32 get_colorhash(struct color_hash *hash, const char *key, int cpp)
slouken@7
   149
{
slouken@11
   150
	struct hash_entry *entry = hash->table[hash_key(key, cpp, hash->size)];
slouken@11
   151
	while(entry) {
slouken@11
   152
		if(memcmp(key, entry->key, cpp) == 0)
slouken@11
   153
			return entry->color;
slouken@11
   154
		entry = entry->next;
slouken@7
   155
	}
slouken@11
   156
	return 0;		/* garbage in - garbage out */
slouken@7
   157
}
slouken@7
   158
slouken@7
   159
static void free_colorhash(struct color_hash *hash)
slouken@7
   160
{
slouken@11
   161
	if(hash && hash->table) {
slouken@11
   162
		free(hash->table);
slouken@11
   163
		free(hash->entries);
slouken@11
   164
		free(hash);
slouken@7
   165
	}
slouken@7
   166
}
slouken@7
   167
slouken@11
   168
#define ARRAYSIZE(a) (int)(sizeof(a) / sizeof((a)[0]))
slouken@11
   169
slouken@11
   170
/*
slouken@11
   171
 * convert colour spec to RGB (in 0xrrggbb format).
slouken@11
   172
 * return 1 if successful. may scribble on the colorspec buffer.
slouken@11
   173
 */
slouken@11
   174
static int color_to_rgb(char *spec, Uint32 *rgb)
slouken@7
   175
{
slouken@11
   176
	/* poor man's rgb.txt */
slouken@11
   177
	static struct { char *name; Uint32 rgb; } known[] = {
slouken@11
   178
		{"none",  0xffffffff},
slouken@11
   179
		{"black", 0x00000000},
slouken@11
   180
		{"white", 0x00ffffff},
slouken@11
   181
		{"red",   0x00ff0000},
slouken@11
   182
		{"green", 0x0000ff00},
slouken@11
   183
		{"blue",  0x000000ff}
slouken@11
   184
	};
slouken@7
   185
slouken@11
   186
	if(spec[0] == '#') {
slouken@11
   187
		char buf[7];
slouken@11
   188
		++spec;
slouken@11
   189
		switch(strlen(spec)) {
slouken@7
   190
		case 3:
slouken@11
   191
			buf[0] = buf[1] = spec[0];
slouken@11
   192
			buf[2] = buf[3] = spec[1];
slouken@11
   193
			buf[4] = buf[5] = spec[2];
slouken@7
   194
			break;
slouken@7
   195
		case 6:
slouken@11
   196
			memcpy(buf, spec, 6);
slouken@7
   197
			break;
slouken@7
   198
		case 12:
slouken@11
   199
			buf[0] = spec[0];
slouken@11
   200
			buf[1] = spec[1];
slouken@11
   201
			buf[2] = spec[4];
slouken@11
   202
			buf[3] = spec[5];
slouken@11
   203
			buf[4] = spec[8];
slouken@11
   204
			buf[5] = spec[9];
slouken@7
   205
			break;
slouken@11
   206
		}
slouken@11
   207
		buf[6] = '\0';
slouken@11
   208
		*rgb = strtol(buf, NULL, 16);
slouken@11
   209
		return 1;
slouken@11
   210
	} else {
slouken@11
   211
		int i;
slouken@11
   212
		for(i = 0; i < ARRAYSIZE(known); i++)
slouken@11
   213
			if(IMG_string_equals(known[i].name, spec)) {
slouken@11
   214
				*rgb = known[i].rgb;
slouken@11
   215
				return 1;
slouken@11
   216
			}
slouken@11
   217
		return 0;
slouken@7
   218
	}
slouken@7
   219
}
slouken@7
   220
slouken@11
   221
static char *skipspace(char *p)
slouken@11
   222
{
slouken@11
   223
	while(isspace((unsigned char)*p))
slouken@11
   224
	      ++p;
slouken@11
   225
	return p;
slouken@11
   226
}
slouken@11
   227
slouken@11
   228
static char *skipnonspace(char *p)
slouken@11
   229
{
slouken@11
   230
	while(!isspace((unsigned char)*p) && *p)
slouken@11
   231
		++p;
slouken@11
   232
	return p;
slouken@11
   233
}
slouken@11
   234
slouken@11
   235
#ifndef MAX
slouken@11
   236
#define MAX(a, b) ((a) > (b) ? (a) : (b))
slouken@11
   237
#endif
slouken@11
   238
slouken@7
   239
/* Load a XPM type image from an SDL datasource */
slouken@7
   240
SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src)
slouken@7
   241
{
slouken@7
   242
	SDL_Surface *image;
slouken@7
   243
	char line[1024];
slouken@11
   244
	char *here;
slouken@7
   245
	int index;
slouken@11
   246
	int x, y;
slouken@7
   247
	int w, h, ncolors, cpp;
slouken@7
   248
	int pixels_len;
slouken@11
   249
	char *pixels = NULL;
slouken@7
   250
	int indexed;
slouken@7
   251
	Uint8 *dst;
slouken@7
   252
	struct color_hash *colors;
slouken@11
   253
	SDL_Color *im_colors = NULL;
slouken@11
   254
	char *keystrings, *nextkey;
slouken@11
   255
	char *error = NULL;
slouken@7
   256
slouken@7
   257
	/* Skip to the first string, which describes the image */
slouken@7
   258
	do {
slouken@11
   259
	        here = SDL_RWgets(line, sizeof(line), src);
slouken@11
   260
		if ( !here ) {
slouken@7
   261
			IMG_SetError("Premature end of data");
slouken@7
   262
			return(NULL);
slouken@7
   263
		}
slouken@11
   264
		here = skipspace(here);
slouken@11
   265
	} while(*here != '"');
slouken@11
   266
	/*
slouken@11
   267
	 * The header string of an XPMv3 image has the format
slouken@11
   268
	 *
slouken@11
   269
	 * <width> <height> <ncolors> <cpp> [ <hotspot_x> <hotspot_y> ]
slouken@11
   270
	 *
slouken@11
   271
	 * where the hotspot coords are intended for mouse cursors.
slouken@11
   272
	 * Right now we don't use the hotspots but it should be handled
slouken@11
   273
	 * one day.
slouken@11
   274
	 */
slouken@11
   275
	if(sscanf(here + 1, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4
slouken@11
   276
	   || w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) {
slouken@11
   277
		IMG_SetError("Invalid format description");
slouken@11
   278
		return(NULL);
slouken@11
   279
	}
slouken@7
   280
slouken@11
   281
	keystrings = malloc(ncolors * cpp);
slouken@11
   282
	if(!keystrings) {
slouken@11
   283
		IMG_SetError("Out of memory");
slouken@11
   284
		free(pixels);
slouken@11
   285
		return NULL;
slouken@11
   286
	}
slouken@11
   287
	nextkey = keystrings;
slouken@7
   288
slouken@11
   289
	/* Create the new surface */
slouken@11
   290
	if(ncolors <= 256) {
slouken@11
   291
		indexed = 1;
slouken@11
   292
		image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 8,
slouken@11
   293
					     0, 0, 0, 0);
slouken@11
   294
		im_colors = image->format->palette->colors;
slouken@11
   295
		image->format->palette->ncolors = ncolors;
slouken@11
   296
	} else {
slouken@11
   297
		indexed = 0;
slouken@11
   298
		image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32,
slouken@11
   299
					     0xff0000, 0x00ff00, 0x0000ff, 0);
slouken@11
   300
	}
slouken@11
   301
	if(!image) {
slouken@11
   302
		/* Hmm, some SDL error (out of memory?) */
slouken@11
   303
		free(pixels);
slouken@11
   304
		return(NULL);
slouken@11
   305
	}
slouken@7
   306
slouken@7
   307
	/* Read the colors */
slouken@11
   308
	colors = create_colorhash(ncolors);
slouken@7
   309
	if ( ! colors ) {
slouken@11
   310
		error = "Out of memory";
slouken@11
   311
		goto done;
slouken@7
   312
	}
slouken@11
   313
	for(index = 0; index < ncolors; ++index ) {
slouken@11
   314
		char *key;
slouken@11
   315
		int len;
slouken@11
   316
slouken@11
   317
		do {
slouken@11
   318
			here = SDL_RWgets(line, sizeof(line), src);
slouken@11
   319
			if(!here) {
slouken@11
   320
				error = "Premature end of data";
slouken@11
   321
				goto done;
slouken@7
   322
			}
slouken@11
   323
			here = skipspace(here);
slouken@11
   324
		} while(*here != '"');
slouken@11
   325
slouken@11
   326
		++here;
slouken@11
   327
		len = strlen(here);
slouken@11
   328
		if(len < cpp + 7)
slouken@11
   329
			continue;	/* cannot be a valid line */
slouken@11
   330
slouken@11
   331
		key = here;
slouken@11
   332
		key[cpp] = '\0';
slouken@11
   333
		here += cpp + 1;
slouken@11
   334
slouken@11
   335
		/* parse a colour definition */
slouken@11
   336
		for(;;) {
slouken@11
   337
			char nametype;
slouken@11
   338
			char *colname;
slouken@11
   339
			char delim;
slouken@11
   340
			Uint32 rgb;
slouken@11
   341
slouken@11
   342
			here = skipspace(here);
slouken@11
   343
			nametype = *here;
slouken@11
   344
			here = skipnonspace(here);
slouken@11
   345
			here = skipspace(here);
slouken@11
   346
			colname = here;
slouken@11
   347
			while(*here && !isspace((unsigned char)*here)
slouken@11
   348
			      && *here != '"')
slouken@11
   349
				here++;
slouken@11
   350
			if(!*here) {
slouken@11
   351
				error = "color parse error";
slouken@11
   352
				goto done;
slouken@7
   353
			}
slouken@11
   354
			if(nametype == 's')
slouken@11
   355
				continue;      /* skip symbolic colour names */
slouken@11
   356
slouken@11
   357
			delim = *here;
slouken@11
   358
			*here = '\0';
slouken@11
   359
			if(delim)
slouken@11
   360
			    here++;
slouken@11
   361
slouken@11
   362
			if(!color_to_rgb(colname, &rgb))
slouken@11
   363
				continue;
slouken@11
   364
slouken@11
   365
			memcpy(nextkey, key, cpp);
slouken@11
   366
			if(indexed) {
slouken@11
   367
				SDL_Color *c = im_colors + index;
slouken@11
   368
				c->r = rgb >> 16;
slouken@11
   369
				c->g = rgb >> 8;
slouken@11
   370
				c->b = rgb;
slouken@11
   371
				add_colorhash(colors, nextkey, cpp, index);
slouken@11
   372
			} else
slouken@11
   373
				add_colorhash(colors, nextkey, cpp, rgb);
slouken@11
   374
			nextkey += cpp;
slouken@11
   375
			if(rgb == 0xffffffff)
slouken@11
   376
				SDL_SetColorKey(image, SDL_SRCCOLORKEY,
slouken@11
   377
						indexed ? index : rgb);
slouken@11
   378
			break;
slouken@7
   379
		}
slouken@7
   380
	}
slouken@7
   381
slouken@7
   382
	/* Read the pixels */
slouken@11
   383
	pixels_len = w * cpp;
slouken@11
   384
	pixels = malloc(MAX(pixels_len + 5, 20));
slouken@11
   385
	if(!pixels) {
slouken@11
   386
		error = "Out of memory";
slouken@11
   387
		goto done;
slouken@11
   388
	}
slouken@11
   389
	dst = image->pixels;
slouken@11
   390
	for (y = 0; y < h; ) {
slouken@22
   391
		char *s;
slouken@11
   392
		char c;
slouken@11
   393
		do {
slouken@11
   394
			if(SDL_RWread(src, &c, 1, 1) <= 0) {
slouken@11
   395
				error = "Premature end of data";
slouken@11
   396
				goto done;
slouken@11
   397
			}
slouken@11
   398
		} while(c == ' ');
slouken@11
   399
		if(c != '"') {
slouken@11
   400
			/* comment or empty line, skip it */
slouken@11
   401
			while(c != '\n' && c != '\r') {
slouken@11
   402
				if(SDL_RWread(src, &c, 1, 1) <= 0) {
slouken@11
   403
					error = "Premature end of data";
slouken@11
   404
					goto done;
slouken@11
   405
				}
slouken@11
   406
			}
slouken@11
   407
			continue;
slouken@11
   408
		}
slouken@11
   409
		if(SDL_RWread(src, pixels, pixels_len + 3, 1) <= 0) {
slouken@11
   410
			error = "Premature end of data";
slouken@7
   411
			goto done;
slouken@7
   412
		}
slouken@11
   413
		s = pixels;
slouken@11
   414
		if(indexed) {
slouken@11
   415
			/* optimization for some common cases */
slouken@11
   416
			if(cpp == 1)
slouken@11
   417
				for(x = 0; x < w; x++)
slouken@11
   418
					dst[x] = QUICK_COLORHASH(colors,
slouken@11
   419
								 s + x);
slouken@11
   420
			else
slouken@11
   421
				for(x = 0; x < w; x++)
slouken@11
   422
					dst[x] = get_colorhash(colors,
slouken@11
   423
							       s + x * cpp,
slouken@11
   424
							       cpp);
slouken@11
   425
		} else {
slouken@11
   426
			for (x = 0; x < w; x++)
slouken@11
   427
				((Uint32*)dst)[x] = get_colorhash(colors,
slouken@11
   428
								  s + x * cpp,
slouken@11
   429
								  cpp);
slouken@7
   430
		}
slouken@11
   431
		dst += image->pitch;
slouken@11
   432
		y++;
slouken@7
   433
	}
slouken@11
   434
slouken@11
   435
done:
slouken@11
   436
	if(error) {
slouken@11
   437
		if(image)
slouken@11
   438
			SDL_FreeSurface(image);
slouken@11
   439
		image = NULL;
slouken@11
   440
		IMG_SetError(error);
slouken@7
   441
	}
slouken@7
   442
	free(pixels);
slouken@11
   443
	free(keystrings);
slouken@7
   444
	free_colorhash(colors);
slouken@7
   445
	return(image);
slouken@7
   446
}
slouken@7
   447
slouken@7
   448
#else
slouken@7
   449
slouken@7
   450
/* See if an image is contained in a data source */
slouken@7
   451
int IMG_isXPM(SDL_RWops *src)
slouken@7
   452
{
slouken@7
   453
	return(0);
slouken@7
   454
}
slouken@7
   455
slouken@7
   456
/* Load a XPM type image from an SDL datasource */
slouken@7
   457
SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src)
slouken@7
   458
{
slouken@7
   459
	return(NULL);
slouken@7
   460
}
slouken@7
   461
slouken@7
   462
#endif /* LOAD_XPM */