/* $Id: xpmread.c,v 5.0 2001/04/07 20:00:59 dik Exp $ * * XPilot, a multiplayer gravity war game. Copyright (C) 1991-2001 by * * Bjørn Stabell * Ken Ronny Schouten * Bert Gijsbers * Dick Balaska * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #ifndef _WINDOWS # include # include # include #endif #include "version.h" #include "config.h" #include "const.h" #include "paint.h" #include "xinit.h" #include "error.h" #include "commonproto.h" #define XPM_READ_C #include "xpmread.h" char xpmread_version[] = VERSION; /* Kludge for visuals under C++ */ #if defined(__cplusplus) #define class c_class #endif static const char *xpm_key_strings[XPM_nkeys] = { "m", "g4", "g", "c", "s" }; static void xpm_print_error(XPM_read *xpmr) { char buf[1024 + 256]; sprintf(buf, "XPM read %s%s%s", xpmr->filename ? xpmr->filename : "internal data", xpmr->error_str ? ": " : "", xpmr->error_str ? xpmr->error_str : ""); error(buf); } static int xpm_read_error(XPM_read *xpmr, const char *err_str) { xpmr->error_str = err_str; return -1; } /* * Split up the input string stream into tokens. * Since we only really need double-quote-separated strings * we ignore the other details. * * Note that there is no backslash processing done for * escaped characters within strings or nul-bytes. * We just assume that all XPM writers will be sensible * enough not to use those tricks. */ static char *xpm_next_token(XPM_read *xpmr) { char *ptr, *tok; /* free any previous token */ if (xpmr->token) { free(xpmr->token); xpmr->token = NULL; } ptr = xpmr->ptr; do { /* skip whitespace */ while (isascii(*ptr) && isspace(*ptr)) { ptr++; } /* where token starts */ tok = ptr; /* skip comments. */ if (*ptr == '/' && ptr[1] == '*') { for (ptr += 2; *ptr; ptr++) { if (*ptr == '*' && ptr[1] == '/') { ptr += 2; break; } } } /* repeat until token does not point to whitespace or comments. */ } while (*ptr && ptr > tok); /* if some data remains... */ if (*ptr) { /* find end of token and let `ptr' point to last char of token. */ /* if it's a string. */ if (*ptr == '"') { /* find end of string. */ for (ptr++; *ptr; ptr++) { if (*ptr == '"') { break; } } } /* if it's something else. */ #define ISKEYWORD(c) (isupper(c) || islower(c) || (c) == '_') #define ISKEYWORDEXT(c) (ISKEYWORD(c) || isdigit(c)) else if (isascii(*ptr) && ISKEYWORD(*ptr)) { for (ptr++; *ptr; ptr++) { if (!isascii(*ptr) || !ISKEYWORDEXT(*ptr)) { break; } } /* one too far in all cases, so backup one position. */ ptr--; } /* if a token remains copy it. */ if (*ptr) { int len = ptr - tok + 1; if ((xpmr->token = (char *)malloc(len + 1)) != NULL) { memcpy(xpmr->token, tok, len); xpmr->token[len] = '\0'; } /* point past token now. */ ptr++; } /* store next input position. */ xpmr->ptr = ptr; } /* found a new token if non-zero. */ return xpmr->token; } /* * Find the next double-quoted-string in the input stream. * Return NULL if none remains. */ static char *xpm_next_string(XPM_read *xpmr) { if (xpmr->static_data) { if (xpmr->token) { free(xpmr->token); } /* point to next string. */ xpmr->static_data++; /* static_data may point to read-only memory (Linux). */ xpmr->token = xp_strdup(*xpmr->static_data); } else { /* skip any non-string tokens. */ while (xpm_next_token(xpmr)) { if (xpmr->token[0] == '"') { /* strip the string double quotes. */ char *str = xp_strdup(xpmr->token + 1); free(xpmr->token); xpmr->token = str; if (str && (str = strchr(str, '"')) != NULL) { *str = '\0'; } break; } } } /* found a new string if non-zero. */ return xpmr->token; } static int xpm_parse_data(XPM_read *xpmr) { unsigned size; int i, j, k, m, num_keys; char *key, *str; unsigned char *pixelp; if (sscanf(xpmr->token, "%u %u %u %u", &xpmr->xpm->width, &xpmr->xpm->height, &xpmr->xpm->ncolors, &xpmr->xpm->cpp) != 4) { return xpm_read_error(xpmr, "Incorrect values specification"); } if (xpmr->xpm->ncolors > 256) { return xpm_read_error(xpmr, "Too many colors defined"); } size = xpmr->xpm->ncolors * sizeof(XPM_color); if (!(xpmr->xpm->colors = (XPM_color *)malloc(size))) { return xpm_read_error(xpmr, "Not enough memory"); } memset(xpmr->xpm->colors, 0, size); size = xpmr->xpm->ncolors * sizeof(char *); if (!(xpmr->chars_ptr = (char **)malloc(size))) { return xpm_read_error(xpmr, "Not enough memory"); } memset(xpmr->chars_ptr, 0, size); size = xpmr->xpm->ncolors * (xpmr->xpm->cpp + 1); if (!(xpmr->chars_mem = (char *)malloc(size))) { return xpm_read_error(xpmr, "Not enough memory"); } memset(xpmr->chars_mem, 0, size); for (i = 0; i < xpmr->xpm->ncolors; i++) { xpmr->chars_ptr[i] = &xpmr->chars_mem[i * (xpmr->xpm->cpp + 1)]; if (!xpm_next_string(xpmr)) { return xpm_read_error(xpmr, "Premature end-of-data"); } for (j = 0; j < xpmr->xpm->cpp; j++) { if (!(xpmr->chars_ptr[i][j] = xpmr->token[j])) { return xpm_read_error(xpmr, "Incomplete color specification"); } } xpmr->chars_ptr[i][j] = '\0'; num_keys = 0; for (key = strtok(&xpmr->token[j + 1], " \t"); key != NULL && (str = strtok(NULL, " \t")) != NULL; key = strtok(NULL, " \t")) { for (k = 0; k < XPM_nkeys; k++) { if (!strcmp(xpm_key_strings[k], key)) { if (xpmr->xpm->colors[i].keys[k]) { /* key already defined for this color! */ break; } xpmr->xpm->colors[i].keys[k] = xp_strdup(str); num_keys++; break; } } } if (num_keys == 0) { return xpm_read_error(xpmr, "Incomplete color specification"); } } size = xpmr->xpm->width * xpmr->xpm->height; if (!(xpmr->xpm->pixels = (unsigned char *)malloc(size))) { return xpm_read_error(xpmr, "Not enough memory"); } pixelp = xpmr->xpm->pixels; for (j = 0; j < xpmr->xpm->height; j++) { if (!xpm_next_string(xpmr)) { return xpm_read_error(xpmr, "Premature end-of-data"); } str = xpmr->token; for (i = 0; i < xpmr->xpm->width; i++) { for (k = 0; k < xpmr->xpm->ncolors; k++) { for (m = 0; m < xpmr->xpm->cpp; m++) { if (xpmr->chars_ptr[k][m] != str[m]) { break; } } if (m == xpmr->xpm->cpp) { *pixelp = k; break; } } if (k == xpmr->xpm->ncolors) { return xpm_read_error(xpmr, "Unmatched pixel"); } pixelp++; str += xpmr->xpm->cpp; } } return 0; } static int xpm_parse_buffer(XPM_read *xpmr) { static const char XPM_header[] = "/* XPM */"; if ((xpmr->ptr = strstr(xpmr->ptr, XPM_header)) == NULL) { return xpm_read_error(xpmr, "Can't find XPM header"); } xpmr->ptr += strlen(XPM_header); while (xpm_next_token(xpmr)) { if (!strcmp(xpmr->token, "{")) { /* no matching "}" */ break; } } if (!xpm_next_string(xpmr)) { return xpm_read_error(xpmr, "Premature end-of-data"); } return xpm_parse_data(xpmr); } static int xpm_load_data(XPM_read *xpmr) { int fd; unsigned size; struct stat st; if ((fd = open(xpmr->filename, O_RDONLY)) == -1) { return xpm_read_error(xpmr, NULL); } if (fstat(fd, &st)) { close(fd); return xpm_read_error(xpmr, xpmr->filename); } size = (unsigned)st.st_size; xpmr->data_size = size; if (!(xpmr->data = (char *)malloc(size + 1))) { close(fd); return xpm_read_error(xpmr, "Not enough memory"); } if (read(fd, xpmr->data, size) != size) { free(xpmr->data); xpmr->data = NULL; close(fd); return xpm_read_error(xpmr, xpmr->filename); } close(fd); xpmr->data[size] = '\0'; xpmr->ptr = xpmr->data; return 0; } static void xpm_free_xpmr(XPM_read *xpmr) { if (xpmr->data) { free(xpmr->data); xpmr->data = NULL; } if (xpmr->token) { free(xpmr->token); xpmr->token = NULL; } if (xpmr->chars_ptr) { free(xpmr->chars_ptr); xpmr->chars_ptr = NULL; } if (xpmr->chars_mem) { free(xpmr->chars_mem); xpmr->chars_mem = NULL; } } static void xpm_free_xpm(XPM *xpm) { if (xpm->colors) { free(xpm->colors); xpm->colors = NULL; } if (xpm->pixels) { free(xpm->pixels); xpm->pixels = NULL; } } static int xpm_read_xpm_from_file(char *filename, XPM *xpm) { XPM_read xpmr; memset(&xpmr, 0, sizeof(xpmr)); xpmr.filename = filename; xpmr.xpm = xpm; if (xpm_load_data(&xpmr) == 0) { xpm_parse_buffer(&xpmr); xpm_free_xpmr(&xpmr); } if (xpmr.error_str) { xpm_print_error(&xpmr); xpm_free_xpm(xpm); return -1; } return 0; } static int xpm_read_xpm_from_data(const char **data, XPM *xpm) { XPM_read xpmr; memset(&xpmr, 0, sizeof(xpmr)); xpmr.xpm = xpm; xpmr.static_data = data; xpmr.token = xp_strdup(*xpmr.static_data); xpm_parse_data(&xpmr); xpm_free_xpmr(&xpmr); if (xpmr.error_str) { xpm_print_error(&xpmr); xpm_free_xpm(xpm); return -1; } return 0; } static int xpm_colors_to_pixels(XPM *xpm, enum XPM_key key, unsigned long *pixels) { int i, j, k; XColor xcolor; char *color_name; for (i = 0; i < xpm->ncolors; i++) { memset(&xcolor, 0, sizeof(xcolor)); color_name = NULL; for (k = key; k >= 0; k--) { if (xpm->colors[i].keys[k]) { color_name = xpm->colors[i].keys[k]; if (XParseColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), color_name, &xcolor)) { break; } printf("Can't parse color \"%s\"\n", color_name); color_name = NULL; } } if (!color_name) { for (k = key + 1; k <= XPM_c; k++) { if (xpm->colors[i].keys[k]) { color_name = xpm->colors[i].keys[k]; if (XParseColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), color_name, &xcolor)) { break; } printf("Can't parse color \"%s\"\n", color_name); color_name = NULL; } } } if (!color_name) { if (xpm->colors[i].keys[XPM_s] && !strcmp(xpm->colors[i].keys[XPM_s], "None")) { pixels[i] = colors[BLACK].pixel; } else { return -1; } } else { unsigned dr, dg, db, dist, mindist = UINT_MAX; for (j = 0; j < maxColors; j++) { dr = xcolor.red - colors[j].red; dg = xcolor.green - colors[j].green; db = xcolor.blue - colors[j].blue; dist = sqr(dr) + sqr(dg) + sqr(db); if (dist < mindist) { mindist = dist; pixels[i] = colors[j].pixel; } } } } return 0; } static XImage *xpm_convert_to_image(XPM *xpm) { XImage *img; int i, j; unsigned char *ptr; enum XPM_key key; unsigned long pixels[256]; if (mono) { key = XPM_m; } else if (visual->class == GrayScale) { if (maxColors == 4) { key = XPM_g4; } else { key = XPM_g; } } else { key = XPM_c; } if (xpm_colors_to_pixels(xpm, key, pixels)) { return NULL; } img = XCreateImage(dpy, visual, dispDepth, ZPixmap, 0, NULL, xpm->width, xpm->height, (dispDepth <= 8) ? 8 : (dispDepth <= 16) ? 16 : 32, 0); if (!img) { error("Can't create XImage"); return NULL; } if (!(img->data = (char *)malloc(img->bytes_per_line * xpm->height))) { error("Can't allocate XImage data"); XDestroyImage(img); return NULL; } ptr = (unsigned char *)xpm->pixels; for (j = 0; j < xpm->height; j++) { for (i = 0; i < xpm->width; i++) { XPutPixel(img, i, j, pixels[*ptr]); ptr++; } } return img; } static Pixmap xpm_image_to_pixmap(XPM *xpm, XImage *img) { Pixmap pixmap; GC pixgc; pixmap = XCreatePixmap(dpy, p_draw, xpm->width, xpm->height, dispDepth); if (pixmap != None) { pixgc = XCreateGC(dpy, pixmap, 0, NULL); XPutImage(dpy, pixmap, pixgc, img, 0, 0, 0, 0, xpm->width, xpm->height); XFreeGC(dpy, pixgc); } else { error("Can't create XPM pixmap"); } return pixmap; } static Pixmap xpm_convert_to_pixmap(XPM *xpm) { Pixmap pixmap; XImage *img; if (!(img = xpm_convert_to_image(xpm))) { return None; } pixmap = xpm_image_to_pixmap(xpm, img); free(img->data); img->data = NULL; XDestroyImage(img); return pixmap; } Pixmap xpm_pixmap_from_data(const char **data) { XPM xpm; Pixmap pixmap; memset(&xpm, 0, sizeof(xpm)); if (xpm_read_xpm_from_data(data, &xpm)) { return None; } pixmap = xpm_convert_to_pixmap(&xpm); xpm_free_xpm(&xpm); return pixmap; } Pixmap xpm_pixmap_from_file(char *filename) { XPM xpm; Pixmap pixmap; memset(&xpm, 0, sizeof(xpm)); if (xpm_read_xpm_from_file(filename, &xpm)) { return None; } pixmap = xpm_convert_to_pixmap(&xpm); xpm_free_xpm(&xpm); return pixmap; } XImage *xpm_image_from_pixmap(Pixmap pixmap) { XImage *img; Window rootw; int x, y; unsigned width, height, border_width, depth; if (!XGetGeometry(dpy, pixmap, &rootw, &x, &y, &width, &height, &border_width, &depth)) { error("Can't get pixmap geometry"); return NULL; } img = XGetImage(dpy, pixmap, 0, 0, width, height, AllPlanes, ZPixmap); if (!img) { error("Can't get Image from Pixmap"); return NULL; } return img; } #if 0 /* some unfinished stuff for xpmwrite.c */ char **xpm_data_from_image(XImage *img) { char *mem; char **data; int i; unsigned long apixel; unsigned char *pixels, *ptr; unsigned x, y, ncolors, cpp, num_ptrs, size; unsigned img_colors[256]; XColor xcolors[256]; if (!(pixels = (char *)malloc(img->width * img->height))) { error("Not enough memory"); return NULL; } memset(img_colors, -1, sizeof(img_colors)); ptr = pixels; for (y = 0; y < img->height; y++) { for (x = 0; x < img->height; x++) { *ptr = (unsigned char)XGetPixel(img, x, y); img_colors[*ptr] = 0; ptr++; } } for (i = 0; i < 256; i++) { ncolors += !img_colors[i]; } cpp = (ncolors <= 16) ? 2 : 1; num_ptrs = img->height + 2 + ncolors; size = (6 + 6 + 4 + 4) + ncolors * (2 + 2 + 2 + 10) + img->height * img->width * cpp + num_ptrs * sizeof(char *); if (!(data = (char **)malloc(size))) { free(pixels); return NULL; } mem = (char *)(data + num_ptrs); sprintf(mem, "%u %u %u %u", img->width, img->height, ncolors, cpp); data[0] = mem; mem += strlen(mem) + 1; if (ncolors <= 16) { for (i = 0; i < ncolors; i++) { sprintf(mem, "%c c %u %u", 'a' + i, img->height, ncolors, cpp); data[0] = mem; mem += strlen(mem) + 1; } } sprintf(mem, ""); data = (char **)mem; size = igm->width * sizeof(char) + img->height; if (!(data = (char **)malloc())) { } } char **xpm_data_from_pixmap(Pixmap pixmap) { char **data = NULL; XImage *img; if ((img = xpm_image_from_pixmap(pixmap)) != NULL) { if (!(data = xpm_data_from_image(img))) { } } } #endif