IMG_xpm.c
author Sam Lantinga <slouken@lokigames.com>
Wed, 29 Nov 2000 11:55:32 +0000
changeset 7 e1b6443ffb6b
child 9 b2f0eaf93201
permissions -rw-r--r--
1.1.0:
Sam Lantinga - Wed Nov 29 00:46:27 PST 2000
* Added XPM file format support
Supports color, greyscale, and mono XPMs with and without transparency
Mattias Engdeg�rd - Thu, 2 Nov 2000 23:23:17 +0100 (MET)
* Fixed array overrun when loading an unsupported format
* Minor compilation fixes for various platforms
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@7
    27
#include <stdio.h>
slouken@7
    28
#include <string.h>
slouken@7
    29
#include <ctype.h>
slouken@7
    30
slouken@7
    31
#include "SDL_image.h"
slouken@7
    32
slouken@7
    33
#ifdef LOAD_XPM
slouken@7
    34
slouken@7
    35
/* See if an image is contained in a data source */
slouken@7
    36
int IMG_isXPM(SDL_RWops *src)
slouken@7
    37
{
slouken@7
    38
	int is_XPM;
slouken@7
    39
	char magic[10];
slouken@7
    40
slouken@7
    41
	is_XPM = 0;
slouken@7
    42
	if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
slouken@7
    43
		if ( strncmp(magic, "/* XPM */", 9) == 0 ) {
slouken@7
    44
			is_XPM = 1;
slouken@7
    45
		}
slouken@7
    46
	}
slouken@7
    47
	return(is_XPM);
slouken@7
    48
}
slouken@7
    49
slouken@7
    50
static char *SDL_RWgets(char *string, int maxlen, SDL_RWops *src)
slouken@7
    51
{
slouken@7
    52
	int i;
slouken@7
    53
slouken@7
    54
	for ( i=0; i<(maxlen-1); ++i ) {
slouken@7
    55
		if ( SDL_RWread(src, &string[i], 1, 1) <= 0 ) {
slouken@7
    56
			/* EOF or error */
slouken@7
    57
			if ( i == 0 ) {
slouken@7
    58
				/* Hmm, EOF on initial read, return NULL */
slouken@7
    59
				string = NULL;
slouken@7
    60
			}
slouken@7
    61
			break;
slouken@7
    62
		}
slouken@7
    63
		/* In this case it's okay to use either '\r' or '\n'
slouken@7
    64
		   as line separators because blank lines are just
slouken@7
    65
		   ignored by the XPM format.
slouken@7
    66
		*/
slouken@7
    67
		if ( (string[i] == '\n') || (string[i] == '\n') ) {
slouken@7
    68
			break;
slouken@7
    69
		}
slouken@7
    70
	}
slouken@7
    71
	if ( string ) {
slouken@7
    72
		string[i] = '\0';
slouken@7
    73
	}
slouken@7
    74
	return(string);
slouken@7
    75
}
slouken@7
    76
slouken@7
    77
/* Hash table to look up colors from pixel strings */
slouken@7
    78
#define HASH_SIZE	256
slouken@7
    79
struct color_hash {
slouken@7
    80
	struct hash_entry {
slouken@7
    81
		int keylen;
slouken@7
    82
		char *key;
slouken@7
    83
		Uint32 color;
slouken@7
    84
		struct hash_entry *next;
slouken@7
    85
	} *entries[HASH_SIZE];
slouken@7
    86
};
slouken@7
    87
slouken@7
    88
static int hash_key(const char *key, int cpp)
slouken@7
    89
{
slouken@7
    90
	int hash;
slouken@7
    91
slouken@7
    92
	hash = 0;
slouken@7
    93
	while ( cpp-- > 0 ) {
slouken@7
    94
		hash += *key++;
slouken@7
    95
	}
slouken@7
    96
	return(hash%HASH_SIZE);
slouken@7
    97
}
slouken@7
    98
slouken@7
    99
static struct color_hash *create_colorhash(void)
slouken@7
   100
{
slouken@7
   101
	struct color_hash *hash;
slouken@7
   102
slouken@7
   103
	hash = (struct color_hash *)malloc(sizeof *hash);
slouken@7
   104
	if ( hash ) {
slouken@7
   105
		memset(hash, 0, (sizeof *hash));
slouken@7
   106
	}
slouken@7
   107
	return(hash);
slouken@7
   108
}
slouken@7
   109
slouken@7
   110
static int add_colorhash(struct color_hash *hash,
slouken@7
   111
                         const char *key, int cpp, Uint32 color)
