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