summaryrefslogtreecommitdiffstats
path: root/apps/plugins/sdl/SDL_image/IMG_xpm.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/sdl/SDL_image/IMG_xpm.c')
-rw-r--r--apps/plugins/sdl/SDL_image/IMG_xpm.c514
1 files changed, 514 insertions, 0 deletions
diff --git a/apps/plugins/sdl/SDL_image/IMG_xpm.c b/apps/plugins/sdl/SDL_image/IMG_xpm.c
new file mode 100644
index 0000000000..486992f459
--- /dev/null
+++ b/apps/plugins/sdl/SDL_image/IMG_xpm.c
@@ -0,0 +1,514 @@
+/*
+ SDL_image: An example image loading library for use with SDL
+ Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+/*
+ * 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"
+
+#ifdef LOAD_XPM
+
+/* See if an image is contained in a data source */
+int IMG_isXPM(SDL_RWops *src)
+{
+ int start;
+ int is_XPM;
+ char magic[9];
+
+ if ( !src )
+ return 0;
+ start = SDL_RWtell(src);
+ is_XPM = 0;
+ if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
+ if ( memcmp(magic, "/* XPM */", sizeof(magic)) == 0 ) {
+ is_XPM = 1;
+ }
+ }
+ SDL_RWseek(src, start, RW_SEEK_SET);
+ return(is_XPM);
+}
+
+/* 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) {
+ free(hash->table);
+ 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)
+{
+ char *linebufnew;
+
+ 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;
+ linebufnew = realloc(linebuf, buflen);
+ if(!linebufnew) {
+ free(linebuf);
+ error = "Out of memory";
+ return NULL;
+ }
+ linebuf = linebufnew;
+ }
+ 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;
+ linebufnew = realloc(linebuf, buflen);
+ if(!linebufnew) {
+ free(linebuf);
+ error = "Out of memory";
+ return NULL;
+ }
+ linebuf = linebufnew;
+ }
+ 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)
+{
+ int start = 0;
+ 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 ( src )
+ start = SDL_RWtell(src);
+
+ 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 = 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 = (Uint8)(rgb >> 16);
+ c->g = (Uint8)(rgb >> 8);
+ c->b = (Uint8)(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 = 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] = (Uint8)QUICK_COLORHASH(colors,
+ line + x);
+ else
+ for(x = 0; x < w; x++)
+ dst[x] = (Uint8)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) {
+ if ( src )
+ SDL_RWseek(src, start, RW_SEEK_SET);
+ if ( image ) {
+ 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)
+{
+ if ( !src ) {
+ /* The error message has been set in SDL_RWFromFile */
+ return NULL;
+ }
+ return load_xpm(NULL, src);
+}
+
+SDL_Surface *IMG_ReadXPMFromArray(char **xpm)
+{
+ return load_xpm(xpm, NULL);
+}
+
+#else /* not LOAD_XPM */
+
+/* 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);
+}
+
+SDL_Surface *IMG_ReadXPMFromArray(char **xpm)
+{
+ return NULL;
+}
+#endif /* not LOAD_XPM */