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