/** ** $Header: /roq/libim/imxpm.c 1 11/02/99 4:38p Zaphod $ ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) ** a division of General Atomics, San Diego, California, USA ** ** Users and possessors of this source code are hereby granted a ** nonexclusive, royalty-free copyright and design patent license to ** use this code in individual software. License is not granted for ** commercial resale, in whole or in part, without prior written ** permission from SDSC. This source is provided "AS IS" without express ** or implied warranty of any kind. ** ** For further information contact: ** E-Mail: info@sds.sdsc.edu ** ** Surface Mail: Information Center ** San Diego Supercomputer Center ** P.O. Box 85608 ** San Diego, CA 92138-5608 ** (619) 534-5000 **/ #define HEADER " $Header: /roq/libim/imxpm.c 1 11/02/99 4:38p Zaphod $" /** ** FILE ** imxpm.c - X11 PixMap file I/O ** ** PROJECT ** libim - SDSC image manipulation library ** ** DESCRIPTION ** imxpm.c contains routines to read and write Sun Icon files for ** the image manipulation library. Raster data read in is stored ** in a VFB and optional CLT in a tag list. Raster data written ** out is taken from a tag list. ** ** PUBLIC CONTENTS ** d =defined constant ** f =function ** m =defined macro ** t =typedef/struct/union ** v =variable ** ? =other ** none ** ** PRIVATE CONTENTS ** ** imXpmRead f read a X11 Bitmap file ** imXpmWrite f write a X11 Bitmap file ** ** ** HISTORY ** $Log: /roq/libim/imxpm.c $ * * 1 11/02/99 4:38p Zaphod * Revision 1.5 1995/06/30 22:12:26 bduggan * Fixed a macro bug (macro was expanded into 's) * * Revision 1.4 1995/06/29 00:28:04 bduggan * updated copyright year * * Revision 1.3 1995/06/29 00:21:14 bduggan * changed comment * * Revision 1.2 1995/06/15 20:12:07 bduggan * Removed embedded comments, took out a useless var. * * Revision 1.1 1995/05/17 23:49:56 bduggan * Initial revision * **/ #include #include "iminternal.h" #include "imxpm.h" /** ** FORMAT ** xpm - X11 Pixel Map ** ** AKA ** pm ** ** FORMAT REFERENCES ** XPM Manual by Arnaud Le Hors ** (Available in postscript format from ftp.x.org in /R5contrib/xpm-3.4a.tar.gz) ** ** ** CODE CREDITS ** Custom Development, Brian Dugggan, San Diego SuperComputer Center, 1995 ** ** DESCRIPTION ** X11 pixmaps are simple C code to declare and initialize an array of strings. ** The elements of the array are indexes into a color lookup table, which is ** described in the beginning of the file. ** ** For instance, here's a typical xpm file: ** (Replace all the \'s below with /'s. I put them in ** backwards so that this example wouldn't mess up compilation ** of this file (imxpm.c). ) ** ** static char* my_pixmap[] = { ** \* width height ncolors charsperpixel [xhot yhot] [XPMEXT] *\ ** "10 10 3 2 0 0 XPMEXT", ** \* colors *\ ** " c red m white s name_1", ** "xx c green m black s name_2", ** "yy c blue m white s name_3", ** \* pixels *\ ** " xxyy", ** "yy yy yy yy yy", ** " yy yy yy yy ", ** "xx xx xx xx xx", ** " xx xx xx xx ", ** " xxyy", ** "yy yy yy yy yy", ** " yy yy yy yy ", ** "xx xx xx xx xx", ** " xxyy", ** "yy yy yy yy yy", ** \* extension data *\ ** "XPMEXT ext1 data1", ** "XPMENDEXT" ** }; ** ** The stuff at the end of the file (XPMEXT..) is an extension for the ** the picture. We don't touch those here. ** ** The color lookup table works as follows: ** The first 2 characters (since there are 2 character per pixel in this particular ** image) reference the number of the clt. Then "c red" indicates that if color ** is possible this color should be read. "m white" means if only mono is possible, this ** value should be white. "g4 val" means use val for 4-bit grayscale. "g val" means ** Use val for >4 bit grayscale. "s name" means 'name' is the name of this entry. (We ** don't use the s flag.) ** ** xhot and yhot are the hot spot location. ** We read and write hotspots. ** **/ /* * TYPEDEF * charTable * * DESCRIPTION * This is the hash array that we're going to use to store the * various character combination. That is, 2 characters determines * an index value. So the array element corresponding to the 2 characters * will contain this index value. */ typedef int *charTable; /* * XPM - MIT X11 Window System PixMap * For information on these structures, how to use them, etc. please * see imfmt.c. */ #ifdef __STDC__ static int imXpmRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); static int imXpmWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); static int imXpmReadClt(FILE* fp,int numColors, TagTable* tagTable, charTable cltTable, int charsPerPixel, ImClt* clt); static int imXpmGetRgb(char* buffer, unsigned char *red, unsigned char *green, unsigned char *blue, int *transparent); static char* strHasFlag(char* buffer, char c); static int imXpmLookUpRgb(char *colorName,unsigned char*red,unsigned char *green,unsigned char *blue, int* transparent); #else static int imXpmRead( ); static int imXpmWrite( ); static int imXpmReadClt(); static int imXpmGetRgb(); static char* strHasFlag(); static int imXpmLookUpRgb(); #endif static char *imXpmNames[ ] = { "xpm", "pm", NULL }; static ImFileFormatReadMap imXpmReadMap[ ] = { /* in out */ /* type,ch,dep, attr. VFB type attr. */ { IN,1,8, 0, IMVFBINDEX8, 0 }, { -1, 0, -1, 0 }, }; static ImFileFormatWriteMap imXpmWriteMap[ ] = { /* in out */ /* VFB type, attr., type,ch,dep, attr., func */ { IMVFBINDEX8, 0, IN,1,8, 0, imXpmWrite }, { -1, 0, -1, 0, NULL }, }; static ImFileMagic imFileXpmMagic []= { { 0, 0, NULL}, }; ImFileFormat ImFileXpmFormat = { imXpmNames, "X11 pixmap file", "X Consortium / MIT", "ASCII pixmap color indexed files.", "ASCII pixmap color indexed files.", imFileXpmMagic, IMNOMULTI, IMPIPE, IMNOMULTI, IMPIPE, imXpmRead, imXpmReadMap, imXpmWriteMap }; /* * FUNCTION * imXpmRead - read an X11 pixmap file * * DESCRIPTION * The file is read and it's mono image written to a new mono VFB. * * Xpm files have characters which are associated with index values. * Thus, we need to associate an integer (the index value) with * several different one or two character strings. This is done using * a very primitive hash table; An array of integers with 128x128 entries. * i.e. one for each two letter comibination. When we read in the colors, * we set the entry in the array corresponding to the two letter code, to its * corresponding index value. */ static int /* Returns # tags read in */ #ifdef __STDC__ imXpmRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) #else imXpmRead( ioType, fd, fp, flagsTable, tagTable ) int ioType; /* I/O flags */ int fd; /* Input file descriptor */ FILE *fp; /* Input file pointer */ TagTable *flagsTable; /* Flags */ TagTable *tagTable; /* Tag table to add to */ #endif { char message[1024]; /* Error message holder */ char buffer[1024]; /* really big char buffer */ char c,c1,c2; /* characters */ int xSize, ySize; /* dimensions */ int charsPerPixel; /* chars per pixel */ int numColors; /* number of colors */ ImClt* clt; /* clt */ charTable cltTable; /* table w/ clt values */ int i,j; /* indices */ ImVfb* vfb; /* vfb */ ImVfbPtr vfbPtr; /* vfb pointer */ int index; /* hash index */ ImHotSpotPtr hotspotptr; /* points to hot spot */ int xHot, yHot; /* hot spot values */ /* * Please notice that in this routine, I have decided NOT * to rewrite a C-language parser. Instead, I'm only going * to be able to read nice xpm files. So xpm's with quotation * marks in the comment blocks, hex values instead of strings, * extreme amounts of whitespace, or other devious sorts of things * will not be handled very well here. Sorry. */ if ( ioType & IMFILEIOFD ) { /* * Given file descriptor. More convenient to deal with * a file pointer for this format. */ if ( (fp = fdopen( fd, "rb" )) == NULL ) { ImErrNo = IMESYS; return ( -1 ); } } /* * Find the first quote in the file. Then read * in width, height, number of colors, chars per pixel. */ c = '0'; while (c!='"' && !feof(fp)) c = fgetc(fp); if (c!='"') ImErrorFatal("Unexpected end of file", -1, IMESYS); fscanf(fp, "%d %d %d %d", &xSize, &ySize, &numColors, &charsPerPixel); sprintf(message,"%d x %d",xSize,ySize); ImInfo("Resolution",message); ImInfo("Type","8-bit Color Indexed"); sprintf(message,"%d entries",numColors); ImInfo("Color Table",message); ImInfo("Compression Type","none"); ImInfo("Alpha Channel","none"); /* Read the rest of this string into a buffer */ i = 0; while (c!=',' && !feof(fp)) { c = fgetc(fp); buffer[i++] = c; } if (c!=',') ImErrorFatal("Unexpected end of file", -1, IMESYS); buffer[i] = '\0'; if (sscanf(buffer,"%d %d",&xHot,&yHot)==2) { /* we have a hot spot */ sprintf(message,"%d, %d",xHot, yHot); ImInfo("Hot Spot",message); ImMalloc(hotspotptr, ImHotSpotPtr, sizeof(ImHotSpot) * 1); ImHotSpotSX( hotspotptr, xHot); ImHotSpotSY( hotspotptr, yHot); TagTableAppend( tagTable, TagEntryAlloc( "image hot spot", POINTER, &hotspotptr) ); } if (numColors > 256) { ImErrorFatal( "Too many colors", -1, IMEUNSUPPORTED ); } if (charsPerPixel > 2) { ImErrorFatal( "Too many characters per pixel. (Max is 2). ", -1, IMEUNSUPPORTED); } /* * Allocate our hash table */ ImMalloc(cltTable, charTable, (sizeof(int) * 128 * 128) ); clt = ImCltAlloc( numColors ); /* * Read the clt */ imXpmReadClt(fp,numColors, tagTable, cltTable, charsPerPixel, clt); /* * Read the pixels */ vfb = ImVfbAlloc(xSize, ySize, IMVFBINDEX8); ImVfbSClt(vfb,clt); vfbPtr = ImVfbQFirst(vfb); for (i=0;ic red" * This means that the color is red. * The 'c' indicates that a color is next. A 'c' may * also be followed by an rgb value, as follows: * * "c #AA00AB" * This indicates 0xaa red, 0x00 blue, 0xab green. * * The 'c' may be replaced by a .. * 'g' to indicate >4 bit grayscale * 'g4' to indicate 4-bit grayscale * 'm' to indicate mono * 's' to indicate a name (which we don't care about) * * More than one of the above flags may be given. * We'll check the string in the order given above * ('c', 'g', 'g4', 'm'), and use the first thing we find. * * There is no support for the g4 flag here. (I have yet * to see an image with ONLY the g4 flag, and no 'c' flag.) * If somebody wants to add g4 support, then the strHasFlag * routine must be duplicated, and modified to check for a * flag of length 2. */ if (colorName=strHasFlag(buffer,'c')) { /* We have a color */ if (colorName[0] == '#') { sscanf(colorName,"#%2x%2x%2x",&redInt, &greenInt, &blueInt); *red = (unsigned char)redInt; *green = (unsigned char)greenInt; *blue = (unsigned char)blueInt; *transparent = 0; return 1; } else return imXpmLookUpRgb(colorName,red,green,blue,transparent); } if (colorName=strHasFlag(buffer,'g')) { /* We have something like "sgi grey 2" */ if (colorName[0] == '#') { sscanf(colorName,"#%2x%2x%2x",&redInt, &greenInt, &blueInt); *red = (unsigned char)redInt; *green = (unsigned char)greenInt; *blue = (unsigned char)blueInt; *transparent = 0; return 1; } else return imXpmLookUpRgb(colorName,red,green,blue,transparent); } if (colorName=strHasFlag(buffer,'m')) { /* we hopefully have black or white */ if (colorName[0] == '#') { sscanf(colorName,"#%2x%2x%2x",&redInt, &greenInt, &blueInt); *red = (unsigned char)redInt; *green = (unsigned char)greenInt; *blue = (unsigned char)blueInt; *transparent = 0; return 1; } else return imXpmLookUpRgb(colorName,red,green,blue,transparent); } ImErrorFatal("Couldn't parse line!", -1, IMESYNTAX); } #define IS_WHITESPACE(s) ((s)==' ' || (s)=='\t') #define IS_SPECIAL_CHAR(xxx) ((xxx)=='m' || (xxx)=='s' || (xxx)=='g' || (xxx)=='c') /* * FUNCTION * strHasFlag * * DESCRIPTION * Check to see if a string has a given character, * surrounded by whitespace. If it does, return the * entry that follows it. * * The string must be null terminated for this to * work. */ static char* #ifdef __STDC__ strHasFlag(char* str,char c) #else strHasFlag(str , c) char* str; char c; #endif { char* ptr; /* points into the string */ int found = 0; /* means we found it */ static char *ret = NULL; /* return this */ if (ret==NULL) { ImMallocRetOnError(ret, char *, 100,NULL); } ptr = str; while (*ptr!='\0' && !found) { /* Advance to the next occurence of c */ while (*ptr!='\0' && *ptr!=c) { ptr++; } if (*ptr==c) { /* is it surrounded by whitespace? */ if (ptr==str && IS_WHITESPACE(*(ptr+1)) ) found = 1; if (IS_WHITESPACE(*(ptr-1)) && IS_WHITESPACE(*(ptr+1))) found = 1; } if (!found && *ptr!='\0') ptr++; } if (!found) return NULL; /* boo hoo */ /* * Copy the next word into a new string. Return that. * Well, we don't really want to copy just the next word. * There are colors named "dark slate grey" and "sgi gray 0". * What a pain. * So, we'll copy the next words that occur, as long as * the words are not one of { "m", "s", "g4", "g", "c" }. */ strcpy(ret, ptr+2); strcat(ret," "); /* for checking past the end */ ptr = ret; while (*ptr!='\0') { ptr++; if (*ptr=='"') *ptr = '\0'; /* * Check for m,s,g,c */ if (IS_WHITESPACE(*ptr) && IS_WHITESPACE(*(ptr+2)) && IS_SPECIAL_CHAR(*(ptr+1))) *ptr = '\0'; /* * Check for g4 */ if (IS_WHITESPACE(*ptr) && IS_WHITESPACE(*(ptr+3)) && *(ptr+1)=='g' && *(ptr+2)=='4') *ptr = '\0'; } /* * Take any whitespace off of the end of the word */ ptr--; while (IS_WHITESPACE(*ptr)) { *ptr = '\0'; ptr--; } return ret; } /* * FUNCTION * imXpmLookUpRgb * * DESCRIPTION * Figure out the r,g,b values based on the name of the color. * */ static int /* returns status */ #ifdef __STDC__ imXpmLookUpRgb(char *colorName,unsigned char*red,unsigned char *green,unsigned char *blue, int* transparent) #else imXpmLookUpRgb(colorName,red,green,blue,transparent) char* colorName; unsigned char* red; unsigned char* green; unsigned char* blue; int* transparent; #endif { static struct imXpmRecordStruct *rgbList=NULL; /* List that we read in */ static int first_time = 1; /* First time in this routine? */ imXpmColor index; /* One color entry */ char message[500]; /* message buffer */ char filename[1024]; /* Name of file with color names */ char* env_var; /* environment variable */ FILE* fp; /* file pointer */ int nColors=0; /* How many colors we read in */ char str[100]; /* string we read in */ char tmpStr[100]; /* Holds the name for a sec */ int tmpRed,tmpGreen,tmpBlue; /* holds these as ints */ /* * Well, here's how we do this. * * On the first pass through this routine, we'll load into memory * a table with all of the color names and rgb values. * * On subsequent passes we'll look stuff up. * * We'll store the rgb value information in a tagTable. * * Where do we get the information? * 1. Check for the environment variable 'IM_XPM_COLORTABLE' * If it is set, use that filename instead of /usr/lib/X11/rgb.txt. * 2. Look in /usr/lib/X11 for rgb.txt. * * There are some very annoying things about rgb colornames. * Most significantly, the rgb.txt file is often different * from the names of the colors in the rgb.dir and rgb.pag * files. If you don't believe me, type 'showrgb'. This'll * show you the names of the colors in the database. Then look * through rgb.txt. Capital letters and spaces are thrown around * like mashed potatoes in a grade school cafeteria. * * The database contained in imxpm.h came from an execution of * the showrgb command. Hopefully by looking for rgb.txt we'll * be okay... If we really wanted to be thorough, we'd use the * dbm library routines to read the rgb.dir file. But, these * routines seem to be on their way to obsolesence. * */ /* * Is this the first pass? * If so, load in the color list. */ if (first_time==1) { first_time = 0; /* Get environment variable. */ env_var = getenv("IM_XPM_COLORTABLE"); if (env_var) strcpy(filename,env_var); else strcpy(filename,"/usr/lib/X11/rgb.txt"); /* Load list */ fp = fopen(filename,"rb"); if (fp) { /* * We don't know the size of the file ahead of time, * and we want to read into an array. * * Rather than use a complicated memory scheme, we'll * just read the file twice. Once to see how big it is, * the second time to read the stuff in. */ nColors = 0; while (!feof(fp)) { fgets(str, 100, fp); nColors++; } ImMalloc(rgbList, struct imXpmRecordStruct *,(nColors+1)* sizeof(struct imXpmRecordStruct)); /* Okay, now reopen the file, and read the stuff in */ fclose(fp); index = rgbList; fp = fopen(filename,"rb"); while (!feof(fp)) { fgets(str,100,fp); if (str[0]!='\0') { sscanf(str,"%d %d %d %[^\n]", &tmpRed, &tmpGreen, &tmpBlue, tmpStr); index->red = tmpRed; index->green = tmpGreen; index->blue = tmpBlue; ImMalloc(index->name,char *, sizeof(char) * (strlen(tmpStr)+1)); strcpy(index->name,tmpStr); index++; } } /* Set last entry to NULL */ index->name = NULL; } /* End of if-fp */ } /* End of if-first time */ /* * First check external list for color name */ if (rgbList) { index = rgbList; while (index->name != NULL && strcmp(index->name,colorName)!=0) { index++; } if (index->name!=NULL) { /* Found in file */ *red = index->red; *green = index->green; *blue = index->blue; if (strcmp(colorName,"None")==0) *transparent = 1; else *transparent = 0; return 1; } } /* * Check internal list for color name */ index = imXpmColorList; while (index->name != NULL && strcmp(index->name,colorName)!=0) { index++; } if (index->name==NULL) { /* Couldn't find it */ sprintf(message,"Unknown color: '%s'",colorName); ImErrorFatal( message, -1, IMEUNSUPPORTED); } *red = index->red; *green = index->green; *blue = index->blue; if (strcmp(colorName,"None")==0) *transparent = 1; else *transparent = 0; return 1; } /* * FUNCTION * imXpmWrite - write an X11 pixmap file * * DESCRIPTION * The X11 pixmap header and image content are written out. */ static int /* Returns # of entries used */ #ifdef __STDC__ imXpmWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) #else imXpmWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ int ioType; /* Type of I/O to perform */ int fd; /* Output file descriptor */ FILE *fp; /* Output file pointer */ TagTable *flagsTable; /* Flags */ TagTable *tagTable; /* Table of info to write */ #endif { char message[300]; /* various messages */ int xSize, ySize; /* dimensions */ ImVfb* vfb; /* a very furry buffalo */ ImVfbPtr vfbPtr; /* points to a vfb */ ImClt* clt; /* a clt */ ImCltPtr cltPtr; /* points to a clt */ char var_name[100]; /* Name of the variable (in xpm header) */ int nColors; /* number of colors in the clt */ char** cltCodes; /* list of codes for indexes */ int i,j; /* indexes */ int transparency_color=-1; /* transparent color */ char c,d; /* characters we loop with */ char colorName[100]; /* name of a color */ ImHotSpotPtr hotspotptr; /* points to a hotspot */ int xHot, yHot; /* hot spot values */ ImInfo("Type","8-bit Color Indexed"); TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); clt = ImVfbQClt( vfb ); if (ioType & IMFILEIOFD) { /* I prefer an fp, thank you very much */ if ( (fp = fdopen( fd, "rb" )) == NULL ) { ImErrNo = IMESYS; return ( -1 ); } } nColors = ImCltQNColors(clt); xSize = ImVfbQWidth(vfb); ySize = ImVfbQHeight(vfb); sprintf(message,"%d x %d",xSize,ySize); ImInfo("Resolution",message); sprintf(message,"%d entries",nColors); ImInfo("Color Table",message); ImInfo("Compression Type","none"); ImInfo("Alpha Channel","none"); fprintf(fp,"/* XPM */\nstatic char*%s[] = {\n",var_name); if (TagTableQNEntry(tagTable, "image hot spot")==0) { /* no hot spot */ fprintf(fp,"/* width height ncolors chars_per_pixel */\n"); fprintf(fp,"\"%d %d %d 2\",\n",xSize,ySize,nColors); } else { /* there is a hot spot */ fprintf(fp,"/* width height ncolors chars_per_pixel hotspotx hotspoty*/\n"); TagEntryQValue( TagTableQDirect( tagTable, "image hot spot", 0), &hotspotptr); xHot = ImHotSpotQX( hotspotptr ); yHot = ImHotSpotQY( hotspotptr ); sprintf(message,"%d, %d",xHot, yHot); ImInfo("Hot Spot",message); fprintf(fp,"\"%d %d %d 2 %d %d\",\n",xSize,ySize,nColors,xHot, yHot); } fprintf(fp,"/* colors */\n"); /* * Print out the clt */ /* * For each entry in the clt we want to make a new two character * code. Then we want to print this code, followed by 'c '. * * We also need to store this code at the array index corresponding to * it's color entry in the clt for later reference. */ ImMalloc(cltCodes, char**, nColors * sizeof(char *) ); /* list of strings of length 2 */ for (i=0; i< nColors; i++) { ImMalloc(cltCodes[i], char*, 3*sizeof(char)); } /* * Fill up the array of codes. If we have a transparent color, * make that " ", since then the text file will look cool. */ transparency_color = ImGetTransparency(tagTable, flagsTable, vfb); if (transparency_color!=-1) { sprintf(message,"Pixels with index %d.",transparency_color); ImInfo("Transparency",message); } c = 'a'; d = 'a'; cltPtr = ImCltQFirst(clt); for (i=0;i