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