forked from KolibriOS/kolibrios
3cf7852e03
git-svn-id: svn://kolibrios.org@5131 a494cfbc-eb01-0410-851d-a64ba20cac60
463 lines
9.8 KiB
C
463 lines
9.8 KiB
C
/*
|
|
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 */
|