1 /*
2 * Schism Tracker - a cross-platform Impulse Tracker clone
3 * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4 * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5 * copyright (c) 2009 Storlek & Mrs. Brisby
6 * copyright (c) 2010-2012 Storlek
7 * URL: http://schismtracker.org/
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "headers.h"
25
26 #include "sdlmain.h"
27 #include "video.h" /* for declaration of xpmdata */
28
29 #include <ctype.h>
30
31 /*
32 ** This came from SDL_image's IMG_xpm.c
33 *
34 SDL_image: An example image loading library for use with SDL
35 Copyright (C) 1999-2004 Sam Lantinga
36
37 This library is free software; you can redistribute it and/or
38 modify it under the terms of the GNU Library General Public
39 License as published by the Free Software Foundation; either
40 version 2 of the License, or (at your option) any later version.
41
42 This library is distributed in the hope that it will be useful,
43 but WITHOUT ANY WARRANTY; without even the implied warranty of
44 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
45 Library General Public License for more details.
46
47 You should have received a copy of the GNU Library General Public
48 License along with this library; if not, write to the Free
49 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
50
51 Sam Lantinga
52 slouken@libsdl.org
53 */
54
55 #define SKIPSPACE(p) \
56 do { \
57 while(isspace((unsigned char)*(p))) \
58 ++(p); \
59 } while(0)
60
61 #define SKIPNONSPACE(p) \
62 do { \
63 while(!isspace((unsigned char)*(p)) && *p) \
64 ++(p); \
65 } while(0)
66
67 /* portable case-insensitive string comparison */
string_equal(const char * a,const char * b,int n)68 static int string_equal(const char *a, const char *b, int n)
69 {
70 while(*a && *b && n) {
71 if(toupper((unsigned char)*a) != toupper((unsigned char)*b))
72 return 0;
73 a++;
74 b++;
75 n--;
76 }
77 return *a == *b;
78 }
79
80 #define ARRAYSIZE(a) (int)(sizeof(a) / sizeof((a)[0]))
81
82 /*
83 * convert colour spec to RGB (in 0xrrggbb format).
84 * return 1 if successful.
85 */
color_to_rgb(const char * spec,int speclen,uint32_t * rgb)86 static int color_to_rgb(const char *spec, int speclen, uint32_t *rgb)
87 {
88 /* poor man's rgb.txt */
89 static struct { const char *name; uint32_t rgb; } known[] = {
90 {"none", 0xffffffff},
91 {"black", 0x00000000},
92 {"white", 0x00ffffff},
93 {"red", 0x00ff0000},
94 {"green", 0x0000ff00},
95 {"blue", 0x000000ff},
96 {"gray27",0x00454545},
97 {"gray4", 0x000a0a0a},
98 };
99
100 if(spec[0] == '#') {
101 char buf[7];
102 switch(speclen) {
103 case 4:
104 buf[0] = buf[1] = spec[1];
105 buf[2] = buf[3] = spec[2];
106 buf[4] = buf[5] = spec[3];
107 break;
108 case 7:
109 memcpy(buf, spec + 1, 6);
110 break;
111 case 13:
112 buf[0] = spec[1];
113 buf[1] = spec[2];
114 buf[2] = spec[5];
115 buf[3] = spec[6];
116 buf[4] = spec[9];
117 buf[5] = spec[10];
118 break;
119 }
120 buf[6] = '\0';
121 *rgb = strtol(buf, NULL, 16);
122 return 1;
123 } else {
124 int i;
125 for(i = 0; i < ARRAYSIZE(known); i++)
126 if(string_equal(known[i].name, spec, speclen)) {
127 *rgb = known[i].rgb;
128 return 1;
129 }
130 return 0;
131 }
132 }
133
134 #define STARTING_HASH_SIZE 256
135 struct hash_entry {
136 char *key;
137 uint32_t color;
138 struct hash_entry *next;
139 };
140
141 struct color_hash {
142 struct hash_entry **table;
143 struct hash_entry *entries; /* array of all entries */
144 struct hash_entry *next_free;
145 int size;
146 int maxnum;
147 };
148
hash_key(const char * key,int cpp,int size)149 static int hash_key(const char *key, int cpp, int size)
150 {
151 int hash;
152
153 hash = 0;
154 while ( cpp-- > 0 ) {
155 hash = hash * 33 + *key++;
156 }
157 return hash & (size - 1);
158 }
159
create_colorhash(int maxnum)160 static struct color_hash *create_colorhash(int maxnum)
161 {
162 int bytes, s;
163 struct color_hash *hash;
164
165 /* we know how many entries we need, so we can allocate
166 everything here */
167 hash = malloc(sizeof *hash);
168 if(!hash)
169 return NULL;
170
171 /* use power-of-2 sized hash table for decoding speed */
172 for(s = STARTING_HASH_SIZE; s < maxnum; s <<= 1)
173 ;
174 hash->size = s;
175 hash->maxnum = maxnum;
176 bytes = hash->size * sizeof(struct hash_entry **);
177 hash->entries = NULL; /* in case malloc fails */
178 hash->table = malloc(bytes);
179 if(!hash->table)
180 return NULL;
181 memset(hash->table, 0, bytes);
182 hash->entries = malloc(maxnum * sizeof(struct hash_entry));
183 if(!hash->entries)
184 return NULL;
185 hash->next_free = hash->entries;
186 return hash;
187 }
188
add_colorhash(struct color_hash * hash,char * key,int cpp,uint32_t color)189 static int add_colorhash(struct color_hash *hash,
190 char *key, int cpp, uint32_t color)
191 {
192 int h = hash_key(key, cpp, hash->size);
193 struct hash_entry *e = hash->next_free++;
194 e->color = color;
195 e->key = key;
196 e->next = hash->table[h];
197 hash->table[h] = e;
198 return 1;
199 }
200
201 /* fast lookup that works if cpp == 1 */
202 #define QUICK_COLORHASH(hash, key) ((hash)->table[*(uint8_t *)(key)]->color)
203
get_colorhash(struct color_hash * hash,const char * key,int cpp)204 static uint32_t get_colorhash(struct color_hash *hash, const char *key, int cpp)
205 {
206 struct hash_entry *entry = hash->table[hash_key(key, cpp, hash->size)];
207 while(entry) {
208 if(memcmp(key, entry->key, cpp) == 0)
209 return entry->color;
210 entry = entry->next;
211 }
212 return 0; /* garbage in - garbage out */
213 }
214
free_colorhash(struct color_hash * hash)215 static void free_colorhash(struct color_hash *hash)
216 {
217 if(hash && hash->table) {
218 free(hash->table);
219 free(hash->entries);
220 free(hash);
221 }
222 }
223
224
xpmdata(const char * data[])225 SDL_Surface *xpmdata(const char *data[])
226 {
227 SDL_Surface *image = NULL;
228 int n;
229 int x, y;
230 int w, h, ncolors, cpp;
231 int indexed;
232 Uint8 *dst;
233 struct color_hash *colors = NULL;
234 SDL_Color *im_colors = NULL;
235 char *keystrings = NULL, *nextkey;
236 const char *line;
237 const char ***xpmlines = NULL;
238 #define get_next_line(q) *(*q)++
239 int error;
240 int usedn;
241
242 error = 0;
243
244 xpmlines = (const char ***) &data;
245
246 line = get_next_line(xpmlines);
247 if(!line) goto done;
248
249 /*
250 * The header string of an XPMv3 image has the format
251 *
252 * <width> <height> <ncolors> <cpp> [ <hotspot_x> <hotspot_y> ]
253 *
254 * where the hotspot coords are intended for mouse cursors.
255 * Right now we don't use the hotspots but it should be handled
256 * one day.
257 */
258 if(sscanf(line, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4
259 || w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) {
260 error = 1;
261 goto done;
262 }
263
264 keystrings = malloc(ncolors * cpp);
265 if(!keystrings) {
266 error = 2;
267 goto done;
268 }
269 nextkey = keystrings;
270
271 /* Create the new surface */
272 if(ncolors <= 256) {
273 indexed = 1;
274 image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 8,
275 0, 0, 0, 0);
276 im_colors = image->format->palette->colors;
277 image->format->palette->ncolors = ncolors;
278 } else {
279 indexed = 0;
280 image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32,
281 0xff0000, 0x00ff00, 0x0000ff, 0);
282 }
283 if(!image) {
284 /* Hmm, some SDL error (out of memory?) */
285 error = 3;
286 goto done;
287 }
288
289 /* Read the colors */
290 colors = create_colorhash(ncolors);
291 if (!colors) {
292 error = 2;
293 goto done;
294 }
295 usedn = 1;
296 for(n = 0; n < ncolors; ++n) {
297 const char *p;
298 line = get_next_line(xpmlines);
299 if(!line)
300 goto done;
301
302 p = line + cpp + 1;
303
304 /* parse a colour definition */
305 for(;;) {
306 char nametype;
307 const char *colname;
308 uint32_t rgb, pixel;
309 SDL_Color *c;
310 int m;
311
312 SKIPSPACE(p);
313 if(!*p) {
314 error = 3;
315 goto done;
316 }
317 nametype = *p;
318 SKIPNONSPACE(p);
319 SKIPSPACE(p);
320 colname = p;
321 SKIPNONSPACE(p);
322 if(nametype == 's')
323 continue; /* skip symbolic colour names */
324
325 if(!color_to_rgb(colname, p - colname, &rgb))
326 continue;
327
328
329 memcpy(nextkey, line, cpp);
330 if(indexed) {
331 /* arrange for None to be color 0 */
332 if (usedn && (rgb == 0xffffffff)) {
333 m = 0;
334 usedn = 0;
335 } else {
336 m = n+usedn;
337 }
338
339 c = im_colors + m;
340 c->r = rgb >> 16;
341 c->g = rgb >> 8;
342 c->b = rgb;
343 pixel = m;
344 } else
345 pixel = rgb;
346 add_colorhash(colors, nextkey, cpp, pixel);
347 nextkey += cpp;
348 if(rgb == 0xffffffff)
349 SDL_SetColorKey(image, SDL_SRCCOLORKEY, pixel);
350 break;
351 }
352 }
353
354 /* Read the pixels */
355 dst = image->pixels;
356 for(y = 0; y < h; y++) {
357 line = get_next_line(xpmlines);
358 if(indexed) {
359 /* optimization for some common cases */
360 if(cpp == 1)
361 for(x = 0; x < w; x++)
362 dst[x] = QUICK_COLORHASH(colors,
363 line + x);
364 else
365 for(x = 0; x < w; x++)
366 dst[x] = get_colorhash(colors,
367 line + x * cpp,
368 cpp);
369 } else {
370 for (x = 0; x < w; x++)
371 ((uint32_t*)dst)[x] = get_colorhash(colors,
372 line + x * cpp,
373 cpp);
374 }
375 dst += image->pitch;
376 }
377
378 done:
379 if(error) {
380 SDL_FreeSurface(image);
381 image = NULL;
382 }
383 free(keystrings);
384 free_colorhash(colors);
385 return image;
386 }
387