IMG_xpm.c 10 KB

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