slouken@7
   112
{
slouken@7
   113
	int hash_index;
slouken@7
   114
	struct hash_entry *prev, *entry;
slouken@7
   115
slouken@7
   116
	/* Create the hash entry */
slouken@7
   117
	entry = (struct hash_entry *)malloc(sizeof *entry);
slouken@7
   118
	if ( ! entry ) {
slouken@7
   119
		return(0);
slouken@7
   120
	}
slouken@7
   121
	entry->keylen = cpp;
slouken@7
   122
	entry->key = strdup(key);
slouken@7
   123
	if ( ! entry->key ) {
slouken@7
   124
		free(entry);
slouken@7
   125
		return(0);
slouken@7
   126
	}
slouken@7
   127
	entry->color = color;
slouken@7
   128
	entry->next = NULL;
slouken@7
   129
slouken@7
   130
	/* Add it to the hash table */
slouken@7
   131
	hash_index = hash_key(key, cpp);
slouken@7
   132
	for ( prev = hash->entries[hash_index];
slouken@7
   133
	      prev && prev->next; prev = prev->next ) {
slouken@7
   134
		/* Go to the end of the list */ ;
slouken@7
   135
	}
slouken@7
   136
	if ( prev ) {
slouken@7
   137
		prev->next = entry;
slouken@7
   138
	} else {
slouken@7
   139
		hash->entries[hash_index] = entry;
slouken@7
   140
	}
slouken@7
   141
	return(1);
slouken@7
   142
}
slouken@7
   143
slouken@7
   144
static int get_colorhash(struct color_hash *hash,
slouken@7
   145
                         const char *key, int cpp, Uint32 *color)
slouken@7
   146
{
slouken@7
   147
	int hash_index;
slouken@7
   148
	struct hash_entry *entry;
slouken@7
   149
slouken@7
   150
	hash_index = hash_key(key, cpp);
slouken@7
   151
	for ( entry = hash->entries[hash_index]; entry; entry = entry->next ) {
slouken@7
   152
		if ( strncmp(key, entry->key, entry->keylen) == 0 ) {
slouken@7
   153
			*color = entry->color;
slouken@7
   154
			return(1);
slouken@7
   155
		}
slouken@7
   156
	}
slouken@7
   157
	return(0);
slouken@7
   158
}
slouken@7
   159
slouken@7
   160
static void free_colorhash(struct color_hash *hash)
slouken@7
   161
{
slouken@7
   162
	int i;
slouken@7
   163
	struct hash_entry *entry, *freeable;
slouken@7
   164
slouken@7
   165
	for ( i=0; i<HASH_SIZE; ++i ) {
slouken@7
   166
		entry = hash->entries[i];
slouken@7
   167
		while ( entry ) {
slouken@7
   168
			freeable = entry;
slouken@7
   169
			entry = entry->next;
slouken@7
   170
			free(freeable->key);
slouken@7
   171
			free(freeable);
slouken@7
   172
		}
slouken@7
   173
	}
slouken@7
   174
	free(hash);
slouken@7
   175
}
slouken@7
   176
slouken@7
   177
