123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- /*
- SDL_image: An example image loading library for use with SDL
- Copyright (C) 1999, 2000, 2001 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
- slouken@libsdl.org
- */
- /* $Id: IMG_xpm.c,v 1.1 2004/07/21 16:24:11 paigoddess Exp $ */
- /*
- * 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.
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- #include "SDL_image.h"
- /* See if an image is contained in a data source */
- int IMG_isXPM(SDL_RWops *src)
- {
- char magic[9];
- return (SDL_RWread(src, magic, sizeof(magic), 1)
- && memcmp(magic, "/* XPM */", 9) == 0);
- }
- /* 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 = (color_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 = (hash_entry**)malloc(bytes);
- if(!hash->table)
- return NULL;
- memset(hash->table, 0, bytes);
- hash->entries = (hash_entry*)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);
- }
- }
- /* 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;
- }
- #define ARRAYSIZE(a) (int)(sizeof(a) / sizeof((a)[0]))
- /*
- * convert colour spec to RGB (in 0xrrggbb format).
- * return 1 if successful.
- */
- static int color_to_rgb(char *spec, int speclen, 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];
- switch(speclen) {
- case 4:
- buf[0] = buf[1] = spec[1];
- buf[2] = buf[3] = spec[2];
- buf[4] = buf[5] = spec[3];
- break;
- case 7:
- memcpy(buf, spec + 1, 6);
- break;
- 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];
- break;
- }
- buf[6] = '\0';
- *rgb = strtol(buf, NULL, 16);
- return 1;
- } else {
- int i;
- for(i = 0; i < ARRAYSIZE(known); i++)
- if(string_equal(known[i].name, spec, speclen)) {
- *rgb = known[i].rgb;
- return 1;
- }
- return 0;
- }
- }
- #ifndef MAX
- #define MAX(a, b) ((a) > (b) ? (a) : (b))
- #endif
- 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.
- */
- static char *get_next_line(char ***lines, SDL_RWops *src, int len)
- {
- 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;
- linebuf = (char*)realloc(linebuf, buflen);
- if(!linebuf) {
- error = "Out of memory";
- return NULL;
- }
- }
- 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;
- linebuf = (char*)realloc(linebuf, buflen);
- if(!linebuf) {
- error = "Out of memory";
- return NULL;
- }
- }
- 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;
- }
- }
- #define SKIPSPACE(p) \
- do { \
- while(isspace((unsigned char)*(p))) \
- ++(p); \
- } while(0)
- #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)
- {
- SDL_Surface *image = NULL;
- int index;
- int x, y;
- int w, h, ncolors, cpp;
- int indexed;
- Uint8 *dst;
- struct color_hash *colors = NULL;
- SDL_Color *im_colors = NULL;
- char *keystrings = NULL, *nextkey;
- char *line;
- char ***xpmlines = NULL;
- int pixels_len;
- error = NULL;
- linebuf = NULL;
- buflen = 0;
- if(xpm)
- xpmlines = &xpm;
- line = get_next_line(xpmlines, src, 0);
- if(!line)
- goto done;
- /*
- * 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(line, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4
- || w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) {
- error = "Invalid format description";
- goto done;
- }
- keystrings = (char*)malloc(ncolors * cpp);
- if(!keystrings) {
- error = "Out of memory";
- goto done;
- }
- 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?) */
- goto done;
- }
- /* Read the colors */
- colors = create_colorhash(ncolors);
- if (!colors) {
- error = "Out of memory";
- goto done;
- }
- for(index = 0; index < ncolors; ++index ) {
- char *p;
- line = get_next_line(xpmlines, src, 0);
- if(!line)
- goto done;
- p = line + cpp + 1;
- /* parse a colour definition */
- for(;;) {
- char nametype;
- char *colname;
- Uint32 rgb, pixel;
- SKIPSPACE(p);
- if(!*p) {
- error = "colour parse error";
- goto done;
- }
- nametype = *p;
- SKIPNONSPACE(p);
- SKIPSPACE(p);
- colname = p;
- SKIPNONSPACE(p);
- if(nametype == 's')
- continue; /* skip symbolic colour names */
- if(!color_to_rgb(colname, p - colname, &rgb))
- continue;
- memcpy(nextkey, line, cpp);
- if(indexed) {
- SDL_Color *c = im_colors + index;
- c->r = rgb >> 16;
- c->g = rgb >> 8;
- c->b = rgb;
- pixel = index;
- } else
- pixel = rgb;
- add_colorhash(colors, nextkey, cpp, pixel);
- nextkey += cpp;
- if(rgb == 0xffffffff)
- SDL_SetColorKey(image, SDL_SRCCOLORKEY, pixel);
- break;
- }
- }
- /* Read the pixels */
- pixels_len = w * cpp;
- dst = (Uint8*)image->pixels;
- for(y = 0; y < h; y++) {
- line = get_next_line(xpmlines, src, pixels_len);
- if(indexed) {
- /* optimization for some common cases */
- if(cpp == 1)
- for(x = 0; x < w; x++)
- dst[x] = QUICK_COLORHASH(colors,
- line + x);
- else
- for(x = 0; x < w; x++)
- dst[x] = get_colorhash(colors,
- line + x * cpp,
- cpp);
- } else {
- for (x = 0; x < w; x++)
- ((Uint32*)dst)[x] = get_colorhash(colors,
- line + x * cpp,
- cpp);
- }
- dst += image->pitch;
- }
- done:
- if(error) {
- SDL_FreeSurface(image);
- image = NULL;
- IMG_SetError(error);
- }
- free(keystrings);
- free_colorhash(colors);
- free(linebuf);
- return(image);
- }
- /* Load a XPM type image from an RWops datasource */
- SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src)
- {
- return load_xpm(NULL, src);
- }
- SDL_Surface *IMG_ReadXPMFromArray(char **xpm)
- {
- return load_xpm(xpm, NULL);
- }
|