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