static int color_to_rgb(const char *colorspec, int *r, int *g, int *b)
slouken@7
   178
{
slouken@7
   179
	char rbuf[3];
slouken@7
   180
	char gbuf[3];
slouken@7
   181
	char bbuf[3];
slouken@7
   182
slouken@7
   183
	/* Handle monochrome black and white */
slouken@7
   184
	if ( strcasecmp(colorspec, "black") == 0 ) {
slouken@7
   185
		*r = 0;
slouken@7
   186
		*g = 0;
slouken@7
   187
		*b = 0;
slouken@7
   188
		return(1);
slouken@7
   189
	}
slouken@7
   190
	if ( strcasecmp(colorspec, "white") == 0 ) {
slouken@7
   191
		*r = 255;
slouken@7
   192
		*g = 255;
slouken@7
   193
		*b = 255;
slouken@7
   194
		return(1);
slouken@7
   195
	}
slouken@7
   196
slouken@7
   197
	/* Normal hexidecimal color */
slouken@7
   198
	switch (strlen(colorspec)) {
slouken@7
   199
		case 3:
slouken@7
   200
			rbuf[0] = colorspec[0];
slouken@7
   201
			rbuf[1] = colorspec[0];
slouken@7
   202
			gbuf[0] = colorspec[1];
slouken@7
   203
			gbuf[1] = colorspec[1];
slouken@7
   204
			bbuf[0] = colorspec[2];
slouken@7
   205
			bbuf[1] = colorspec[2];
slouken@7
   206
			break;
slouken@7
   207
		case 6:
slouken@7
   208
			rbuf[0] = colorspec[0];
slouken@7
   209
			rbuf[1] = colorspec[1];
slouken@7
   210
			gbuf[0] = colorspec[2];
slouken@7
   211
			gbuf[1] = colorspec[3];
slouken@7
   212
			bbuf[0] = colorspec[4];
slouken@7
   213
			bbuf[1] = colorspec[5];
slouken@7
   214
			break;
slouken@7
   215
		case 12:
slouken@7
   216
			rbuf[0] = colorspec[0];
slouken@7
   217
			rbuf[1] = colorspec[1];
slouken@7
   218
			gbuf[0] = colorspec[4];
slouken@7
   219
			gbuf[1] = colorspec[5];
slouken@7
   220
			bbuf[0] = colorspec[8];
slouken@7
   221
			bbuf[1] = colorspec[9];
slouken@7
   222
			break;
slouken@7
   223
		default:
slouken@7
   224
			return(0);
slouken@7
   225
	}
slouken@7
   226
	rbuf[2] = '\0';
slouken@7
   227
	*r = (int)strtol(rbuf, NULL, 16);
slouken@7
   228
	gbuf[2] = '\0';
slouken@7
   229
	*g = (int)strtol(gbuf, NULL, 16);
slouken@7
   230
	bbuf[2] = '\0';
slouken@7
   231
	*b = (int)strtol(bbuf, NULL, 16);
slouken@7
   232
	return(1);
slouken@7
   233
}
slouken@7
   234
slouken@7
   235
/* Load a XPM type image from an SDL datasource */
slouken@7
   236
SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src)
slouken@7
   237
{
slouken@7
   238
	SDL_Surface *image;
slouken@7
   239
	char line[1024];
slouken@7
   240
	char *here, *stop;
slouken@7
   241
	int index;
slouken@7
   242
	int i, x, y;
slouken@7
   243
	int w, h, ncolors, cpp;
slouken@7
   244
	int found;
slouken@7
   245
	int r, g, b;
slouken@7
   246
	Uint32 colorkey;
slouken@7
   247
	char *colorkey_string;
slouken@7
   248
	int pixels_len;
slouken@7
   249
	char *pixels;
slouken@7
   250
	int indexed;
slouken@7
   251
	Uint8 *dst;
slouken@7
   252
	Uint32 pixel;
slouken@7
   253
	struct color_hash *colors;
slouken@7
   254
slouken@7
   255
	/* Skip to the first string, which describes the image */
slouken@7
   256
	image = NULL;
slouken@7
   257
	do {
slouken@7
   258
		here = SDL_RWgets(line, sizeof(line), src);
slouken@7
   259
		if ( ! here ) {
slouken@7
   260
			IMG_SetError("Premature end of data");
slouken@7
   261
			return(NULL);
slouken@7
   262
		}
slouken@7
   263
		if ( *here == '"' ) {
slouken@7
   264
			++here;
slouken@7
   265
			/* Skip to width */
slouken@7
   266
			while ( isspace(*here) ) ++here;
slouken@7
   267
			w = atoi(here);
slouken@7
   268
			while ( ! isspace(*here) ) ++here;
slouken@7
   269
			/* Skip to height */
slouken@7
   270
			while ( isspace(*here) ) ++here;
slouken@7
   271
			h = atoi(here);
slouken@7
   272
			while ( ! isspace(*here) ) ++here;
slouken@7
   273
			/* Skip to number of colors */
slouken@7
   274
			while ( isspace(*here) ) ++here;
slouken@7
   275
			ncolors = atoi(here);
slouken@7
   276
			while ( ! isspace(*here) ) ++here;
slouken@7
   277
			/* Skip to characters per pixel */
slouken@7
   278
			while ( isspace(*here) ) ++here;
slouken@7
   279
			cpp = atoi(here);
slouken@7
   280
			while ( ! isspace(*here) ) ++here;
slouken@7
   281
slouken@7
   282
			/* Verify the parameters */
slouken@7
   283
			if ( !w || !h || !ncolors || !cpp ) {
slouken@7
   284
				IMG_SetError("Invalid format description");
slouken@7
   285
				return(NULL);
slouken@7
   286
			}
slouken@7
   287
			pixels_len = 1+w*cpp+1+1;
slouken@7
   288
			pixels = (char *)malloc(pixels_len);
slouken@7
   289
			if ( ! pixels ) {
slouken@7
   290
				IMG_SetError("Out of memory");
slouken@7
   291
				return(NULL);
slouken@7
   292
			}
slouken@7
   293
slouken@7
   294
			/* Create the new surface */
slouken@7
   295
			if ( ncolors <= 256 ) {
slouken@7
   296
				indexed = 1;
slouken@7
   297
				image = SDL_CreateRGBSurface(SDL_SWSURFACE,
slouken@7
   298
							w, h, 8, 0, 0, 0, 0);
slouken@7
   299
			} else {
slouken@7
   300
				int rmask, gmask, bmask;
slouken@7
   301
				indexed = 0;
slouken@7
   302
				if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) {
slouken@7
   303
					rmask = 0x000000ff;
slouken@7
   304
					gmask = 0x0000ff00;
slouken@7
   305
					bmask = 0x00ff0000;
slouken@7
   306
				} else {
slouken@7
   307
					rmask = 0x00ff0000;
slouken@7
   308
					gmask = 0x0000ff00;
slouken@7
   309
					bmask = 0x000000ff;
slouken@7
   310
				}
slouken@7
   311
				image = SDL_CreateRGBSurface(SDL_SWSURFACE,
slouken@7
   312
							w, h, 32,
slouken@7
   313
							rmask, gmask, bmask, 0);
slouken@7
   314
			}
slouken@7
   315
			if ( ! image ) {
slouken@7
   316
				/* Hmm, some SDL error (out of memory?) */
slouken@7
   317
				free(pixels);
slouken@7
   318
				return(NULL);
slouken@7
   319
			}
slouken@7
   320
		}
slouken@7
   321
	} while ( ! image );
