Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

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