1 /********************************************************************************
2 *                                                                               *
3 *                            V i s u a l   C l a s s                            *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1999,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: FXVisual.cpp,v 1.73.2.1 2005/02/12 06:46:19 fox Exp $                        *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "FXHash.h"
28 #include "FXThread.h"
29 #include "FXStream.h"
30 #include "FXString.h"
31 #include "FXSize.h"
32 #include "FXPoint.h"
33 #include "FXRectangle.h"
34 #include "FXRegistry.h"
35 #include "FXAccelTable.h"
36 #include "FXObjectList.h"
37 #include "FXApp.h"
38 #include "FXVisual.h"
39 
40 
41 /*
42   Notes:
43 
44   - Purpose is to provide the illusion of having True Color on all systems.
45   - The visual is what determines how an RGB tuple maps to a color on the device.
46   - Thus, the visual also knows the colormap, and perhaps other lookup mechanisms.
47   - When a drawable is created, it is created with a certain visual.
48   - When painting, graphic contexts are specific for a certain visual; hence,
49     the visual should probably keep track of the gc's.
50   - FIRST set up pseudo color ramp, then read it back and set up the tables.
51     This way, table setup can be reused for read-only colormaps [StaticColor]
52     also...
53   - We try to match the deepest visual not deeper than the specified depth.
54   - For some info on visual setup, see:
55 
56       http://www.wolfram.com/~cwikla/articles/txa/visual.html
57       http://www.wolfram.com/~cwikla/articles/txa/xcc.1.html
58       http://www.wolfram.com/~cwikla/articles/txa/xcc.2.html
59 
60   - Freshly constructed FXVisual sets maxcolors to 1000000 in anticipation
61     of private colormap. [FXApp however sets the default FXVisual's maximum
62     to a lower value as the colormap is shared between lots of apps].
63   - Always check for Standard Colormap first [even for default colormap], as
64     that (a) makes initialization simpler, and (b) may give us the preferred
65     colors to grab on that system [*** Too bad this does not work! ***].
66   - Find closest depth to the given depth hint.
67   - RGB Ordering:
68 
69        RGB 111      > | R  G  B
70        BGR 000      --+--------
71        RBG 110      R | x  4  2
72        GBR 001      G |    x  1
73        BRG 100      B |       x
74        GRB 011
75 
76   - Just because I always forget:
77 
78       StaticGray   0
79       GrayScale    1
80       StaticColor  2
81       PseudoColor  3
82       TrueColor    4
83       DirectColor  5
84 
85   - SGI Infinite Reality may have up to 12 bits for red, green, blue each!
86 
87   - With the "depth" we mean number of significant bits per pixel, i.e. padding
88     is not included.
89 */
90 
91 
92 #define DISPLAY(app) ((Display*)((app)->display))
93 
94 
95 // Maximum size of the colormap; for high-end graphics systems
96 // you may want to define HIGHENDGRAPHICS to allow large colormaps
97 #ifdef HIGHENDGRAPHICS
98 #define MAX_MAPSIZE 4096
99 #else
100 #define MAX_MAPSIZE 256
101 #endif
102 
103 using namespace FX;
104 
105 /*******************************************************************************/
106 
107 namespace FX {
108 
109 // Object implementation
110 FXIMPLEMENT(FXVisual,FXId,NULL,0)
111 
112 
113 // Deserialization
FXVisual()114 FXVisual::FXVisual(){
115   flags=0;
116   hint=1;
117   depth=0;
118   numcolors=0;
119   numred=0;
120   numgreen=0;
121   numblue=0;
122   maxcolors=1000000;
123   type=VISUALTYPE_UNKNOWN;
124   info=NULL;
125   colormap=0;
126   freemap=FALSE;
127 #ifndef WIN32
128   visual=NULL;
129   gc=0;
130   scrollgc=0;
131 #else
132   pixelformat=0;
133 #endif
134   }
135 
136 
137 // Construct
FXVisual(FXApp * a,FXuint flgs,FXuint d)138 FXVisual::FXVisual(FXApp* a,FXuint flgs,FXuint d):FXId(a){
139   FXTRACE((100,"FXVisual::FXVisual %p\n",this));
140   flags=flgs;
141   hint=FXMAX(1,d);
142   depth=0;
143   numcolors=0;
144   numred=0;
145   numgreen=0;
146   numblue=0;
147   maxcolors=1000000;
148   type=VISUALTYPE_UNKNOWN;
149   info=NULL;
150   colormap=0;
151   freemap=FALSE;
152 #ifndef WIN32
153   visual=NULL;
154   gc=0;
155   scrollgc=0;
156 #else
157   pixelformat=0;
158 #endif
159   }
160 
161 
162 #ifndef WIN32
163 
164 /*******************************************************************************/
165 
166 // X11 Internal helper functions
167 
168 
169 // Standard dither kernel
170 static const FXint dither[16]={
171    0*16,  8*16,  2*16, 10*16,
172   12*16,  4*16, 14*16,  6*16,
173    3*16, 11*16,  1*16,  9*16,
174   15*16,  7*16, 13*16,  5*16,
175   };
176 
177 
178 // Find shift amount
findshift(FXPixel mask)179 static inline FXuint findshift(FXPixel mask){
180   register FXuint sh=0;
181   while(!(mask&(1<<sh))) sh++;
182   return sh;
183   }
184 
185 
186 // Apply gamma correction to an intensity value in [0..max].
gamma_adjust(FXdouble gamma,FXuint value,FXuint max)187 static FXuint gamma_adjust(FXdouble gamma,FXuint value,FXuint max){
188   register FXdouble x=(FXdouble)value / (FXdouble)max;
189   return (FXuint) (((FXdouble)max * pow(x,1.0/gamma))+0.5);
190   }
191 
192 
193 // Apparently, fabs() gives trouble on HP-UX aCC compiler
fxabs(double a)194 static inline double fxabs(double a){ return a<0 ? -a : a; }
195 
196 
197 // Setup for true color
setuptruecolor()198 void FXVisual::setuptruecolor(){
199   register FXuint  redshift,greenshift,blueshift;
200   register FXPixel redmask,greenmask,bluemask;
201   register FXPixel redmax,greenmax,bluemax;
202   register FXuint i,c,d,r,g,b;
203   register FXdouble gamma;
204 
205   // Get gamma
206   gamma=getApp()->reg().readRealEntry("SETTINGS","displaygamma",1.0);
207 
208   // Arrangement of pixels
209   redmask=((Visual*)visual)->red_mask;
210   greenmask=((Visual*)visual)->green_mask;
211   bluemask=((Visual*)visual)->blue_mask;
212   redshift=findshift(redmask);
213   greenshift=findshift(greenmask);
214   blueshift=findshift(bluemask);
215   redmax=redmask>>redshift;
216   greenmax=greenmask>>greenshift;
217   bluemax=bluemask>>blueshift;
218   numred=redmax+1;
219   numgreen=greenmax+1;
220   numblue=bluemax+1;
221   numcolors=numred*numgreen*numblue;
222 
223   // Make dither tables
224   for(d=0; d<16; d++){
225     for(i=0; i<256; i++){
226       c=gamma_adjust(gamma,i,255);
227       r=(redmax*c+dither[d])/255;
228       g=(greenmax*c+dither[d])/255;
229       b=(bluemax*c+dither[d])/255;
230       rpix[d][i]=r << redshift;
231       gpix[d][i]=g << greenshift;
232       bpix[d][i]=b << blueshift;
233       }
234     }
235 
236   // What did we get
237   FXTRACE((150,"True color:\n"));
238   FXTRACE((150,"  visual id    = 0x%02lx\n",((Visual*)visual)->visualid));
239   FXTRACE((150,"  depth        = %d\n",depth));
240   FXTRACE((150,"  gamma        = %6f\n",gamma));
241   FXTRACE((150,"  map_entries  = %d\n",((Visual*)visual)->map_entries));
242   FXTRACE((150,"  numcolors    = %d\n",numcolors));
243   FXTRACE((150,"  BitOrder     = %s\n",(BitmapBitOrder(DISPLAY(getApp()))==MSBFirst)?"MSBFirst":"LSBFirst"));
244   FXTRACE((150,"  ByteOrder    = %s\n",(ImageByteOrder(DISPLAY(getApp()))==MSBFirst)?"MSBFirst":"LSBFirst"));
245   FXTRACE((150,"  Padding      = %d\n",BitmapPad(DISPLAY(getApp()))));
246   FXTRACE((150,"  redmax       = %3ld; redmask   =%08lx; redshift   = %-2d\n",redmax,redmask,redshift));
247   FXTRACE((150,"  greenmax     = %3ld; greenmask =%08lx; greenshift = %-2d\n",greenmax,greenmask,greenshift));
248   FXTRACE((150,"  bluemax      = %3ld; bluemask  =%08lx; blueshift  = %-2d\n",bluemax,bluemask,blueshift));
249 
250   // Set type
251   type=VISUALTYPE_TRUE;
252   }
253 
254 
255 // Setup direct color
setupdirectcolor()256 void FXVisual::setupdirectcolor(){
257   register FXuint  redshift,greenshift,blueshift;
258   register FXPixel redmask,greenmask,bluemask;
259   register FXPixel redmax,greenmax,bluemax;
260   register FXuint  mapsize,maxcols,i,j,r,g,b,emax,rr,gg,bb,rm,gm,bm,d;
261   register FXuint  bestmatchr,bestmatchg,bestmatchb;
262   register FXdouble mindist,dist,gamma;
263   register FXbool gottable,allocedcolor;
264   XColor *table,color;
265   FXPixel *alloced;
266 
267   // Get gamma
268   gamma=getApp()->reg().readRealEntry("SETTINGS","displaygamma",1.0);
269 
270   // Get map size
271   mapsize=((Visual*)visual)->map_entries;
272 
273   // Arrangement of pixels
274   redmask=((Visual*)visual)->red_mask;
275   greenmask=((Visual*)visual)->green_mask;
276   bluemask=((Visual*)visual)->blue_mask;
277   redshift=findshift(redmask);
278   greenshift=findshift(greenmask);
279   blueshift=findshift(bluemask);
280   redmax=redmask>>redshift;
281   greenmax=greenmask>>greenshift;
282   bluemax=bluemask>>blueshift;
283 
284   rm=redmax;
285   gm=greenmax;
286   bm=bluemax;
287 
288   // Maximum number of colors to allocate
289   maxcols=FXMIN(maxcolors,mapsize);
290 
291   // No more allocations than allowed
292   if(redmax>=maxcols) redmax=maxcols-1;
293   if(greenmax>=maxcols) greenmax=maxcols-1;
294   if(bluemax>=maxcols) bluemax=maxcols-1;
295 
296   numred=redmax+1;
297   numgreen=greenmax+1;
298   numblue=bluemax+1;
299   numcolors=numred*numgreen*numblue;
300   emax=FXMAX3(redmax,greenmax,bluemax);
301 
302   gottable=0;
303 
304   // Allocate color table
305   FXMALLOC(&table,XColor,mapsize);
306   FXMALLOC(&alloced,FXPixel,mapsize);
307 
308   // Allocate ramp
309   for(i=r=g=b=0; i<=emax; i++){
310 
311     // We try to get gamma-corrected colors
312     color.red=gamma_adjust(gamma,(r*65535)/redmax,65535);
313     color.green=gamma_adjust(gamma,(g*65535)/greenmax,65535);
314     color.blue=gamma_adjust(gamma,(b*65535)/bluemax,65535);
315     color.flags=DoRed|DoGreen|DoBlue;
316 
317     // First try just using XAllocColor
318     allocedcolor=XAllocColor(DISPLAY(getApp()),colormap,&color);
319     if(!allocedcolor){
320 
321       // Get colors in the map
322       if(!gottable){
323         rr=0;
324         gg=0;
325         bb=0;
326         for(j=0; j<mapsize; j++){
327           table[j].pixel=(rr<<redshift) | (gg<<greenshift) | (bb<<blueshift);
328           table[j].flags=DoRed|DoGreen|DoBlue;
329           if(rr<redmax) rr++;
330           if(gg<greenmax) gg++;
331           if(bb<bluemax) bb++;
332           }
333         XQueryColors(DISPLAY(getApp()),colormap,table,mapsize);
334         gottable=1;
335         }
336 
337       // Find best match for red
338       for(mindist=1.0E10,bestmatchr=0,j=0; j<mapsize; j++){
339         dist=fxabs(color.red-table[j].red);
340         if(dist<mindist){ bestmatchr=j; mindist=dist; if(mindist==0.0) break; }
341         }
342 
343       // Find best match for green
344       for(mindist=1.0E10,bestmatchg=0,j=0; j<mapsize; j++){
345         dist=fxabs(color.green-table[j].green);
346         if(dist<mindist){ bestmatchg=j; mindist=dist; if(mindist==0.0) break; }
347         }
348 
349       // Find best match for blue
350       for(mindist=1.0E10,bestmatchb=0,j=0; j<mapsize; j++){
351         dist=fxabs(color.blue-table[j].blue);
352         if(dist<mindist){ bestmatchb=j; mindist=dist; if(mindist==0.0) break; }
353         }
354 
355       // Now try to allocate this color
356       color.red=table[bestmatchr].red;
357       color.green=table[bestmatchg].green;
358       color.blue=table[bestmatchb].blue;
359 
360       // Try to allocate the closest match color.  This should only
361       // fail if the cell is read/write.  Otherwise, we're incrementing
362       // the cell's reference count.
363       allocedcolor=XAllocColor(DISPLAY(getApp()),colormap,&color);
364       if(!allocedcolor){
365         color.red=table[bestmatchr].red;
366         color.green=table[bestmatchg].green;
367         color.blue=table[bestmatchb].blue;
368         color.pixel=(table[bestmatchr].pixel&redmask) | (table[bestmatchg].pixel&greenmask) | (table[bestmatchb].pixel&bluemask);
369         }
370       }
371 
372     FXTRACE((200,"Alloc %3d %3d %3d (%6d %6d %6d) pixel=%08lx\n",r,g,b,color.red,color.green,color.blue,color.pixel));
373 
374     alloced[i]=color.pixel;
375 
376     if(r<redmax) r++;
377     if(g<greenmax) g++;
378     if(b<bluemax) b++;
379     }
380 
381   // Fill dither tables
382   for(d=0; d<16; d++){
383     for(i=0; i<256; i++){
384       rpix[d][i]=alloced[((redmax*i+dither[d])/255)]&redmask;
385       gpix[d][i]=alloced[((greenmax*i+dither[d])/255)]&greenmask;
386       bpix[d][i]=alloced[((bluemax*i+dither[d])/255)]&bluemask;
387       }
388     }
389 
390   // Free table
391   FXFREE(&table);
392   FXFREE(&alloced);
393 
394   // What did we get
395   FXTRACE((150,"Direct color:\n"));
396   FXTRACE((150,"  visual id    = 0x%02lx\n",((Visual*)visual)->visualid));
397   FXTRACE((150,"  depth        = %d\n",depth));
398   FXTRACE((150,"  gamma        = %6f\n",gamma));
399   FXTRACE((150,"  map_entries  = %d\n",mapsize));
400   FXTRACE((150,"  numcolors    = %d\n",numcolors));
401   FXTRACE((150,"  redmax       = %3ld; redmask   =%08lx; redshift   = %-2d\n",redmax,redmask,redshift));
402   FXTRACE((150,"  greenmax     = %3ld; greenmask =%08lx; greenshift = %-2d\n",greenmax,greenmask,greenshift));
403   FXTRACE((150,"  bluemax      = %3ld; bluemask  =%08lx; blueshift  = %-2d\n",bluemax,bluemask,blueshift));
404 
405   // Set type
406   type=VISUALTYPE_TRUE;
407   }
408 
409 
410 // Setup for pseudo color
setuppseudocolor()411 void FXVisual::setuppseudocolor(){
412   register FXuint r,g,b,mapsize,bestmatch,maxcols,i,d;
413   register FXdouble mindist,dist,gamma,dr,dg,db;
414   register FXPixel redmax,greenmax,bluemax;
415   register FXbool gottable,allocedcolor;
416   XColor table[256],color;
417 
418   // Get gamma
419   gamma=getApp()->reg().readRealEntry("SETTINGS","displaygamma",1.0);
420 
421   // Get map size
422   mapsize=((Visual*)visual)->map_entries;
423   if(mapsize>256) mapsize=256;
424 
425   // How many colors to allocate
426   maxcols=FXMIN(maxcolors,mapsize);
427 
428   // Find a product of r*g*b which will fit the available map.
429   // We prefer b+1>=g and g>=r>=b; start with 6x7x6 or 252 colors.
430   numred=6;
431   numgreen=7;
432   numblue=6;
433   while(numred*numgreen*numblue>maxcols){
434     if(numblue==numred && numblue==numgreen) numblue--;
435     else if(numred==numgreen) numred--;
436     else numgreen--;
437     }
438 
439   // We want at most maxcols colors
440   numcolors=numred*numgreen*numblue;
441   redmax=numred-1;
442   greenmax=numgreen-1;
443   bluemax=numblue-1;
444   gottable=0;
445 
446   // Allocate color ramp
447   for(r=0; r<numred; r++){
448     for(g=0; g<numgreen; g++){
449       for(b=0; b<numblue; b++){
450 
451         // We try to get gamma-corrected colors
452         color.red=gamma_adjust(gamma,(r*65535)/redmax,65535);
453         color.green=gamma_adjust(gamma,(g*65535)/greenmax,65535);
454         color.blue=gamma_adjust(gamma,(b*65535)/bluemax,65535);
455         color.flags=DoRed|DoGreen|DoBlue;
456 
457         // First try just using XAllocColor
458         allocedcolor=XAllocColor(DISPLAY(getApp()),colormap,&color);
459         if(!allocedcolor){
460 
461           // Get colors in the map
462           if(!gottable){
463             for(i=0; i<mapsize; i++){
464               table[i].pixel=i;
465               table[i].flags=DoRed|DoGreen|DoBlue;
466               }
467             XQueryColors(DISPLAY(getApp()),colormap,table,mapsize);
468             gottable=1;
469             }
470 
471           // Find best match
472           for(mindist=1.0E10,bestmatch=0,i=0; i<mapsize; i++){
473             dr=color.red-table[i].red;
474             dg=color.green-table[i].green;
475             db=color.blue-table[i].blue;
476             dist=dr*dr+dg*dg+db*db;
477             if(dist<mindist){
478               bestmatch=i;
479               mindist=dist;
480               if(mindist==0.0) break;
481               }
482             }
483 
484           // Return result
485           color.red=table[bestmatch].red;
486           color.green=table[bestmatch].green;
487           color.blue=table[bestmatch].blue;
488 
489           // Try to allocate the closest match color.  This should only
490           // fail if the cell is read/write.  Otherwise, we're incrementing
491           // the cell's reference count.
492           allocedcolor=XAllocColor(DISPLAY(getApp()),colormap,&color);
493 
494           // Cell was read/write; we can't use read/write cells as some
495           // other app might change our colors and mess up the display.
496           // However, rumor has it that some X terminals and the Solaris
497           // X server have XAllocColor fail even if we're asking for a
498           // color which is known to be in the table; so we'll use this
499           // color anyway and hope nobody changes it..
500           if(!allocedcolor){
501             color.pixel=bestmatch;
502             color.red=table[bestmatch].red;
503             color.green=table[bestmatch].green;
504             color.blue=table[bestmatch].blue;
505             }
506           }
507 
508         // Remember this color
509         lut[(r*numgreen+g)*numblue+b]=color.pixel;
510         }
511       }
512     }
513 
514   // Set up dither table
515   for(d=0; d<16; d++){
516     for(i=0; i<256; i++){
517       r=(redmax*i+dither[d])/255;
518       g=(greenmax*i+dither[d])/255;
519       b=(bluemax*i+dither[d])/255;
520       rpix[d][i]=r*numgreen*numblue;
521       gpix[d][i]=g*numblue;
522       bpix[d][i]=b;
523       }
524     }
525 
526   // What did we get
527   FXTRACE((150,"Pseudo color display:\n"));
528   FXTRACE((150,"  visual id    = 0x%02lx\n",((Visual*)visual)->visualid));
529   FXTRACE((150,"  depth        = %d\n",depth));
530   FXTRACE((150,"  gamma        = %6f\n",gamma));
531   FXTRACE((150,"  map_entries  = %d\n",mapsize));
532   FXTRACE((150,"  numcolors    = %d\n",numcolors));
533   FXTRACE((150,"  redmax       = %ld\n",redmax));
534   FXTRACE((150,"  greenmax     = %ld\n",greenmax));
535   FXTRACE((150,"  bluemax      = %ld\n",bluemax));
536 
537   // Set type
538   type=VISUALTYPE_INDEX;
539   }
540 
541 
542 // Setup for static color
setupstaticcolor()543 void FXVisual::setupstaticcolor(){
544   register FXuint mapsize,bestmatch,i,nr,ng,nb,r,g,b,j,d;
545   register FXdouble mindist,dist,gamma,dr,dg,db;
546   register FXPixel redmax,greenmax,bluemax;
547   FXbool rcnt[256],gcnt[256],bcnt[256];
548   XColor table[256],color;
549 
550   // Get gamma
551   gamma=getApp()->reg().readRealEntry("SETTINGS","displaygamma",1.0);
552   mapsize=((Visual*)visual)->map_entries;
553   if(mapsize>256) mapsize=256;
554 
555   // Read back table
556   for(i=0; i<mapsize; i++) table[i].pixel=i;
557   XQueryColors(DISPLAY(getApp()),colormap,table,mapsize);
558 
559   // How many shades of r,g,b do we have?
560   for(i=0; i<256; i++){ rcnt[i]=gcnt[i]=bcnt[i]=0; }
561   for(i=0; i<mapsize; i++){
562     rcnt[table[i].red/257]=1;
563     gcnt[table[i].green/257]=1;
564     bcnt[table[i].blue/257]=1;
565     }
566   nr=ng=nb=0;
567   for(i=0; i<256; i++){
568     if(rcnt[i]) nr++;
569     if(gcnt[i]) ng++;
570     if(bcnt[i]) nb++;
571     }
572   FXTRACE((200,"nr=%3d ng=%3d nb=%3d\n",nr,ng,nb));
573 
574   // Limit to a reasonable table size
575   if(nr*ng*nb>4096){
576     numred=16;
577     numgreen=16;
578     numblue=16;
579     }
580   else{
581     numred=nr;
582     numgreen=ng;
583     numblue=nb;
584     }
585 
586   numcolors=numred*numgreen*numblue;
587   redmax=numred-1;
588   greenmax=numgreen-1;
589   bluemax=numblue-1;
590 
591   // Allocate color ramp
592   for(r=0; r<numred; r++){
593     for(g=0; g<numgreen; g++){
594       for(b=0; b<numblue; b++){
595 
596         // Color to get
597         color.red=gamma_adjust(gamma,(r*65535)/redmax,65535);
598         color.green=gamma_adjust(gamma,(g*65535)/greenmax,65535);
599         color.blue=gamma_adjust(gamma,(b*65535)/bluemax,65535);
600 
601         // Find best match
602         for(mindist=1.0E10,bestmatch=0,j=0; j<mapsize; j++){
603           dr=(color.red-table[j].red);
604           dg=(color.green-table[j].green);
605           db=(color.blue-table[j].blue);
606           dist=dr*dr+dg*dg+db*db;
607           if(dist<mindist){
608             bestmatch=j;
609             mindist=dist;
610             if(mindist==0.0) break;
611             }
612           }
613 
614         // Add color into table
615         lut[(r*numgreen+g)*numblue+b]=table[bestmatch].pixel;
616         }
617       }
618     }
619 
620   // Set up dither table
621   for(d=0; d<16; d++){
622     for(i=0; i<256; i++){
623       r=(redmax*i+dither[d])/255;
624       g=(greenmax*i+dither[d])/255;
625       b=(bluemax*i+dither[d])/255;
626       rpix[d][i]=r*numgreen*numblue;
627       gpix[d][i]=g*numblue;
628       bpix[d][i]=b;
629       }
630     }
631 
632   // What did we get
633   FXTRACE((150,"Static color:\n"));
634   FXTRACE((150,"  visual id    = 0x%02lx\n",((Visual*)visual)->visualid));
635   FXTRACE((150,"  depth        = %d\n",depth));
636   FXTRACE((150,"  gamma        = %6f\n",gamma));
637   FXTRACE((150,"  map_entries  = %d\n",mapsize));
638   FXTRACE((150,"  numcolors    = %d\n",numcolors));
639   FXTRACE((150,"  redmax       = %ld\n",redmax));
640   FXTRACE((150,"  greenmax     = %ld\n",greenmax));
641   FXTRACE((150,"  bluemax      = %ld\n",bluemax));
642 
643   // Set type
644   type=VISUALTYPE_INDEX;
645   }
646 
647 
648 // Setup for gray scale
setupgrayscale()649 void FXVisual::setupgrayscale(){
650   register FXuint g,bestmatch,mapsize,maxcols,graymax,i,d;
651   register FXdouble mindist,dist,gamma,dr,dg,db;
652   register FXbool gottable,allocedcolor;
653   XColor table[256],color;
654   FXPixel alloced[256];
655 
656   // Get gamma
657   gamma=getApp()->reg().readRealEntry("SETTINGS","displaygamma",1.0);
658 
659   // Get map size
660   mapsize=((Visual*)visual)->map_entries;
661   if(mapsize>256) mapsize=256;
662 
663   // How many to allocate
664   maxcols=FXMIN(mapsize,maxcolors);
665 
666   // Colors
667   numcolors=maxcols;
668   graymax=numcolors-1;
669   gottable=0;
670 
671   // Allocate gray ramp
672   for(g=0; g<numcolors; g++){
673 
674     // We try to allocate gamma-corrected colors!
675     color.red=color.green=color.blue=gamma_adjust(gamma,(g*65535)/graymax,65535);
676     color.flags=DoRed|DoGreen|DoBlue;
677 
678     // First try just using XAllocColor
679     allocedcolor=XAllocColor(DISPLAY(getApp()),colormap,&color);
680     if(!allocedcolor){
681 
682       // Get colors in the map
683       if(!gottable){
684         for(i=0; i<mapsize; i++){
685           table[i].pixel=i;
686           table[i].flags=DoRed|DoGreen|DoBlue;
687           }
688         XQueryColors(DISPLAY(getApp()),colormap,table,mapsize);
689         gottable=1;
690         }
691 
692       // Find best match
693       for(mindist=1.0E10,bestmatch=0,i=0; i<mapsize; i++){
694         dr=color.red-table[i].red;
695         dg=color.green-table[i].green;
696         db=color.blue-table[i].blue;
697         dist=dr*dr+dg*dg+db*db;
698         if(dist<mindist){
699           bestmatch=i;
700           mindist=dist;
701           if(mindist==0.0) break;
702           }
703         }
704 
705       // Return result
706       color.red=table[bestmatch].red;
707       color.green=table[bestmatch].green;
708       color.blue=table[bestmatch].blue;
709 
710       // Try to allocate the closest match color.  This should only
711       // fail if the cell is read/write.  Otherwise, we're incrementing
712       // the cell's reference count.
713       allocedcolor=XAllocColor(DISPLAY(getApp()),colormap,&color);
714 
715       // Cell was read/write; we can't use read/write cells as some
716       // other app might change our colors and mess up the display.
717       // However, rumor has it that some X terminals and the Solaris
718       // X server have XAllocColor fail even if we're asking for a
719       // color which is known to be in the table; so we'll use this
720       // color anyway and hope nobody changes it..
721       if(!allocedcolor){
722         color.pixel=bestmatch;
723         color.red=table[bestmatch].red;
724         color.green=table[bestmatch].green;
725         color.blue=table[bestmatch].blue;
726         }
727       }
728 
729     // Keep track
730     alloced[g]=color.pixel;
731     }
732 
733   // Set up color ramps
734   for(d=0; d<16; d++){
735     for(i=0; i<256; i++){
736       rpix[d][i]=gpix[d][i]=bpix[d][i]=alloced[(graymax*i+dither[d])/255];
737       }
738     }
739 
740   // What did we get
741   FXTRACE((150,"Gray Scale:\n"));
742   FXTRACE((150,"  visual id    = 0x%02lx\n",((Visual*)visual)->visualid));
743   FXTRACE((150,"  depth        = %d\n",depth));
744   FXTRACE((150,"  gamma        = %6f\n",gamma));
745   FXTRACE((150,"  map_entries  = %d\n",mapsize));
746   FXTRACE((150,"  numcolors    = %d\n",numcolors));
747   FXTRACE((150,"  graymax      = %d\n",graymax));
748 
749   // Set type
750   type=VISUALTYPE_GRAY;
751   }
752 
753 
754 // Setup for static gray
setupstaticgray()755 void FXVisual::setupstaticgray(){
756   register FXuint i,d,c,graymax;
757   register FXdouble gamma;
758 
759   // Get gamma
760   gamma=getApp()->reg().readRealEntry("SETTINGS","displaygamma",1.0);
761 
762   // Number of colors
763   numcolors=((Visual*)visual)->map_entries;
764   graymax=(numcolors-1);
765 
766   // Set up color ramps
767   for(d=0; d<16; d++){
768     for(i=0; i<256; i++){
769       c=gamma_adjust(gamma,i,255);
770       rpix[d][i]=gpix[d][i]=bpix[d][i]=(graymax*c+dither[d])/255;
771       }
772     }
773 
774   // What did we get
775   FXTRACE((150,"Static Gray:\n"));
776   FXTRACE((150,"  visual id    = 0x%02lx\n",((Visual*)visual)->visualid));
777   FXTRACE((150,"  depth        = %d\n",depth));
778   FXTRACE((150,"  gamma        = %6f\n",gamma));
779   FXTRACE((150,"  map_entries  = %d\n",((Visual*)visual)->map_entries));
780   FXTRACE((150,"  numcolors    = %d\n",numcolors));
781   FXTRACE((150,"  graymax      = %d\n",graymax));
782 
783   type=VISUALTYPE_GRAY;
784   }
785 
786 
787 // Setup for pixmap monochrome; this one has no colormap!
setuppixmapmono()788 void FXVisual::setuppixmapmono(){
789   register FXuint d,i,c;
790   register FXdouble gamma;
791 
792   // Get gamma
793   gamma=getApp()->reg().readRealEntry("SETTINGS","displaygamma",1.0);
794 
795   // Number of colors
796   numcolors=2;
797 
798   // Set up color ramps
799   for(d=0; d<16; d++){
800     for(i=0; i<256; i++){
801       c=gamma_adjust(gamma,i,255);
802       rpix[d][i]=gpix[d][i]=bpix[d][i]=(c+dither[d])/255;
803       }
804     }
805 
806   // What did we get
807   FXTRACE((150,"Pixmap monochrome:\n"));
808   FXTRACE((150,"  depth        = %d\n",depth));
809   FXTRACE((150,"  gamma        = %6f\n",gamma));
810   FXTRACE((150,"  map_entries  = %d\n",2));
811   FXTRACE((150,"  numcolors    = %d\n",2));
812   FXTRACE((150,"  black        = 0\n"));
813   FXTRACE((150,"  white        = 1\n"));
814 
815   // Set type
816   type=VISUALTYPE_MONO;
817   }
818 
819 
820 /*
821 // Try determine standard colormap
822 static FXbool getstdcolormap(Display *dpy,VisualID visualid,XStandardColormap& map){
823   XStandardColormap *stdmaps=NULL;
824   int status,count,i;
825   status=XGetRGBColormaps(dpy,RootWindow(dpy,DefaultScreen(dpy)),&stdmaps,&count,XA_RGB_DEFAULT_MAP);
826   if(status){
827     status=FALSE;
828     for(i=0; i<count; i++){
829       FXTRACE((150,"Standarn XA_RGB_DEFAULT_MAP map #%d:\n",i));
830       FXTRACE((150,"  colormap   = %ld\n",stdmaps[i].colormap));
831       FXTRACE((150,"  red_max    = %ld  red_mult   = %ld\n",stdmaps[i].red_max,stdmaps[i].red_mult));
832       FXTRACE((150,"  green_max  = %ld  green_mult = %ld\n",stdmaps[i].green_max,stdmaps[i].green_mult));
833       FXTRACE((150,"  blue_max   = %ld  blue_mult  = %ld\n",stdmaps[i].blue_max,stdmaps[i].blue_mult));
834       FXTRACE((150,"  base pixel = %ld\n",stdmaps[i].base_pixel));
835       FXTRACE((150,"  visualid   = 0x%02lx\n",stdmaps[i].visualid));
836       FXTRACE((150,"  killid     = %ld\n",stdmaps[i].killid));
837       if(stdmaps[i].visualid==visualid){
838         FXTRACE((150,"  Matched\n"));
839         map=stdmaps[i];
840         status=TRUE;
841         break;
842         }
843       }
844     }
845   if(stdmaps) XFree(stdmaps);
846   return status;
847   }
848 */
849 
850 
851 // Determine colormap, then initialize it
setupcolormap()852 void FXVisual::setupcolormap(){
853   //XStandardColormap stdmap;
854   if(flags&VISUAL_MONOCHROME){
855     colormap=None;
856     FXTRACE((150,"%s::create: need no colormap\n",getClassName()));
857     setuppixmapmono();
858     }
859   else{
860     if((flags&VISUAL_OWNCOLORMAP) || (visual!=DefaultVisual(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp()))))){
861       colormap=XCreateColormap(DISPLAY(getApp()),RootWindow(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp()))),((Visual*)visual),AllocNone);
862       FXTRACE((150,"%s::create: allocate colormap\n",getClassName()));
863       freemap=TRUE;
864       }
865     else{
866       //getstdcolormap(DISPLAY(getApp()),((Visual*)visual)->visualid,stdmap);
867       colormap=DefaultColormap(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp())));
868       FXTRACE((150,"%s::create: use default colormap\n",getClassName()));
869       }
870     switch(((Visual*)visual)->c_class){
871       case TrueColor:   setuptruecolor(); break;
872       case DirectColor: setupdirectcolor(); break;
873       case PseudoColor: setuppseudocolor(); break;
874       case StaticColor: setupstaticcolor(); break;
875       case GrayScale:   setupgrayscale(); break;
876       case StaticGray:  setupstaticgray(); break;
877       }
878     }
879   }
880 
881 
882 // Make GC for given visual and depth; graphics exposures optional
setupgc(FXbool gex)883 void* FXVisual::setupgc(FXbool gex){
884   XGCValues gval;
885   FXID drawable;
886   GC gg;
887 
888   gval.fill_style=FillSolid;
889   gval.graphics_exposures=gex;
890 
891   // For default visual; this is easy as we already have a matching window
892   if((Visual*)visual==DefaultVisual(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp())))){
893     gg=XCreateGC(DISPLAY(getApp()),XDefaultRootWindow(DISPLAY(getApp())),GCFillStyle|GCGraphicsExposures,&gval);
894     }
895 
896   // For arbitrary visual; create a temporary pixmap of the same depth as the visual
897   else{
898     drawable=XCreatePixmap(DISPLAY(getApp()),XDefaultRootWindow(DISPLAY(getApp())),1,1,depth);
899     gg=XCreateGC(DISPLAY(getApp()),drawable,GCFillStyle|GCGraphicsExposures,&gval);
900     XFreePixmap(DISPLAY(getApp()),drawable);
901     }
902   return gg;
903   }
904 
905 
906 #else
907 
908 /*******************************************************************************/
909 
910 // WIN32 Internal helper functions
911 
912 
913 struct LOGPALETTE256 {
914   WORD         palVersion;
915   WORD         palNumEntries;
916   PALETTEENTRY palPalEntry[257];
917   };
918 
919 
920 struct BITMAPINFO256 {
921   BITMAPINFOHEADER bmiHeader;
922   DWORD            bmiColors[256];
923   };
924 
925 
926 // Get number of bits in n
findnbits(DWORD n)927 static inline FXuint findnbits(DWORD n){
928   register FXuint nb=0;
929   while(n){nb+=(n&1);n>>=1;}
930   return nb;
931   }
932 
933 
934 // Make palette
createAllPurposePalette()935 static HPALETTE createAllPurposePalette(){
936   LOGPALETTE256 palette;
937   HPALETTE hPalette,hStockPalette;
938   FXint num,r,g,b;
939 
940   // We will use the stock palette
941   hStockPalette=(HPALETTE)GetStockObject(DEFAULT_PALETTE);
942 
943   // Fill in first 20 entries from system color palette
944   num=GetPaletteEntries(hStockPalette,0,20,palette.palPalEntry);
945 
946   // Calculate remaining 216 colors, 8 of which match the standard
947   // 20 colors and 4 of which match the gray shades above
948   for(r=0; r<256; r+=51){
949     for(g=0; g<256; g+=51){
950       for(b=0; b<256; b+=51){
951         palette.palPalEntry[num].peRed=r;
952         palette.palPalEntry[num].peGreen=g;
953         palette.palPalEntry[num].peBlue=b;
954         palette.palPalEntry[num].peFlags=0;
955         num++;
956         }
957       }
958     }
959 
960 
961   // Fill in the rest
962   palette.palVersion=0x300;
963   palette.palNumEntries=num;
964 
965   // Create palette and we're done
966   hPalette=CreatePalette((const LOGPALETTE*)&palette);
967 
968   // Return palette
969   return hPalette;
970   }
971 
972 
973 #endif
974 
975 
976 /*******************************************************************************/
977 
978 
979 // Initialize
create()980 void FXVisual::create(){
981   if(!xid){
982     if(getApp()->isInitialized()){
983       FXTRACE((100,"%s::create %p\n",getClassName(),this));
984 #ifndef WIN32
985       XVisualInfo vitemplate;
986       XVisualInfo *vi;
987       FXint nvi,i,d,dbest;
988 
989       // Assume the default
990       visual=DefaultVisual(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp())));
991       depth=DefaultDepth(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp())));
992 
993       // True color
994       if(flags&VISUAL_TRUECOLOR){
995         vitemplate.screen=DefaultScreen(DISPLAY(getApp()));
996         vi=XGetVisualInfo(DISPLAY(getApp()),VisualScreenMask,&vitemplate,&nvi);
997         if(vi){
998           for(i=0,dbest=1000000; i<nvi; i++){
999             if((vi[i].c_class==DirectColor) || (vi[i].c_class==TrueColor)){
1000               d=vi[i].depth-hint;
1001               if(d<0) d*=-100;         // Strongly prefer >= hint
1002               if(d<dbest){
1003                 dbest=d;
1004                 visual=vi[i].visual;
1005                 depth=vi[i].depth;
1006                 }
1007               }
1008             }
1009           XFree((char*)vi);
1010           }
1011         }
1012 
1013       // Index color
1014       else if(flags&VISUAL_INDEXCOLOR){
1015         vitemplate.screen=DefaultScreen(DISPLAY(getApp()));
1016         vi=XGetVisualInfo(DISPLAY(getApp()),VisualScreenMask,&vitemplate,&nvi);
1017         if(vi){
1018           for(i=0,dbest=1000000; i<nvi; i++){
1019             if((vi[i].c_class==StaticColor) || (vi[i].c_class==PseudoColor)){
1020               d=vi[i].depth-hint;
1021               if(d<0) d*=-100;         // Strongly prefer >= hint
1022               if(d<dbest){
1023                 dbest=d;
1024                 visual=vi[i].visual;
1025                 depth=vi[i].depth;
1026                 }
1027               }
1028             }
1029           XFree((char*)vi);
1030           }
1031         }
1032 
1033       // Gray scale color
1034       else if(flags&VISUAL_GRAYSCALE){
1035         vitemplate.screen=DefaultScreen(DISPLAY(getApp()));
1036         vi=XGetVisualInfo(DISPLAY(getApp()),VisualScreenMask,&vitemplate,&nvi);
1037         if(vi){
1038           for(i=0,dbest=1000000; i<nvi; i++){
1039             if((vi[i].c_class==GrayScale) || (vi[i].c_class==StaticGray)){
1040               d=vi[i].depth-hint;
1041               if(d<0) d*=-100;         // Strongly prefer >= hint
1042               if(d<dbest){
1043                 dbest=d;
1044                 visual=vi[i].visual;
1045                 depth=vi[i].depth;
1046                 }
1047               }
1048             }
1049           XFree((char*)vi);
1050           }
1051         }
1052 
1053       // Get the best (deepest) visual
1054       else if(flags&VISUAL_BEST){
1055         vitemplate.screen=DefaultScreen(DISPLAY(getApp()));
1056         vi=XGetVisualInfo(DISPLAY(getApp()),VisualScreenMask,&vitemplate,&nvi);
1057         if(vi){
1058           for(i=0,dbest=1000000; i<nvi; i++){
1059             d=vi[i].depth-hint;
1060             if(d<0) d*=-100;         // Strongly prefer >= hint
1061             if(d<dbest){
1062               dbest=d;
1063               visual=vi[i].visual;
1064               depth=vi[i].depth;
1065               }
1066             }
1067           XFree((char*)vi);
1068           }
1069         }
1070 
1071       // Monochrome visual (for masks and stipples, not for windows)
1072       else if(flags&VISUAL_MONOCHROME){
1073         numcolors=2;
1074         depth=1;
1075         }
1076 
1077       FXASSERT(visual);
1078 
1079       // Initialize colormap
1080       setupcolormap();
1081 
1082       // Make GC's for this visual
1083       gc=setupgc(FALSE);
1084       scrollgc=setupgc(TRUE);
1085 
1086       xid=1;
1087 #else
1088 
1089       HDC hdc;
1090       HBITMAP hbm;
1091       BITMAPINFO256 bmi;
1092       FXuint redbits,greenbits,bluebits,redmask,greenmask,bluemask;
1093 
1094       // Check for palette support
1095       hdc=GetDC(GetDesktopWindow());
1096 
1097       // Check for palette mode; assume 8-bit for now
1098       if(GetDeviceCaps(hdc,RASTERCAPS)&RC_PALETTE){
1099         colormap=createAllPurposePalette();
1100         depth=8;
1101         numred=6;               // We have a 6x6x6 ramp, at least...
1102         numgreen=6;
1103         numblue=6;
1104         numcolors=256;
1105         type=VISUALTYPE_INDEX;
1106         freemap=TRUE;
1107         }
1108 
1109       // True color mode; find out how deep
1110       else{
1111         memset(&bmi,0,sizeof(BITMAPINFO256));
1112         bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
1113         // Get a device-dependent bitmap that's compatible with the
1114         // screen, then convert the DDB to a DIB.  We need to call GetDIBits
1115         // twice: the first call just fills in the BITMAPINFOHEADER; the
1116         // second fills in the bitfields or palette.
1117         hbm=CreateCompatibleBitmap(hdc,1,1);
1118         GetDIBits(hdc,hbm,0,1,NULL,(LPBITMAPINFO)&bmi,DIB_RGB_COLORS);
1119         GetDIBits(hdc,hbm,0,1,NULL,(LPBITMAPINFO)&bmi,DIB_RGB_COLORS);
1120         DeleteObject(hbm);
1121         if(bmi.bmiHeader.biCompression==BI_BITFIELDS){
1122           redmask=bmi.bmiColors[0];
1123           greenmask=bmi.bmiColors[1];
1124           bluemask=bmi.bmiColors[2];
1125           FXTRACE((150,"redmask   = %08x\n",redmask));
1126           FXTRACE((150,"greenmask = %08x\n",greenmask));
1127           FXTRACE((150,"bluemask  = %08x\n",bluemask));
1128           redbits=findnbits(redmask);
1129           greenbits=findnbits(greenmask);
1130           bluebits=findnbits(bluemask);
1131           numred=1<<redbits;
1132           numgreen=1<<greenbits;
1133           numblue=1<<bluebits;
1134           depth=redbits+greenbits+bluebits;
1135           numcolors=numred*numgreen*numblue;
1136           type=VISUALTYPE_TRUE;
1137           }
1138         else{
1139           type=VISUALTYPE_UNKNOWN;
1140           }
1141         }
1142       ReleaseDC(GetDesktopWindow(),hdc);
1143 
1144       FXTRACE((150,"numred          = %d\n",numred));
1145       FXTRACE((150,"numgreen        = %d\n",numgreen));
1146       FXTRACE((150,"numblue         = %d\n",numblue));
1147       FXTRACE((150,"numcolors       = %d\n",numcolors));
1148       FXTRACE((150,"depth           = %d\n",depth));
1149       FXTRACE((150,"type            = %d\n",type));
1150 
1151       // This is just a placeholder
1152       xid=(void*)1;
1153 #endif
1154       }
1155     }
1156   }
1157 
1158 
1159 // Detach visual
detach()1160 void FXVisual::detach(){
1161   if(xid){
1162     FXTRACE((100,"%s::detach %p\n",getClassName(),this));
1163     colormap=0;
1164     freemap=FALSE;
1165     xid=0;
1166     }
1167   }
1168 
1169 
1170 // Destroy visual
destroy()1171 void FXVisual::destroy(){
1172   if(xid){
1173     if(getApp()->isInitialized()){
1174       FXTRACE((100,"%s::destroy %p\n",getClassName(),this));
1175 #ifndef WIN32
1176       if(freemap){XFreeColormap(DISPLAY(getApp()),colormap);}
1177       XFreeGC(DISPLAY(getApp()),(GC)gc);
1178       XFreeGC(DISPLAY(getApp()),(GC)scrollgc);
1179 #else
1180       if(freemap){DeleteObject((HPALETTE)colormap);}
1181 #endif
1182       colormap=0;
1183       freemap=FALSE;
1184       }
1185     xid=0;
1186     }
1187   }
1188 
1189 
1190 // Get pixel value for color
getPixel(FXColor clr)1191 FXPixel FXVisual::getPixel(FXColor clr){
1192 #ifndef WIN32
1193   switch(type){
1194     case VISUALTYPE_TRUE:    return rpix[1][FXREDVAL(clr)] | gpix[1][FXGREENVAL(clr)] | bpix[1][FXBLUEVAL(clr)];
1195     case VISUALTYPE_INDEX:   return lut[rpix[1][FXREDVAL(clr)]+gpix[1][FXGREENVAL(clr)]+bpix[1][FXBLUEVAL(clr)]];
1196     case VISUALTYPE_GRAY:    return gpix[1][(77*FXREDVAL(clr)+151*FXGREENVAL(clr)+29*FXBLUEVAL(clr))>>8];
1197     case VISUALTYPE_MONO:    return gpix[1][(77*FXREDVAL(clr)+151*FXGREENVAL(clr)+29*FXBLUEVAL(clr))>>8];
1198     case VISUALTYPE_UNKNOWN: return 0;
1199     }
1200   return 0;
1201 #else
1202   return PALETTERGB(FXREDVAL(clr),FXGREENVAL(clr),FXBLUEVAL(clr));
1203 #endif
1204   }
1205 
1206 
1207 // Get color value for pixel
getColor(FXPixel pix)1208 FXColor FXVisual::getColor(FXPixel pix){
1209 #ifndef WIN32
1210   XColor color;
1211   color.pixel=pix;
1212   XQueryColor(DISPLAY(getApp()),colormap,&color);
1213   return FXRGB(((color.red+128)/257),((color.green+128)/257),((color.blue+128)/257));
1214 #else
1215   return PALETTEINDEX(pix);
1216 #endif
1217   }
1218 
1219 
1220 // Set maximum number of colors to allocate
setMaxColors(FXuint maxcols)1221 void FXVisual::setMaxColors(FXuint maxcols){
1222   if(xid){ fxerror("%s::setMaxColors: visual already initialized.\n",getClassName()); }
1223   if(maxcols<2) maxcols=2;
1224   maxcolors=maxcols;
1225   }
1226 
1227 
1228 // Save to stream
save(FXStream & store) const1229 void FXVisual::save(FXStream& store) const {
1230   FXId::save(store);
1231   store << flags;
1232   store << depth;
1233   }
1234 
1235 
1236 // Load from stream
load(FXStream & store)1237 void FXVisual::load(FXStream& store){
1238   FXId::load(store);
1239   store >> flags;
1240   store >> depth;
1241   }
1242 
1243 
1244 // Destroy
~FXVisual()1245 FXVisual::~FXVisual(){
1246   FXTRACE((100,"FXVisual::~FXVisual %p\n",this));
1247   destroy();
1248   }
1249 
1250 }
1251