slouken@7
   322
slouken@7
   323
	/* Read the colors */
slouken@7
   324
	colors = create_colorhash();
slouken@7
   325
	if ( ! colors ) {
slouken@7
   326
		SDL_FreeSurface(image);
slouken@7
   327
		free(pixels);
slouken@7
   328
		IMG_SetError("Out of memory");
slouken@7
   329
		return(NULL);
slouken@7
   330
	}
slouken@7
   331
	colorkey_string = NULL;
slouken@7
   332
	for ( index=0; index < ncolors; ++index ) {
slouken@7
   333
		here = SDL_RWgets(line, sizeof(line), src);
slouken@7
   334
		if ( ! here ) {
slouken@7
   335
			SDL_FreeSurface(image);
slouken@7
   336
			image = NULL;
slouken@7
   337
			IMG_SetError("Premature end of data");
slouken@7
   338
			goto done;
slouken@7
   339
		}
slouken@7
   340
		if ( *here == '"' ) {
slouken@7
   341
			const char *key;
slouken@7
   342
			++here;
slouken@7
   343
			/* Grab the pixel key */
slouken@7
   344
			key = here;
slouken@7
   345
			for ( i=0; i<cpp; ++i ) {
slouken@7
   346
				if ( ! *here++ ) {
slouken@7
   347
					/* Parse error */
slouken@7
   348
					continue;
slouken@7
   349
				}
slouken@7
   350
			}
slouken@7
   351
			if ( *here ) {
slouken@7
   352
				*here++ = '\0';
slouken@7
   353
			}
slouken@7
   354
			/* Find the color identifier */
slouken@7
   355
			found = 0;
slouken@7
   356
			while ( *here && ! found ) {
slouken@7
   357
				while ( isspace(*here) ) ++here;
slouken@7
   358
				if ( (*here != 'c') &&
slouken@7
   359
				     (*here != 'g') &&
slouken@7
   360
				     (*here != 'm') ) {
slouken@7
   361
					/* Skip color type */
slouken@7
   362
					while ( *here && !isspace(*here) )
slouken@7
   363
						++here;
slouken@7
   364
					/* Skip color name */
slouken@7
   365
					while ( isspace(*here) ) ++here;
slouken@7
   366
					while ( *here && !isspace(*here) )
slouken@7
   367
						++here;
slouken@7
   368
					continue;
slouken@7
   369
				}
slouken@7
   370
				++here;
slouken@7
   371
				while ( isspace(*here) ) ++here;
slouken@7
   372
				if ( strncasecmp(here, "None", 4) == 0 ) {
slouken@7
   373
					colorkey_string = strdup(key);
slouken@7
   374
					if ( indexed ) {
slouken@7
   375
						colorkey = (Uint32)index;
slouken@7
   376
					} else {
slouken@7
   377
						colorkey = 0xFFFFFFFF;
slouken@7
   378
					}
slouken@7
   379
					found = 1;
slouken@7
   380
					continue;
slouken@7
   381
				}
slouken@7
   382
				if ( *here == '#' ) {
slouken@7
   383
					++here;
slouken@7
   384
				}
slouken@7
   385
				while ( isspace(*here) ) ++here;
slouken@7
   386
				for ( stop=here; isalnum(*stop); ++stop ) {
slouken@7
   387
					/* Skip the pixel color */;
slouken@7
   388
				}
slouken@7
   389
				*stop++ = '\0';
slouken@7
   390
				found = color_to_rgb(here, &r, &g, &b);
slouken@7
   391
				if ( found ) {
slouken@7
   392
					if ( indexed ) {
slouken@7
   393
						SDL_Color *color;
slouken@7
   394
						color = &image->format->palette->colors[index];
slouken@7
   395
						color->r = (Uint8)r;
slouken@7
   396
						color->g = (Uint8)g;
slouken@7
   397
						color->b = (Uint8)b;
slouken@7
   398
						pixel = index;
slouken@7
   399
					} else {
slouken@7
   400
						pixel = (r<<16)|(g<<8)|b;
slouken@7
   401
					}
slouken@7
   402
					add_colorhash(colors, key, cpp, pixel);
slouken@7
   403
				}
slouken@7
   404
				*here = '\0';
slouken@7
   405
			}
slouken@7
   406
			if ( ! found ) {
slouken@7
   407
				/* Hum, couldn't parse a color.. */;
slouken@7
   408
			}
slouken@7
   409
		}
slouken@7
   410
	}
