0,0 → 1,462 |
/* |
IMGLIB: An example image loading library for use with SDL |
Copyright (C) 1999 Sam Lantinga |
|
This library is free software; you can redistribute it and/or |
modify it under the terms of the GNU Library General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
|
This library is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Library General Public License for more details. |
|
You should have received a copy of the GNU Library General Public |
License along with this library; if not, write to the Free |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
Sam Lantinga |
5635-34 Springhouse Dr. |
Pleasanton, CA 94588 (USA) |
slouken@devolution.com |
*/ |
|
/* This is an XPM image file loading framework */ |
|
#include <stdlib.h> |
#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) |
{ |
int is_XPM; |
char magic[10]; |
|
is_XPM = 0; |
if ( SDL_RWread(src, magic, sizeof(magic), 1) ) { |
if(memcmp(magic, "/* XPM */", 9) == 0) { |
is_XPM = 1; |
} |
} |
return(is_XPM); |
} |
|
static char *SDL_RWgets(char *string, int maxlen, SDL_RWops *src) |
{ |
int i; |
|
for ( i=0; i<(maxlen-1); ++i ) { |
if ( SDL_RWread(src, &string[i], 1, 1) <= 0 ) { |
/* EOF or error */ |
if ( i == 0 ) { |
/* Hmm, EOF on initial read, return NULL */ |
return NULL; |
} |
break; |
} |
/* In this case it's okay to use either '\r' or '\n' |
as line separators because blank lines are just |
ignored by the XPM format. |
*/ |
if ( (string[i] == '\n') || (string[i] == '\r') ) { |
break; |
} |
} |
string[i] = '\0'; |
return(string); |
} |
|
/* Hash table to look up colors from pixel strings */ |
#define STARTING_HASH_SIZE 256 |
|
struct hash_entry { |
char *key; |
Uint32 color; |
struct hash_entry *next; |
}; |
|
struct color_hash { |
struct hash_entry **table; |
struct hash_entry *entries; /* array of all entries */ |
struct hash_entry *next_free; |
int size; |
int maxnum; |
}; |
|
static int hash_key(const char *key, int cpp, int size) |
{ |
int hash; |
|
hash = 0; |
while ( cpp-- > 0 ) { |
hash = hash * 33 + *key++; |
} |
return hash & (size - 1); |
} |
|
static struct color_hash *create_colorhash(int maxnum) |
{ |
int bytes, s; |
struct color_hash *hash; |
|
/* 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)); |
if(!hash->entries) |
return NULL; |
hash->next_free = hash->entries; |
return hash; |
} |
|
static int add_colorhash(struct color_hash *hash, |
char *key, int cpp, Uint32 color) |
{ |
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; |
} |
|
/* fast lookup that works if cpp == 1 */ |
#define QUICK_COLORHASH(hash, key) ((hash)->table[*(Uint8 *)(key)]->color) |
|
static Uint32 get_colorhash(struct color_hash *hash, const char *key, int cpp) |
{ |
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; |
} |
return 0; /* garbage in - garbage out */ |
} |
|
static void free_colorhash(struct color_hash *hash) |
{ |
if(hash && hash->table) { |
free(hash->table); |
free(hash->entries); |
free(hash); |
} |
} |
|
#define ARRAYSIZE(a) (int)(sizeof(a) / sizeof((a)[0])) |
|
/* |
* convert colour spec to RGB (in 0xrrggbb format). |
* return 1 if successful. may scribble on the colorspec buffer. |
*/ |
static int color_to_rgb(char *spec, Uint32 *rgb) |
{ |
/* 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]; |
++spec; |
switch(strlen(spec)) { |
case 3: |
buf[0] = buf[1] = spec[0]; |
buf[2] = buf[3] = spec[1]; |
buf[4] = buf[5] = spec[2]; |
break; |
case 6: |
memcpy(buf, spec, 6); |
break; |
case 12: |
buf[0] = spec[0]; |
buf[1] = spec[1]; |
buf[2] = spec[4]; |
buf[3] = spec[5]; |
buf[4] = spec[8]; |
buf[5] = spec[9]; |
break; |
} |
buf[6] = '\0'; |
*rgb = strtol(buf, NULL, 16); |
return 1; |
} else { |
int i; |
for(i = 0; i < ARRAYSIZE(known); i++) |
if(IMG_string_equals(known[i].name, spec)) { |
*rgb = known[i].rgb; |
return 1; |
} |
return 0; |
} |
} |
|
static char *skipspace(char *p) |
{ |
while(isspace((unsigned char)*p)) |
++p; |
return p; |
} |
|
static char *skipnonspace(char *p) |
{ |
while(!isspace((unsigned char)*p) && *p) |
++p; |
return p; |
} |
|
#ifndef MAX |
#define MAX(a, b) ((a) > (b) ? (a) : (b)) |
#endif |
|
/* Load a XPM type image from an SDL datasource */ |
SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src) |
{ |
SDL_Surface *image; |
char line[1024]; |
char *here; |
int index; |
int x, y; |
int w, h, ncolors, cpp; |
int pixels_len; |
char *pixels = NULL; |
int indexed; |
Uint8 *dst; |
struct color_hash *colors; |
SDL_Color *im_colors = NULL; |
char *keystrings, *nextkey; |
char *error = NULL; |
|
/* Skip to the first string, which describes the image */ |
do { |
here = SDL_RWgets(line, sizeof(line), src); |
if ( !here ) { |
IMG_SetError("Premature end of data"); |
return(NULL); |
} |
here = skipspace(here); |
} while(*here != '"'); |
/* |
* 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. |
*/ |
if(sscanf(here + 1, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4 |
|| w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) { |
IMG_SetError("Invalid format description"); |
return(NULL); |
} |
|
keystrings = malloc(ncolors * cpp); |
if(!keystrings) { |
IMG_SetError("Out of memory"); |
free(pixels); |
return NULL; |
} |
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?) */ |
free(pixels); |
return(NULL); |
} |
|
/* Read the colors */ |
colors = create_colorhash(ncolors); |
if ( ! colors ) { |
error = "Out of memory"; |
goto done; |
} |
for(index = 0; index < ncolors; ++index ) { |
char *key; |
int len; |
|
do { |
here = SDL_RWgets(line, sizeof(line), src); |
if(!here) { |
error = "Premature end of data"; |
goto done; |
} |
here = skipspace(here); |
} while(*here != '"'); |
|
++here; |
len = strlen(here); |
if(len < cpp + 7) |
continue; /* cannot be a valid line */ |
|
key = here; |
key[cpp] = '\0'; |
here += cpp + 1; |
|
/* parse a colour definition */ |
for(;;) { |
char nametype; |
char *colname; |
char delim; |
Uint32 rgb; |
|
here = skipspace(here); |
nametype = *here; |
here = skipnonspace(here); |
here = skipspace(here); |
colname = here; |
while(*here && !isspace((unsigned char)*here) |
&& *here != '"') |
here++; |
if(!*here) { |
error = "color parse error"; |
goto done; |
} |
if(nametype == 's') |
continue; /* skip symbolic colour names */ |
|
delim = *here; |
*here = '\0'; |
if(delim) |
here++; |
|
if(!color_to_rgb(colname, &rgb)) |
continue; |
|
memcpy(nextkey, key, cpp); |
if(indexed) { |
SDL_Color *c = im_colors + index; |
c->r = rgb >> 16; |
c->g = rgb >> 8; |
c->b = rgb; |
add_colorhash(colors, nextkey, cpp, index); |
} else |
add_colorhash(colors, nextkey, cpp, rgb); |
nextkey += cpp; |
if(rgb == 0xffffffff) |
SDL_SetColorKey(image, SDL_SRCCOLORKEY, |
indexed ? index : rgb); |
break; |
} |
} |
|
/* Read the pixels */ |
pixels_len = w * cpp; |
pixels = malloc(MAX(pixels_len + 5, 20)); |
if(!pixels) { |
error = "Out of memory"; |
goto done; |
} |
dst = image->pixels; |
for (y = 0; y < h; ) { |
char *s; |
char c; |
do { |
if(SDL_RWread(src, &c, 1, 1) <= 0) { |
error = "Premature end of data"; |
goto done; |
} |
} while(c == ' '); |
if(c != '"') { |
/* comment or empty line, skip it */ |
while(c != '\n' && c != '\r') { |
if(SDL_RWread(src, &c, 1, 1) <= 0) { |
error = "Premature end of data"; |
goto done; |
} |
} |
continue; |
} |
if(SDL_RWread(src, pixels, pixels_len + 3, 1) <= 0) { |
error = "Premature end of data"; |
goto done; |
} |
s = pixels; |
if(indexed) { |
/* optimization for some common cases */ |
if(cpp == 1) |
for(x = 0; x < w; x++) |
dst[x] = QUICK_COLORHASH(colors, |
s + x); |
else |
for(x = 0; x < w; x++) |
dst[x] = get_colorhash(colors, |
s + x * cpp, |
cpp); |
} else { |
for (x = 0; x < w; x++) |
((Uint32*)dst)[x] = get_colorhash(colors, |
s + x * cpp, |
cpp); |
} |
dst += image->pitch; |
y++; |
} |
|
done: |
if(error) { |
if(image) |
SDL_FreeSurface(image); |
image = NULL; |
IMG_SetError(error); |
} |
free(pixels); |
free(keystrings); |
free_colorhash(colors); |
return(image); |
} |
|
#else |
|
/* See if an image is contained in a data source */ |
int IMG_isXPM(SDL_RWops *src) |
{ |
return(0); |
} |
|
/* Load a XPM type image from an SDL datasource */ |
SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src) |
{ |
return(NULL); |
} |
|
#endif /* LOAD_XPM */ |