1
/****************************************************************************
3
** This file is based on sources of the Qt GUI Toolkit, used under the terms
4
** of the GNU General Public License version 2 (see the original copyright
6
** All further contributions to this file are (and are required to be)
7
** licensed under the terms of the GNU General Public License as published by
8
** the Free Software Foundation; either version 2 of the License, or
9
** (at your option) any later version.
11
** The original Qt license header follows:
14
** Implementation of QColor class for X11
18
** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
20
** This file is part of the kernel module of the Qt GUI Toolkit.
22
** This file may be distributed under the terms of the Q Public License
23
** as defined by Trolltech AS of Norway and appearing in the file
24
** LICENSE.QPL included in the packaging of this file.
26
** This file may be distributed and/or modified under the terms of the
27
** GNU General Public License version 2 as published by the Free Software
28
** Foundation and appearing in the file LICENSE.GPL included in the
29
** packaging of this file.
31
** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
32
** licenses for Unix/X11 may use this file in accordance with the Qt Commercial
33
** License Agreement provided with the Software.
35
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
36
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
38
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
39
** information about Qt Commercial License Agreements.
40
** See http://www.trolltech.com/qpl/ for QPL licensing information.
41
** See http://www.trolltech.com/gpl/ for GPL licensing information.
43
** Contact info@trolltech.com if any conditions of this licensing are
46
**********************************************************************/
51
//#include "qpaintdevice.h"
52
//#include "qapplication.h"
53
//#include "qapplication_p.h"
54
//#include "qt_x11_p.h"
58
int qt_ncols_option = 216; // used in qcolor_x11.cpp
62
/*****************************************************************************
63
The color dictionary speeds up color allocation significantly for X11.
64
When there are no more colors, QColor::alloc() will set the colors_avail
65
flag to FALSE and try to find the nearest color.
66
NOTE: From deep within the event loop, the colors_avail flag is reset to
67
TRUE (calls the function qt_reset_color_avail()), because some other
68
application might free its colors, thereby making them available for
70
*****************************************************************************/
72
//#include "qintdict.h"
75
uint pix; // allocated pixel value
76
int context; // allocation context
80
typedef QIntDict<QColorData> QColorDict;
81
typedef QIntDictIterator<QColorData> QColorDictIt;
83
static int current_alloc_context = 0; // current color alloc context
84
static const uint col_std_dict = 419;
85
static const uint col_large_dict = 18397;
87
class QColorScreenData {
101
//# QColorDict *colorDict; // dict of allocated colors
102
bool colors_avail; // X colors available
103
bool g_truecolor; // truecolor visual
104
Visual *g_vis; // visual
105
XColor *g_carr; // color array
106
bool g_carr_fetch; // perform XQueryColors?
107
int g_cells; // number of entries in g_carr
108
bool *g_our_alloc; // our allocated colors
109
uint red_mask , green_mask , blue_mask;
110
int red_shift, green_shift, blue_shift;
117
static int screencount = 0;
118
static QColorScreenData **screendata = 0; // array of screendata pointers
122
This function is called from the event loop. It resets the colors_avail
123
flag so that the application can retry to allocate read-only colors
124
that other applications may have deallocated lately.
126
The g_our_alloc and g_carr are global arrays that optimize color
127
approximation when there are no more colors left to allocate.
130
void qt_reset_color_avail()
133
for ( i = 0; i < screencount; i++ ) {
134
screendata[i]->colors_avail = true;
135
screendata[i]->g_carr_fetch = true; // do XQueryColors if !colors_avail
141
Finds the nearest color.
144
static int find_nearest_color( int r, int g, int b, int* mindist_out,
145
QColorScreenData *sd )
148
int mindist = 200000;
149
int rx, gx, bx, dist;
150
XColor *xc = &sd->g_carr[0];
151
for ( int i=0; i<sd->g_cells; i++ ) {
152
rx = r - (xc->red >> 8);
153
gx = g - (xc->green >> 8);
154
bx = b - (xc->blue>> 8);
155
dist = rx*rx + gx*gx + bx*bx; // calculate distance
156
if ( dist < mindist ) { // minimal?
162
*mindist_out = mindist;
167
/*****************************************************************************
168
QColor misc internal functions
169
*****************************************************************************/
171
static int highest_bit( uint v )
174
uint b = (uint)1 << 31; // get pos of highest bit in v
175
for ( i=31; ((b & v) == 0) && i>=0; i-- )
181
/*****************************************************************************
182
QColor static member functions
183
*****************************************************************************/
186
Returns the maximum number of colors supported by the underlying
187
window system if the window system uses a palette.
189
Otherwise returns -1. Use numBitPlanes() to calculate the available
193
int QColor::maxColors()
195
Visual *visual = (Visual *) QPaintDevice::x11AppVisual();
196
if (visual->c_class & 1)
197
return QPaintDevice::x11AppCells();
202
Returns the number of color bit planes for the underlying window
205
The returned value is equal to the default pixmap depth.
207
\sa QPixmap::defaultDepth()
210
int QColor::numBitPlanes()
212
return QPaintDevice::x11AppDepth();
217
Internal initialization required for QColor.
218
This function is called from the QApplication constructor.
223
void QColor::initialize()
225
static const int blackIdx = 2;
226
static const int whiteIdx = 3;
228
if ( color_init ) // already initialized
232
Display *dpy = QPaintDevice::x11AppDisplay();
233
int spec = QApplication::colorSpec();
235
screencount = ScreenCount( dpy );
236
screendata = new QColorScreenData*[ screencount ];
238
int scr = QPaintDevice::x11AppScreen();
240
for ( scr = 0; scr < screencount; ++scr ) {
241
screendata[scr] = new QColorScreenData;
242
screendata[scr]->g_vis = (Visual *) QPaintDevice::x11AppVisual( scr );
243
screendata[scr]->g_truecolor = screendata[scr]->g_vis->c_class == TrueColor;
245
int ncols= QPaintDevice::x11AppCells( scr );
247
if ( screendata[scr]->g_truecolor ) {
251
// Create the g_our_alloc array, which remembers which color pixels
253
screendata[scr]->g_cells = qMin(ncols,256);
254
screendata[scr]->g_carr = new XColor[screendata[scr]->g_cells];
255
Q_CHECK_PTR( screendata[scr]->g_carr );
256
memset( screendata[scr]->g_carr, 0,
257
screendata[scr]->g_cells*sizeof(XColor) );
258
screendata[scr]->g_carr_fetch = true; // run XQueryColors on demand
259
screendata[scr]->g_our_alloc = new bool[screendata[scr]->g_cells];
260
Q_CHECK_PTR( screendata[scr]->g_our_alloc );
261
memset( screendata[scr]->g_our_alloc, false,
262
screendata[scr]->g_cells*sizeof(bool) );
263
XColor *xc = &screendata[scr]->g_carr[0];
264
for ( int i=0; i<screendata[scr]->g_cells; i++ ) {
265
xc->pixel = i; // g_carr[i] = color i
271
if ( screendata[scr]->g_truecolor ) { // truecolor
272
dictsize = 1; // will not need color dict
273
screendata[scr]->red_mask = (uint)screendata[scr]->g_vis->red_mask;
274
screendata[scr]->green_mask = (uint)screendata[scr]->g_vis->green_mask;
275
screendata[scr]->blue_mask = (uint)screendata[scr]->g_vis->blue_mask;
276
screendata[scr]->red_shift =
277
highest_bit( screendata[scr]->red_mask ) - 7;
278
screendata[scr]->green_shift =
279
highest_bit( screendata[scr]->green_mask ) - 7;
280
screendata[scr]->blue_shift =
281
highest_bit( screendata[scr]->blue_mask ) - 7;
283
dictsize = col_std_dict;
286
screendata[scr]->colorDict = new QColorDict(dictsize); // create dictionary
287
Q_CHECK_PTR( screendata[scr]->colorDict );
290
if ( spec == (int)QApplication::ManyColor ) {
291
screendata[scr]->color_reduce = true;
293
switch ( qt_ncols_option ) {
296
screendata[scr]->col_div_r = screendata[scr]->col_div_g =
297
screendata[scr]->col_div_b = (255/(6-1));
300
// 2:3:1 proportions, solved numerically
301
if ( qt_ncols_option > 255 ) qt_ncols_option = 255;
302
if ( qt_ncols_option < 1 ) qt_ncols_option = 1;
307
if ( nb*2 < nr && (nb+1)*nr*ng < qt_ncols_option )
309
else if ( nr*3 < ng*2 && nb*(nr+1)*ng < qt_ncols_option )
311
else if ( nb*nr*(ng+1) < qt_ncols_option )
315
qt_ncols_option = nr*ng*nb;
316
screendata[scr]->col_div_r = (255/(nr-1));
317
screendata[scr]->col_div_g = (255/(ng-1));
318
screendata[scr]->col_div_b = (255/(nb-1));
324
scr = QPaintDevice::x11AppScreen();
326
// Initialize global color objects
327
if ( QPaintDevice::x11AppDefaultVisual(scr) &&
328
QPaintDevice::x11AppDefaultColormap(scr) ) {
329
globalColors()[blackIdx].setPixel((uint) BlackPixel(dpy, scr));
330
globalColors()[whiteIdx].setPixel((uint) WhitePixel(dpy, scr));
332
globalColors()[blackIdx].alloc(scr);
333
globalColors()[whiteIdx].alloc(scr);
336
#if 0 /* 0 == allocate colors on demand */
337
setLazyAlloc( false ); // allocate global colors
338
((QColor*)(&darkGray))-> alloc();
339
((QColor*)(&gray))-> alloc();
340
((QColor*)(&lightGray))-> alloc();
341
((QColor*)(&::red))-> alloc();
342
((QColor*)(&::green))-> alloc();
343
((QColor*)(&::blue))-> alloc();
344
((QColor*)(&cyan))-> alloc();
345
((QColor*)(&magenta))-> alloc();
346
((QColor*)(&yellow))-> alloc();
347
((QColor*)(&darkRed))-> alloc();
348
((QColor*)(&darkGreen))-> alloc();
349
((QColor*)(&darkBlue))-> alloc();
350
((QColor*)(&darkCyan))-> alloc();
351
((QColor*)(&darkMagenta))-> alloc();
352
((QColor*)(&darkYellow))-> alloc();
353
setLazyAlloc( true );
358
Internal clean up required for QColor.
359
This function is called from the QApplication destructor.
364
void QColor::cleanup()
370
for ( scr = 0; scr < screencount; scr++ ) {
371
if ( screendata[scr]->g_carr ) {
372
delete [] screendata[scr]->g_carr;
373
screendata[scr]->g_carr = 0;
375
if ( screendata[scr]->g_our_alloc ) {
376
delete [] screendata[scr]->g_our_alloc;
377
screendata[scr]->g_our_alloc = 0;
380
if ( screendata[scr]->colorDict ) {
381
screendata[scr]->colorDict->setAutoDelete( true );
382
screendata[scr]->colorDict->clear();
383
delete screendata[scr]->colorDict;
384
screendata[scr]->colorDict = 0;
387
delete screendata[scr];
390
delete [] screendata;
396
/*****************************************************************************
397
QColor member functions
398
*****************************************************************************/
402
Allocates the color on screen \a screen. Only used in X11.
406
uint QColor::alloc( int screen )
408
Display *dpy = QPaintDevice::x11AppDisplay();
410
screen = QPaintDevice::x11AppScreen();
412
return dpy ? (uint)BlackPixel(dpy, screen) : 0;
413
int r = qRed(d.argb);
414
int g = qGreen(d.argb);
415
int b = qBlue(d.argb);
417
QColorScreenData *sd = screendata[screen];
418
if ( sd->g_truecolor ) { // truecolor: map to pixel
419
r = sd->red_shift > 0 ? r << sd->red_shift : r >> -sd->red_shift;
420
g = sd->green_shift > 0 ? g << sd->green_shift : g >> -sd->green_shift;
421
b = sd->blue_shift > 0 ? b << sd->blue_shift : b >> -sd->blue_shift;
422
pix = (b & sd->blue_mask) | (g & sd->green_mask) | (r & sd->red_mask)
423
| ~(sd->blue_mask | sd->green_mask | sd->red_mask);
424
if ( screen == QPaintDevice::x11AppScreen() )
429
QColorData *c = sd->colorDict->find( (long)(d.argb) );
430
if ( c ) { // found color in dictionary
432
if ( screen == QPaintDevice::x11AppScreen() ) {
433
d.d8.invalid = false; // color ok
435
d.d8.pix = pix; // use same pixel value
436
if ( c->context != current_alloc_context ) {
437
c->context = 0; // convert to default context
438
sd->g_our_alloc[pix] = true; // reuse without XAllocColor
450
bool try_again = false;
451
bool try_alloc = !sd->color_reduce;
455
// This loop is run until we manage to either allocate or
456
// find an approximate color, it stops after a few iterations.
460
if ( try_alloc && sd->colors_avail &&
461
XAllocColor(dpy, QPaintDevice::x11AppColormap( screen ),&col) ) {
462
// We could allocate the color
463
pix = (uint) col.pixel;
464
if ( screen == QPaintDevice::x11AppScreen() ) {
466
d.d8.invalid = false;
468
sd->g_carr[d.d8.pix] = col; // update color array
469
if ( current_alloc_context == 0 )
470
sd->g_our_alloc[d.d8.pix] = true; // reuse without XAllocColor
473
// No available colors, or we did not want to allocate one
475
sd->colors_avail = false; // no more available colors
476
if ( sd->g_carr_fetch ) { // refetch color array
477
sd->g_carr_fetch = false;
478
XQueryColors( dpy, QPaintDevice::x11AppColormap( screen ), sd->g_carr,
482
i = find_nearest_color( r, g, b, &mindist, sd );
484
if ( mindist != 0 && !try_alloc ) {
485
// Not an exact match with an existing color
486
int rr = ((r+sd->col_div_r/2)/sd->col_div_r)*sd->col_div_r;
487
int rg = ((g+sd->col_div_g/2)/sd->col_div_g)*sd->col_div_g;
488
int rb = ((b+sd->col_div_b/2)/sd->col_div_b)*sd->col_div_b;
492
int dist = rx*rx + gx*gx + bx*bx; // calculate distance
493
if ( dist < mindist ) {
494
// reduced color is closer - try to alloc it
503
sd->colors_avail = true;
504
continue; // Try alloc reduced color
508
if ( i == -1 ) { // no nearest color?!
510
hsv(&unused, &unused, &value);
511
if (value < 128) { // dark, use black
512
d.argb = qRgb(0,0,0);
513
pix = (uint)BlackPixel( dpy, screen );
514
if ( screen == QPaintDevice::x11AppScreen() ) {
515
d.d8.invalid = false;
519
} else { // light, use white
520
d.argb = qRgb(0xff,0xff,0xff);
521
pix = (uint)WhitePixel( dpy, screen );
522
if ( screen == QPaintDevice::x11AppScreen() ) {
523
d.d8.invalid = false;
530
if ( sd->g_our_alloc[i] ) { // we've already allocated it
531
; // i == g_carr[i].pixel
533
// Try to allocate existing color
535
if ( XAllocColor(dpy, QPaintDevice::x11AppColormap( screen ), &col) ) {
537
sd->g_carr[i] = col; // update color array
538
if ( screen == QPaintDevice::x11AppScreen() ) {
539
if ( current_alloc_context == 0 )
540
sd->g_our_alloc[i] = true; // only in the default context
543
// Oops, it's gone again
546
sd->colors_avail = true;
547
sd->g_carr_fetch = true;
550
if ( !try_again ) { // got it
551
pix = (uint)sd->g_carr[i].pixel;
552
if ( screen == QPaintDevice::x11AppScreen() ) {
553
d.d8.invalid = false;
555
d.d8.pix = pix; // allocated X11 color
560
} while ( try_again && try_count < 2 );
562
if ( try_again ) { // no hope of allocating color
564
hsv(&unused, &unused, &value);
565
if (value < 128) { // dark, use black
566
d.argb = qRgb(0,0,0);
567
pix = (uint)BlackPixel( dpy, screen );
568
if ( screen == QPaintDevice::x11AppScreen() ) {
569
d.d8.invalid = false;
573
} else { // light, use white
574
d.argb = qRgb(0xff,0xff,0xff);
575
pix = (uint)WhitePixel( dpy, screen );
576
if ( screen == QPaintDevice::x11AppScreen() ) {
577
d.d8.invalid = false;
585
// All colors outside context 0 must go into the dictionary
586
bool many = sd->colorDict->count() >= sd->colorDict->size() * 8;
587
if ( many && sd->colorDict->size() == col_std_dict ) {
588
sd->colorDict->resize( col_large_dict );
590
if ( !many || current_alloc_context != 0 ) {
591
c = new QColorData; // insert into color dict
594
c->context = current_alloc_context;
595
sd->colorDict->insert( (long)d.argb, c ); // store color in dict
602
Allocates the RGB color and returns the pixel value.
604
Allocating a color means to obtain a pixel value from the RGB
605
specification. The pixel value is an index into the global color
606
table, but should be considered an arbitrary platform-dependent value.
608
The pixel() function calls alloc() if necessary, so in general you
609
don't need to call this function.
611
\sa enterAllocContext()
613
// ### 4.0 - remove me?
622
Returns the pixel value for screen \a screen.
624
This value is used by the underlying window system to refer to a color.
625
It can be thought of as an index into the display hardware's color table,
626
but the value is an arbitrary 32-bit value.
630
uint QColor::pixel( int screen ) const
632
if (screen != QPaintDevice::x11AppScreen() &&
633
// don't allocate color0 or color1, they have fixed pixel
634
// values for all screens
635
d.argb != qRgba(255, 255, 255, 1) && d.argb != qRgba(0, 0, 0, 1))
636
return ((QColor*)this)->alloc( screen );
641
void QColor::setSystemNamedColor( const QString& name )
643
void QColor::setSystemNamedColor( const char* name )
646
// setSystemNamedColor should look up rgb values from the built in
647
// color tables first (see qcolor_p.cpp), and failing that, use
648
// the window system's interface for translating names to rgb values...
649
// we do this so that things like uic can load an XPM file with named colors
650
// and convert it to a png without having to use window system functions...
652
d.argb = qt_get_rgb_val( name.latin1() );
654
d.argb = qt_get_rgb_val( name );
658
if ( qt_get_named_rgb( name.latin1(), &rgb ) ) {
660
if ( qt_get_named_rgb( name, &rgb ) ) {
662
setRgb( qRed(rgb), qGreen(rgb), qBlue(rgb) );
663
if ( colormodel == d8 ) {
664
d.d8.invalid = false;
670
} else if ( !color_init ) {
671
#if defined(QT_CHECK_STATE)
672
qWarning( "QColor::setSystemNamedColor: Cannot perform this operation "
673
"because QApplication does not exist" );
675
// set color to invalid
679
if ( XLookupColor(QPaintDevice::x11AppDisplay(),
681
QPaintDevice::x11AppColormap(), name.latin1(),
683
QPaintDevice::x11AppColormap(), name,
686
setRgb( col.red>>8, col.green>>8, col.blue>>8 );
688
// set color to invalid
694
#define MAX_CONTEXTS 16
695
static int context_stack[MAX_CONTEXTS];
696
static int context_ptr = 0;
698
static void init_context_stack()
700
static bool did_init = false;
703
context_stack[0] = current_alloc_context = 0;
709
Enters a color allocation context and returns a non-zero unique
712
Color allocation contexts are useful for programs that need to
713
allocate many colors and throw them away later, like image
714
viewers. The allocation context functions work for true color
715
displays as well as for colormap displays, except that
716
QColor::destroyAllocContext() does nothing for true color.
720
QPixmap loadPixmap( QString fileName )
722
static int alloc_context = 0;
724
QColor::destroyAllocContext( alloc_context );
725
alloc_context = QColor::enterAllocContext();
726
QPixmap pm( fileName );
727
QColor::leaveAllocContext();
732
The example code loads a pixmap from file. It frees up all colors
733
that were allocated the last time loadPixmap() was called.
735
The initial/default context is 0. Qt keeps a list of colors
736
associated with their allocation contexts. You can call
737
destroyAllocContext() to get rid of all colors that were allocated
738
in a specific context.
740
Calling enterAllocContext() enters an allocation context. The
741
allocation context lasts until you call leaveAllocContext().
742
QColor has an internal stack of allocation contexts. Each call to
743
enterAllocContex() must have a corresponding leaveAllocContext().
747
int c1 = QColor::enterAllocContext(); // enter context c1
749
int c2 = QColor::enterAllocContext(); // enter context c2
751
QColor::leaveAllocContext(); // leave context c2
753
QColor::leaveAllocContext(); // leave context c1
755
// Now, free all colors that were allocated in context c2
756
QColor::destroyAllocContext( c2 );
759
You may also want to set the application's color specification.
760
See QApplication::setColorSpec() for more information.
762
\sa leaveAllocContext(), currentAllocContext(), destroyAllocContext(),
763
QApplication::setColorSpec()
766
int QColor::enterAllocContext()
768
static int context_seq_no = 0;
769
init_context_stack();
770
if ( context_ptr+1 == MAX_CONTEXTS ) {
771
#if defined(QT_CHECK_STATE)
772
qWarning( "QColor::enterAllocContext: Context stack overflow" );
776
current_alloc_context = context_stack[++context_ptr] = ++context_seq_no;
777
return current_alloc_context;
782
Leaves a color allocation context.
784
See enterAllocContext() for a detailed explanation.
786
\sa enterAllocContext(), currentAllocContext()
789
void QColor::leaveAllocContext()
791
init_context_stack();
792
if ( context_ptr == 0 ) {
793
#if defined(QT_CHECK_STATE)
794
qWarning( "QColor::leaveAllocContext: Context stack underflow" );
798
current_alloc_context = context_stack[--context_ptr];
803
Returns the current color allocation context.
805
The default context is 0.
807
\sa enterAllocContext(), leaveAllocContext()
810
int QColor::currentAllocContext()
812
return current_alloc_context;
817
Destroys a color allocation context, \e context.
819
This function deallocates all colors that were allocated in the
820
specified \a context. If \a context == -1, it frees up all colors
821
that the application has allocated. If \a context == -2, it frees
822
up all colors that the application has allocated, except those in
825
The function does nothing for true color displays.
827
\sa enterAllocContext(), alloc()
830
void QColor::destroyAllocContext( int context )
832
init_context_stack();
837
for ( screen = 0; screen < screencount; ++screen ) {
838
if ( screendata[screen]->g_truecolor )
846
memset( freeing, false, screendata[screen]->g_cells*sizeof(bool) );
848
QColorDictIt it( *screendata[screen]->colorDict );
851
while ( (d=it.current()) ) {
852
rgbv = (uint)it.currentKey();
853
if ( (d->context || context==-1) &&
854
(d->context == context || context < 0) ) {
855
if ( !screendata[screen]->g_our_alloc[d->pix] && !freeing[d->pix] ) {
856
// will free this color
857
pixels[i++] = d->pix;
858
freeing[d->pix] = true;
861
screendata[screen]->colorDict->remove( (long)rgbv );
866
XFreeColors( QPaintDevice::x11AppDisplay(),
867
QPaintDevice::x11AppColormap( screen ),