slouken@7
   411
slouken@7
   412
	/* Read the pixels */
slouken@7
   413
	for ( y=0; y < h; ) {
slouken@7
   414
		here = SDL_RWgets(pixels, pixels_len, src);
slouken@7
   415
		if ( ! here ) {
slouken@7
   416
			SDL_FreeSurface(image);
slouken@7
   417
			image = NULL;
slouken@7
   418
			IMG_SetError("Premature end of data");
slouken@7
   419
			goto done;
slouken@7
   420
		}
slouken@7
   421
		if ( *here == '"' ) {
slouken@7
   422
			++here;
slouken@7
   423
			dst = (Uint8 *)image->pixels + y*image->pitch;
slouken@7
   424
			for ( x=0; x<w; ++x ) {
slouken@7
   425
				pixel = 0;
slouken@7
   426
				if ( colorkey_string &&
slouken@7
   427
				     (strncmp(here,colorkey_string,cpp)==0) ) {
slouken@7
   428
					pixel = colorkey;
slouken@7
   429
				} else {
slouken@7
   430
					get_colorhash(colors, here,cpp, &pixel);
slouken@7
   431
				}
slouken@7
   432
				if ( indexed ) {
slouken@7
   433
					*dst++ = pixel;
slouken@7
   434
				} else {
slouken@7
   435
					*((Uint32 *)dst)++ = pixel;
slouken@7
   436
				}
slouken@7
   437
				for ( i=0; *here && i<cpp; ++i ) {
slouken@7
   438
					++here;
slouken@7
   439
				}
slouken@7
   440
			}
slouken@7
   441
			++y;
slouken@7
   442
		}
slouken@7
   443
	}
slouken@7
   444
	if ( colorkey_string ) {
slouken@7
   445
        	SDL_SetColorKey(image, SDL_SRCCOLORKEY, colorkey);
slouken@7
   446
	}
slouken@7
   447
done:
slouken@7
   448
	free(pixels);
slouken@7
   449
	free_colorhash(colors);
slouken@7
   450
	if ( colorkey_string ) {
slouken@7
   451
		free(colorkey_string);
slouken@7
   452
	}
slouken@7
   453
	return(image);
slouken@7
   454
}
slouken@7
   455
slouken@7
   456
#else
slouken@7
   457
slouken@7
   458
/* See if an image is contained in a data source */
slouken@7
   459
int IMG_isXPM(SDL_RWops *src)
slouken@7
   460
{
slouken@7
   461
	return(0);
slouken@7
   462
}
slouken@7
   463
slouken@7
   464
/* Load a XPM type image from an SDL datasource */
slouken@7
   465
SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src)
slouken@7
   466
{
slouken@7
   467
	return(NULL);
slouken@7
   468
}
slouken@7
   469
slouken@7
   470
#endif /* LOAD_XPM */