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