Line data Source code
1 : /* Copyright (C) 2000-2012 by George Williams */
2 : /* 2013apr11, additional fixes and error checks done, Jose Da Silva */
3 : /*
4 : * Redistribution and use in source and binary forms, with or without
5 : * modification, are permitted provided that the following conditions are met:
6 :
7 : * Redistributions of source code must retain the above copyright notice, this
8 : * list of conditions and the following disclaimer.
9 :
10 : * Redistributions in binary form must reproduce the above copyright notice,
11 : * this list of conditions and the following disclaimer in the documentation
12 : * and/or other materials provided with the distribution.
13 :
14 : * The name of the author may not be used to endorse or promote products
15 : * derived from this software without specific prior written permission.
16 :
17 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18 : * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 : * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20 : * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 : * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 : * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 : * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : */
28 : #include "gimage.h"
29 : /*#include "gxdrawP.h"*/
30 : #include "string.h"
31 : #include "utype.h"
32 :
33 : /* X Pixmap format
34 : These are c files, all the significant data resides in strings, everything
35 : else may be ignored (well, the first line must be the comment / * XPM * /)
36 :
37 : / * XPM * /
38 : static char * plaid[] =
39 : {
40 : / * plaid pixmap * /
41 : / * width height ncolors chars_per_pixel * /
42 : "22 22 4 2 0 0 XPMEXT",
43 : / * colors * /
44 : " c red m white s light_color",
45 : "Y c green m black s ines_in_mix",
46 : "+ c yellow m white s lines_in_dark ",
47 : "x m black s dark_color ",
48 : / * pixels * /
49 : "x x x x x x x x x x x x + x x x x x ",
50 : " x x x x x x x x x x x x x x x x ",
51 : "x x x x x x x x x x x x + x x x x x ",
52 :
53 : The first string has 4 interesting numbers in it: width, height, ncolors,
54 : characters per pixel, these may be followed by the hot spot location (2 nums)
55 : or the text XPMEXT which means there are extensions later.
56 :
57 : The next few strings (there will be ncolors of them) provide a mapping between
58 : character(s) and colors. Above the " " combination maps to red on a color
59 : display, white on monochrome and is called light_color.
60 :
61 : After that come lines of pixels. Each line is width*chars_per_pixel long and
62 : there are height of them.
63 :
64 : color names may be: a name in the x database (ie sky blue), #xxxxxx (hex rgb),
65 : %xxxxxx (hex hsb), None==transparent, "a symbolic name" and I don't know what
66 : good they are.
67 : */
68 : /* There seems to be another, similar format XPM2 which is the same except
69 : nothing is in a string. Example:
70 : ! XPM2
71 : 64 64 2 1
72 : . c #FFFFFFFFFFFF
73 : + c #000000000000
74 : .....................++.......+...+.............................
75 : .+....................................+..++.....................
76 : ..+.............................................................
77 : .....+.................+........................................
78 : ........++..................................+...................
79 : ................................................................
80 : ................................................................
81 : ................................................+...............
82 : ............................+...................................
83 : */
84 :
85 0 : static int getstring(unsigned char *buf,int sz,FILE *fp) {
86 : /* get a string of text within "" marks and skip */
87 : /* backslash sequences, or concatenated strings. */
88 0 : int ch, incomment=0;
89 :
90 0 : while ( (ch=getc(fp))>=0 ) {
91 0 : if ( ch=='"' && !incomment )
92 0 : break;
93 0 : if ( !incomment && ch=='/' ) {
94 0 : if ( (ch=getc(fp))<0 ) break;
95 0 : if ( ch=='*' ) incomment=true;
96 0 : else ungetc(ch,fp);
97 0 : } else if ( incomment && ch=='*' ) {
98 0 : if ( (ch=getc(fp))<0 ) break;
99 0 : if ( ch=='/' ) incomment=false;
100 0 : else ungetc(ch,fp);
101 : }
102 : }
103 0 : if ( ch<0 )
104 0 : return( 0 );
105 :
106 : /* Get data within quote marks */
107 0 : while ( --sz>0 && (ch=getc(fp))>=0 && ch!='"' )
108 0 : *buf++ = ch;
109 0 : if ( ch!='"' )
110 0 : return( 0 );
111 0 : *buf = '\0';
112 0 : return( 1 );
113 : }
114 :
115 0 : static int gww_getline(unsigned char *buf,int sz,FILE *fp) {
116 : /* get a single line of text (leave-out the '\n') */
117 0 : int ch=0;
118 0 : unsigned char *pt=buf;
119 :
120 0 : while ( --sz>0 && (ch=getc(fp))>=0 && ch!='\n' && ch!='\r' )
121 0 : *pt++ = ch;
122 0 : if ( ch=='\r' && (ch=getc(fp))!='\n' )
123 0 : ungetc(ch,fp);
124 0 : *pt = '\0';
125 0 : if ( ch<0 && pt==buf )
126 0 : return( 0 );
127 0 : return( 1 );
128 : }
129 :
130 : #define TRANS 0x1000000
131 : union hash {
132 : long color;
133 : union hash *table;
134 : };
135 :
136 0 : static void freetab(union hash *tab, int nchars) {
137 : int i;
138 :
139 0 : if ( tab && nchars>1 ) {
140 0 : for ( i=0; i<256; ++i )
141 0 : if ( tab[i].table!=NULL )
142 0 : freetab(tab[i].table,nchars-1);
143 : }
144 0 : free(tab);
145 0 : }
146 :
147 0 : static int fillupclut(Color *clut, union hash *tab,int index,int nchars) {
148 : int i;
149 :
150 0 : if ( nchars==1 ) {
151 0 : for ( i=0; i<256; ++i )
152 0 : if ( tab[i].color!=-1 ) {
153 0 : if ( tab[i].color == TRANS ) {
154 0 : clut[256] = index;
155 0 : tab[i].color = 0;
156 : }
157 0 : clut[index] = tab[i].color;
158 0 : tab[i].color = index++;
159 : }
160 : } else {
161 0 : for ( i=0; i<256; ++i )
162 0 : if ( tab[i].table!=NULL )
163 0 : index = fillupclut(clut,tab[i].table,index,nchars-1);
164 : }
165 0 : return( index );
166 : }
167 :
168 0 : static long parsecol(char *start, char *end) {
169 0 : long ret = -1;
170 : int ch;
171 :
172 0 : while ( !isspace(*start) && *start!='\0' ) ++start;
173 0 : while ( isspace(*start) ) ++start;
174 0 : while ( end>start && isspace(end[-1]) ) --end;
175 0 : ch = *end; *end = '\0';
176 :
177 0 : if ( strcmp(start,"None")==0 )
178 0 : ret = TRANS; /* no_color==transparent */
179 0 : else if ( *start=='#' || *start=='%' ) {
180 0 : if ( end-start==4 ) {
181 0 : sscanf(start+1,"%lx",&ret);
182 0 : ret = ((ret&0xf00)<<12) | ((ret&0xf0)<<8) | ((ret&0xf)<<4);
183 0 : } else if ( end-start==7 )
184 0 : sscanf(start+1,"%lx",&ret);
185 0 : else if ( end-start==13 ) {
186 : int r,g,b;
187 0 : sscanf(start+1,"%4x%4x%4x",&r,&g,&b);
188 0 : ret = ((r>>8)<<16) | ((g>>8)<<8) | (b>>8);
189 : }
190 0 : if ( *start=='%' ) {
191 : /* TODO! */
192 : /* How do I translate from HSB to RGB???? */
193 : ;
194 : }
195 0 : } else if ( strcmp(start,"white")==0 ) {
196 0 : ret = COLOR_CREATE(255,255,255);
197 : } else {
198 0 : ret = 0;
199 : }
200 :
201 0 : *end = ch;
202 0 : return ret;
203 : }
204 :
205 0 : static char *findnextkey(char *str) {
206 0 : int oktostart=1;
207 :
208 0 : while ( *str ) {
209 0 : if ( isspace(*str)) oktostart=true;
210 0 : else if ( oktostart ) {
211 0 : if (( *str=='c' && isspace(str[1])) ||
212 0 : (*str=='m' && isspace(str[1])) ||
213 0 : (*str=='g' && isspace(str[1])) ||
214 0 : (*str=='g' && str[1]=='4' && isspace(str[2])) ||
215 0 : (*str=='s' && isspace(str[1])) )
216 0 : return( str );
217 0 : oktostart = false;
218 : }
219 0 : ++str;
220 : }
221 0 : return( str );
222 : }
223 :
224 0 : static long findcol(char *str) {
225 : char *pt, *end;
226 0 : const char *try_order = "cgm"; /* Try in this order to find something */
227 :
228 0 : while ( *try_order ) {
229 0 : pt = findnextkey(str);
230 0 : while ( *pt ) {
231 0 : end = findnextkey(pt+2);
232 0 : if ( *pt==*try_order )
233 0 : return( parsecol(pt,end) );
234 0 : pt = end;
235 : }
236 0 : ++try_order;
237 : }
238 0 : return( 0 );
239 : }
240 :
241 0 : static union hash *parse_colors(FILE *fp,unsigned char *line, int lsiz, int ncols, int nchars,
242 : int (*getdata)(unsigned char *,int,FILE *)) {
243 : union hash *tab;
244 : union hash *sub;
245 : int i, j;
246 :
247 0 : if ( (tab=(union hash *)malloc(256*sizeof(union hash)))==NULL ) {
248 0 : NoMoreMemMessage();
249 0 : return( NULL );
250 : }
251 :
252 0 : if ( nchars==1 )
253 0 : memset(tab,-1,256*sizeof(union hash));
254 0 : for ( i=0; i<ncols; ++i ) {
255 0 : if ( !getdata(line,lsiz,fp) ) {
256 0 : freetab(tab,nchars);
257 0 : return( NULL );
258 : }
259 0 : sub = tab;
260 0 : for ( j=0; j<nchars-1; ++j ) {
261 0 : if ( sub[line[j]].table==NULL ) {
262 0 : if ( (sub[line[j]].table=(union hash *)malloc(256*sizeof(union hash)))==NULL ) {
263 0 : NoMoreMemMessage();
264 0 : freetab(tab,nchars);
265 0 : return( NULL );
266 : }
267 0 : if ( j==nchars-2 )
268 0 : memset(sub[line[j]].table,-1,256*sizeof(union hash));
269 : }
270 0 : sub = sub[line[j]].table;
271 : }
272 0 : sub[line[j]].color = findcol((char *) line+j+1);
273 : }
274 0 : return( tab );
275 : }
276 :
277 0 : GImage *GImageReadXpm(char * filename) {
278 : /* Import an *.xpm image, else cleanup and return NULL if error */
279 : /* TODO: There is an XPM3 library that takes care of all cases. */
280 : FILE *fp;
281 0 : GImage *ret=NULL;
282 : struct _GImage *base;
283 : int width, height, cols, nchar;
284 : unsigned char buf[80];
285 : unsigned char *line, *lpt;
286 : int y,j, lsiz;
287 : union hash *tab, *sub;
288 : unsigned char *pt, *end; unsigned long *ipt;
289 0 : int (*getdata)(unsigned char *,int,FILE *) = NULL;
290 :
291 0 : if ( (fp=fopen(filename,"r"))==NULL ) {
292 0 : fprintf(stderr,"Can't open \"%s\"\n", filename);
293 0 : return( NULL );
294 : }
295 :
296 0 : line=NULL; tab=NULL; nchar=0;
297 : /* If file begins with XPM then read lines using getstring;() */
298 : /* otherwise for XPM2 read lines using function gww_getline() */
299 0 : if ( (fgets((char *)buf,sizeof(buf),fp))==NULL )
300 0 : goto errorGImageReadXpm;
301 0 : if ( strstr((char *) buf,"XPM2")!=NULL )
302 0 : getdata = gww_getline;
303 0 : else if ( strstr((char *)buf,"/*")!=NULL && strstr((char *)buf,"XPM")!=NULL && strstr((char *)buf,"*/")!=NULL )
304 0 : getdata = getstring;
305 :
306 : /* If no errors yet then go get width, height, colors, nchars */
307 0 : if ( getdata==NULL ||
308 0 : !getdata(buf,sizeof(buf),fp) ||
309 0 : sscanf((char *)buf,"%d %d %d %d",&width,&height,&cols,&nchar)!=4 )
310 : goto errorGImageReadXpm;
311 :
312 : /* Prepare to fetch one graphic line at a time for conversion */
313 0 : if ( (line=(unsigned char *)malloc((lsiz=nchar*width+20)*sizeof(unsigned char)))==NULL ) {
314 0 : NoMoreMemMessage();
315 0 : goto errorGImageReadXpmMem;
316 : }
317 :
318 : /* Fetch color table */
319 0 : if ( (tab=parse_colors(fp,line,lsiz,cols,nchar,getdata))==NULL )
320 0 : goto errorGImageReadXpmMem;
321 :
322 0 : if ( cols<=256 ) {
323 : Color clut[257];
324 0 : clut[256] = COLOR_UNKNOWN;
325 0 : fillupclut(clut,tab,0,nchar);
326 0 : if ( (ret=GImageCreate(it_index,width,height))==NULL )
327 0 : goto errorGImageReadXpmMem;
328 0 : ret->u.image->clut->clut_len = cols;
329 0 : memcpy(ret->u.image->clut->clut,clut,cols*sizeof(Color));
330 0 : ret->u.image->trans = clut[256];
331 0 : ret->u.image->clut->trans_index = clut[256];
332 : } else {
333 0 : if ( (ret=GImageCreate(it_true,width,height))==NULL )
334 0 : goto errorGImageReadXpmMem;
335 0 : ret->u.image->trans = TRANS; /* TRANS isn't a valid Color, but it fits in our 32 bit pixels */
336 : }
337 :
338 : /* Get image */
339 0 : base = ret->u.image;
340 0 : for ( y=0; y<height; ++y ) {
341 0 : if ( !getdata(line,lsiz,fp))
342 0 : goto errorGImageReadXpm;
343 0 : pt = (uint8 *) (base->data+y*base->bytes_per_line); ipt = NULL; end = pt+width;
344 0 : if ( cols>256 )
345 0 : ipt = (unsigned long *) pt;
346 0 : for ( lpt=line; *line && pt<end; ) {
347 0 : sub = tab;
348 0 : for ( j=0; *lpt && j<nchar-1; ++j, ++lpt )
349 0 : if ( sub!=NULL )
350 0 : sub = sub[*lpt].table;
351 0 : if ( sub!=NULL ) {
352 0 : if ( cols<=256 )
353 0 : *pt = sub[*lpt].color;
354 : else
355 0 : *ipt = sub[*lpt].color;
356 : }
357 0 : ++pt; ++ipt; ++lpt;
358 : }
359 : }
360 0 : free(line);
361 0 : freetab(tab,nchar);
362 0 : fclose(fp);
363 0 : return( ret );
364 :
365 : errorGImageReadXpm:
366 0 : fprintf(stderr,"Bad input file \"%s\"\n",filename );
367 : errorGImageReadXpmMem:
368 0 : GImageDestroy(ret);
369 0 : free(line); freetab(tab,nchar);
370 0 : fclose(fp);
371 0 : return( NULL );
372 : }
|