1 /********************************************************************************
2 *                                                                               *
3 *                          X P M   I n p u t / O u t p u t                      *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 2000,2005 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or                 *
9 * modify it under the terms of the GNU Lesser General Public                    *
10 * License as published by the Free Software Foundation; either                  *
11 * version 2.1 of the License, or (at your option) any later version.            *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU             *
16 * Lesser General Public License for more details.                               *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public              *
19 * License along with this library; if not, write to the Free Software           *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
21 *********************************************************************************
22 * $Id: fxxpmio.cpp,v 1.50 2005/02/04 06:12:58 fox Exp $                         *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "FXHash.h"
28 #include "FXStream.h"
29 #include "fxpriv.h"
30 
31 
32 /*
33   Notes:
34   - The transparent color hopefully does not occur in the image.
35   - If the image is rendered opaque, the transparent is close to white.
36   - References: http://www-sop.inria.fr/koala/lehors/xpm.html
37   - XPM reader/writer is tweaked so that an XPM written to FXStream
38     can be read back in and read exactly as many bytes as were written.
39   - There may be other comment blocks in the file
40 */
41 
42 #define MAXPRINTABLE    92
43 #define MAXVALUE        96
44 #define HASH1(x,n)      (((unsigned int)(x)*13)%(n))            // Number [0..n-1]
45 #define HASH2(x,n)      (1|(((unsigned int)(x)*17)%((n)-1)))    // Number [1..n-2]
46 
47 using namespace FX;
48 
49 
50 /*******************************************************************************/
51 
52 namespace FX {
53 
54 
55 extern FXAPI FXbool fxcheckXPM(FXStream& store);
56 extern FXAPI FXbool fxloadXPM(const FXchar **pix,FXColor*& data,FXint& width,FXint& height);
57 extern FXAPI FXbool fxloadXPM(FXStream& store,FXColor*& data,FXint& width,FXint& height);
58 extern FXAPI FXbool fxsaveXPM(FXStream& store,const FXColor *data,FXint width,FXint height,FXbool fast=TRUE);
59 
60 
61 
62 // Read till end of line
readline(FXStream & store,FXchar * buffer,FXuint size)63 static void readline(FXStream& store,FXchar* buffer,FXuint size){
64   register FXuint i=0;
65   while(!store.eof() && i<size){
66     store >> buffer[i];
67     if(buffer[i]=='\r') continue;
68     if(buffer[i]=='\n') break;
69     i++;
70     }
71   buffer[i]=0;
72   }
73 
74 
75 // Read quoted text
readtext(FXStream & store,FXchar * buffer,FXuint size)76 static void readtext(FXStream& store,FXchar* buffer,FXuint size){
77   register FXuint i=0;
78   FXchar ch;
79   store >> ch;
80   while(!store.eof() && ch!='"') store >> ch;
81   while(!store.eof() && i<size){
82     store >> ch;
83     if(ch=='"') break;
84     buffer[i++]=ch;
85     }
86   buffer[i]=0;
87   }
88 
89 
90 // Parse next word
nextword(const FXchar * & src,FXchar * dst)91 static FXint nextword(const FXchar*& src,FXchar* dst){
92   register FXchar *ptr=dst;
93   while(*src && isspace((FXuchar)*src)) src++;
94   while(*src && !isspace((FXuchar)*src)) *ptr++=*src++;
95   *ptr=0;
96   return (FXint)(ptr-dst);
97   }
98 
99 
100 // Is key
iskey(const FXchar * str)101 static FXbool iskey(const FXchar *str){
102   return ((str[0]=='c' || str[0]=='s' || str[0]=='m' || str[0]=='g') && str[1]==0) || (str[0]=='g' && str[1]=='4' && str[2]==0);
103   }
104 
105 
106 // Check if stream contains a XPM
fxcheckXPM(FXStream & store)107 FXbool fxcheckXPM(FXStream& store){
108   FXuchar signature[9];
109   store.load(signature,9);
110   store.position(-9,FXFromCurrent);
111   return signature[0]=='/' && signature[1]=='*' && signature[2]==' ' && signature[3]=='X' && signature[4]=='P' && signature[5]=='M' && signature[6]==' ' && signature[7]=='*' && signature[8]=='/';
112   }
113 
114 
115 // Load image from array of strings
fxloadXPM(const FXchar ** pixels,FXColor * & data,FXint & width,FXint & height)116 FXbool fxloadXPM(const FXchar **pixels,FXColor*& data,FXint& width,FXint& height){
117   FXchar  lookuptable[1024][8],name[100],word[100],flag,best;
118   FXColor colortable[16384],*pix,color;
119   const FXchar *src,*line;
120   FXint   i,j,ncolors,cpp,c;
121 
122   // Null out
123   data=NULL;
124   width=0;
125   height=0;
126   color=0;
127 
128   // NULL pointer passed in
129   if(!pixels) return FALSE;
130 
131   // Read pointer
132   line=*pixels++;
133 
134   // No size description line
135   if(!line) return FALSE;
136 
137   // Parse size description
138   sscanf(line,"%d %d %u %u",&width,&height,&ncolors,&cpp);
139 
140   // Check size
141   if(width<1 || height<1 || width>16384 || height>16384) return FALSE;
142 
143   // Sensible inputs
144   if(cpp<1 || cpp>8 || ncolors<1) return FALSE;
145 
146   // Limited number of colors for long lookup strings
147   if(cpp>2 && ncolors>1024) return FALSE;
148 
149   // Allow more colors for short lookup strings
150   if(ncolors>16384) return FALSE;
151 
152   FXTRACE((50,"fxloadXPM: width=%d height=%d ncolors=%d cpp=%d\n",width,height,ncolors,cpp));
153 
154   // Read the color table
155   for(c=0; c<ncolors; c++){
156     line=*pixels++;
157     src=line+cpp;
158     nextword(src,word);
159     best='z';
160     while(iskey(word)){
161       flag=word[0];
162       name[0]=0;
163       while(nextword(src,word) && !iskey(word)){
164         strcat(name,word);
165         }
166       if(flag<best){                    // c < g < m < s
167         color=fxcolorfromname(name);
168         best=flag;
169         }
170       }
171     if(cpp==1){
172       colortable[(FXuchar)line[0]]=color;
173       }
174     else if(cpp==2){
175       colortable[(((FXuchar)line[1])<<7)+(FXuchar)line[0]]=color;
176       }
177     else{
178       colortable[c]=color;
179       strncpy(lookuptable[c],line,cpp);
180       }
181     }
182 
183   // Try allocate pixels
184   if(!FXMALLOC(&data,FXColor,width*height)){
185     return FALSE;
186     }
187 
188   // Read the pixels
189   for(i=0,pix=data; i<height; i++){
190     line=*pixels++;
191     for(j=0; j<width; j++){
192       if(cpp==1){
193         color=colortable[(FXuchar)line[0]];
194         }
195       else if(cpp==2){
196         color=colortable[(((FXuchar)line[1])<<7)+(FXuchar)line[0]];
197         }
198       else{
199         for(c=0; c<ncolors; c++){
200           if(strncmp(lookuptable[c],line,cpp)==0){ color=colortable[c]; break; }
201           }
202         }
203       line+=cpp;
204       *pix++=color;
205       }
206     }
207   return TRUE;
208   }
209 
210 
211 /*******************************************************************************/
212 
213 
214 // Load image from stream
fxloadXPM(FXStream & store,FXColor * & data,FXint & width,FXint & height)215 FXbool fxloadXPM(FXStream& store,FXColor*& data,FXint& width,FXint& height){
216   FXchar  lookuptable[1024][8],line[100],name[100],word[100],flag,best,ch;
217   FXColor colortable[16384],*pix,color;
218   const FXchar *src;
219   FXint   i,j,ncolors,cpp,c;
220 
221   // Null out
222   data=NULL;
223   width=0;
224   height=0;
225   color=0;
226 
227   // Read header line
228   readline(store,name,sizeof(name));
229   if(!strstr(name,"XPM")) return FALSE;
230 
231   // Read description
232   readtext(store,line,sizeof(line));
233 
234   // Parse size description
235   if(sscanf(line,"%d %d %u %u",&width,&height,&ncolors,&cpp)!=4) return FALSE;
236 
237   // Check size
238   if(width<1 || height<1 || width>16384 || height>16384) return FALSE;
239 
240   // Sensible inputs
241   if(cpp<1 || cpp>8 || ncolors<1) return FALSE;
242 
243   // Limited number of colors for long lookup strings
244   if(cpp>2 && ncolors>1024) return FALSE;
245 
246   // Allow more colors for short lookup strings
247   if(ncolors>16384) return FALSE;
248 
249   FXTRACE((50,"fxloadXPM: width=%d height=%d ncolors=%d cpp=%d\n",width,height,ncolors,cpp));
250 
251   // Read the color table
252   for(c=0; c<ncolors; c++){
253     readtext(store,line,sizeof(line));
254     src=line+cpp;
255     nextword(src,word);
256     best='z';
257     while(iskey(word)){
258       flag=word[0];
259       name[0]=0;
260       while(nextword(src,word) && !iskey(word)){
261         strcat(name,word);
262         }
263       if(flag<best){                    // c < g < m < s
264         color=fxcolorfromname(name);
265         best=flag;
266         }
267       }
268     if(cpp==1){
269       colortable[(FXuchar)line[0]]=color;
270       }
271     else if(cpp==2){
272       colortable[(((FXuchar)line[1])<<7)+(FXuchar)line[0]]=color;
273       }
274     else{
275       colortable[c]=color;
276       strncpy(lookuptable[c],line,cpp);
277       }
278     }
279 
280   // Try allocate pixels
281   if(!FXMALLOC(&data,FXColor,width*height)){
282     return FALSE;
283     }
284 
285   // Read the pixels
286   for(i=0,pix=data; i<height; i++){
287     while(!store.eof() && (store>>ch,ch!='"'));
288     for(j=0; j<width; j++){
289       store.load(line,cpp);
290       if(cpp==1){
291         color=colortable[(FXuchar)line[0]];
292         }
293       else if(cpp==2){
294         color=colortable[(((FXuchar)line[1])<<7)+(FXuchar)line[0]];
295         }
296       else{
297         for(c=0; c<ncolors; c++){
298           if(strncmp(lookuptable[c],line,cpp)==0){ color=colortable[c]; break; }
299           }
300         }
301       *pix++=color;
302       }
303     while(!store.eof() && (store>>ch,ch!='"'));
304     }
305 
306   // We got the image, but we're not done yet; need to read few more bytes
307   // the number of bytes read here must match the number of bytes written
308   // by fxsaveXPM() so that the stream won't get out of sync
309   while(!store.eof()){
310     store >> ch;
311     if(ch=='\n') break;
312     }
313   return TRUE;
314   }
315 
316 
317 /*******************************************************************************/
318 
319 
320 // Save image to a stream
fxsaveXPM(FXStream & store,const FXColor * data,FXint width,FXint height,FXbool fast)321 FXbool fxsaveXPM(FXStream& store,const FXColor *data,FXint width,FXint height,FXbool fast){
322   const FXchar printable[]=" .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjklzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
323   const FXchar quote='"';
324   const FXchar comma=',';
325   const FXchar newline='\n';
326   FXColor   colormap[256];
327   FXint     numpixels=width*height;
328   FXint     ncolors,cpp,len,i,j,c1,c2;
329   FXchar    buffer[200];
330   FXColor   color;
331   FXuchar  *pixels,*ptr,pix;
332 
333   // Must make sense
334   if(!data || width<=0 || height<=0) return FALSE;
335 
336   // Allocate temp buffer for pixels
337   if(!FXMALLOC(&pixels,FXuchar,numpixels)) return FALSE;
338 
339   // First, try EZ quantization, because it is exact; a previously
340   // loaded XPM will be re-saved with exactly the same colors.
341   if(!fxezquantize(pixels,data,colormap,ncolors,width,height,256)){
342     if(fast){
343       fxfsquantize(pixels,data,colormap,ncolors,width,height,256);
344       }
345     else{
346       fxwuquantize(pixels,data,colormap,ncolors,width,height,256);
347       }
348     }
349 
350   FXASSERT(ncolors<=256);
351 
352   // How many characters needed to represent one pixel, characters per line
353   cpp=(ncolors>MAXPRINTABLE)?2:1;
354 
355   // Save header
356   store.save("/* XPM */\nstatic char * image[] = {\n",36);
357 
358   // Save values
359   len=sprintf(buffer,"\"%d %d %d %d\",\n",width,height,ncolors,cpp);
360   store.save(buffer,len);
361 
362   // Save the colors
363   for(i=0; i<ncolors; i++){
364     color=colormap[i];
365     c1=printable[i%MAXPRINTABLE];
366     c2=printable[i/MAXPRINTABLE];
367     if(FXALPHAVAL(color)){
368       len=sprintf(buffer,"\"%c%c c #%02x%02x%02x\",\n",c1,c2,FXREDVAL(color),FXGREENVAL(color),FXBLUEVAL(color));
369       store.save(buffer,len);
370       }
371     else{
372       len=sprintf(buffer,"\"%c%c c None\",\n",c1,c2);
373       store.save(buffer,len);
374       }
375     }
376 
377   // Save the image
378   ptr=pixels;
379   for(i=0; i<height; i++){
380     store << quote;
381     for(j=0; j<width; j++){
382       pix=*ptr++;
383       if(cpp==1){
384         store << printable[pix];
385         }
386       else{
387         store << printable[pix%MAXPRINTABLE];
388         store << printable[pix/MAXPRINTABLE];
389         }
390       }
391     store << quote;
392     if(i<height-1){ store << comma; store << newline; }
393     }
394   store.save("};\n",3);
395   FXFREE(&pixels);
396   return TRUE;
397   }
398 
399 }
400 
401