1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qcolormap.h"
44 #include "qapplication.h"
46 #include "qdesktopwidget.h"
47 #include "qvarlengtharray.h"
49 #include "qx11info_x11.h"
50 #include <private/qt_x11_p.h>
55 class QColormapPrivate
59 : ref(1), mode(QColormap::Direct), depth(0),
60 colormap(0), defaultColormap(true),
61 visual(0), defaultVisual(true),
62 r_max(0), g_max(0), b_max(0),
63 r_shift(0), g_shift(0), b_shift(0)
85 QVector<QColor> colors;
90 static uint right_align(uint v)
97 static int lowest_bit(uint v)
101 for (i = 0; ((v & b) == 0u) && i < 32; ++i)
103 return i == 32 ? -1 : i;
106 static int cube_root(int v)
110 // brute force algorithm
113 const int b = i * i * i;
124 static Visual *find_visual(Display *display,
131 XVisualInfo *vi, rvi;
134 uint mask = VisualScreenMask;
137 if (visual_class != -1) {
138 rvi.c_class = visual_class;
139 mask |= VisualClassMask;
141 if (visual_id != -1) {
142 rvi.visualid = visual_id;
143 mask |= VisualIDMask;
146 Visual *visual = DefaultVisual(display, screen);
147 *defaultVisual = true;
148 *depth = DefaultDepth(display, screen);
150 vi = XGetVisualInfo(display, mask, &rvi, &count);
153 for (int x = 0; x < count; ++x) {
154 if (vi[x].depth > vi[best].depth)
157 if (best >= 0 && best <= count && vi[best].visualid != XVisualIDFromVisual(visual)) {
158 visual = vi[best].visual;
159 *defaultVisual = (visual == DefaultVisual(display, screen));
160 *depth = vi[best].depth;
168 static void query_colormap(QColormapPrivate *d, int screen)
170 Display *display = QX11Info::display();
172 // query existing colormap
173 int q_colors = (((1u << d->depth) > 256u) ? 256u : (1u << d->depth));
175 memset(queried, 0, sizeof(queried));
176 for (int x = 0; x < q_colors; ++x)
177 queried[x].pixel = x;
178 XQueryColors(display, d->colormap, queried, q_colors);
180 d->colors.resize(q_colors);
181 for (int x = 0; x < q_colors; ++x) {
182 if (queried[x].red == 0
183 && queried[x].green == 0
184 && queried[x].blue == 0
185 && queried[x].pixel != BlackPixel(display, screen)) {
186 // unallocated color cell, skip it
190 d->colors[x] = QColor::fromRgbF(queried[x].red / float(USHRT_MAX),
191 queried[x].green / float(USHRT_MAX),
192 queried[x].blue / float(USHRT_MAX));
195 // for missing colors, find the closest color in the existing colormap
196 Q_ASSERT(d->pixels.size());
197 for (int x = 0; x < d->pixels.size(); ++x) {
198 if (d->pixels.at(x) != -1)
202 if (d->mode == QColormap::Indexed) {
203 const int r = (x / (d->g_max * d->b_max)) % d->r_max;
204 const int g = (x / d->b_max) % d->g_max;
205 const int b = x % d->b_max;
206 rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
207 (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
208 (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
213 // find closest color
214 int mindist = INT_MAX, best = -1;
215 for (int y = 0; y < q_colors; ++y) {
216 int r = qRed(rgb) - (queried[y].red >> 8);
217 int g = qGreen(rgb) - (queried[y].green >> 8);
218 int b = qBlue(rgb) - (queried[y].blue >> 8);
219 int dist = (r * r) + (g * g) + (b * b);
220 if (dist < mindist) {
226 Q_ASSERT(best >= 0 && best < q_colors);
227 if (d->visual->c_class & 1) {
229 xcolor.red = queried[best].red;
230 xcolor.green = queried[best].green;
231 xcolor.blue = queried[best].blue;
232 xcolor.pixel = queried[best].pixel;
234 if (XAllocColor(display, d->colormap, &xcolor)) {
235 d->pixels[x] = xcolor.pixel;
237 // some weird stuff is going on...
238 d->pixels[x] = (qGray(rgb) < 127
239 ? BlackPixel(display, screen)
240 : WhitePixel(display, screen));
248 static void init_gray(QColormapPrivate *d, int screen)
250 d->pixels.resize(d->r_max);
252 for (int g = 0; g < d->g_max; ++g) {
253 const int gray = (g * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1);
254 const QRgb rgb = qRgb(gray, gray, gray);
258 if (d->visual->c_class & 1) {
260 xcolor.red = qRed(rgb) * 0x101;
261 xcolor.green = qGreen(rgb) * 0x101;
262 xcolor.blue = qBlue(rgb) * 0x101;
265 if (XAllocColor(QX11Info::display(), d->colormap, &xcolor))
266 d->pixels[g] = xcolor.pixel;
270 query_colormap(d, screen);
273 static void init_indexed(QColormapPrivate *d, int screen)
275 d->pixels.resize(d->r_max * d->g_max * d->b_max);
278 for (int x = 0, r = 0; r < d->r_max; ++r) {
279 for (int g = 0; g < d->g_max; ++g) {
280 for (int b = 0; b < d->b_max; ++b, ++x) {
281 const QRgb rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
282 (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
283 (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
287 if (d->visual->c_class & 1) {
289 xcolor.red = qRed(rgb) * 0x101;
290 xcolor.green = qGreen(rgb) * 0x101;
291 xcolor.blue = qBlue(rgb) * 0x101;
294 if (XAllocColor(QX11Info::display(), d->colormap, &xcolor))
295 d->pixels[x] = xcolor.pixel;
301 query_colormap(d, screen);
304 static void init_direct(QColormapPrivate *d, bool ownColormap)
306 if (d->visual->c_class != DirectColor || !ownColormap)
309 // preallocate 768 on the stack, so that we don't have to malloc
310 // for the common case (<= 24 bpp)
311 QVarLengthArray<XColor, 768> colorTable(d->r_max + d->g_max + d->b_max);
314 for (int r = 0; r < d->r_max; ++r) {
315 colorTable[i].red = r << 8 | r;
316 colorTable[i].pixel = r << d->r_shift;
317 colorTable[i].flags = DoRed;
321 for (int g = 0; g < d->g_max; ++g) {
322 colorTable[i].green = g << 8 | g;
323 colorTable[i].pixel = g << d->g_shift;
324 colorTable[i].flags = DoGreen;
328 for (int b = 0; b < d->b_max; ++b) {
329 colorTable[i].blue = (b << 8 | b);
330 colorTable[i].pixel = b << d->b_shift;
331 colorTable[i].flags = DoBlue;
335 XStoreColors(X11->display, d->colormap, colorTable.data(), colorTable.count());
338 static QColormap **cmaps = 0;
340 void QColormap::initialize()
342 Display *display = QX11Info::display();
343 const int screens = ScreenCount(display);
345 cmaps = new QColormap*[screens];
347 for (int i = 0; i < screens; ++i) {
348 cmaps[i] = new QColormap;
349 QColormapPrivate * const d = cmaps[i]->d;
351 bool use_stdcmap = false;
352 int color_count = X11->color_count;
355 d->depth = DefaultDepth(display, i);
356 d->colormap = DefaultColormap(display, i);
357 d->defaultColormap = true;
358 d->visual = DefaultVisual(display, i);
359 d->defaultVisual = true;
361 Visual *argbVisual = 0;
363 if (X11->visual && i == DefaultScreen(display)) {
364 // only use the outside colormap on the default screen
365 d->visual = find_visual(display, i, X11->visual->c_class,
366 XVisualIDFromVisual(X11->visual),
367 &d->depth, &d->defaultVisual);
368 } else if ((X11->visual_class != -1 && X11->visual_class >= 0 && X11->visual_class < 6)
369 || (X11->visual_id != -1)) {
370 // look for a specific visual or type of visual
371 d->visual = find_visual(display, i, X11->visual_class, X11->visual_id,
372 &d->depth, &d->defaultVisual);
373 } else if (QApplication::colorSpec() == QApplication::ManyColor) {
374 // look for a TrueColor w/ a depth higher than 8bpp
375 d->visual = find_visual(display, i, TrueColor, -1, &d->depth, &d->defaultVisual);
377 d->visual = DefaultVisual(display, i);
378 d->defaultVisual = true;
381 } else if (!X11->custom_cmap) {
382 XStandardColormap *stdcmap = 0;
385 #ifndef QT_NO_XRENDER
386 if (X11->use_xrender) {
391 templ.c_class = TrueColor;
392 XVisualInfo *xvi = XGetVisualInfo(X11->display, VisualScreenMask |
394 VisualClassMask, &templ, &nvi);
395 for (int idx = 0; idx < nvi; ++idx) {
396 XRenderPictFormat *format = XRenderFindVisualFormat(X11->display,
398 if (format->type == PictTypeDirect && format->direct.alphaMask) {
399 argbVisual = xvi[idx].visual;
406 if (XGetRGBColormaps(display, RootWindow(display, i),
407 &stdcmap, &ncmaps, XA_RGB_DEFAULT_MAP)) {
409 for (int c = 0; c < ncmaps; ++c) {
410 if (!stdcmap[c].red_max ||
411 !stdcmap[c].green_max ||
412 !stdcmap[c].blue_max ||
413 !stdcmap[c].red_mult ||
414 !stdcmap[c].green_mult ||
415 !stdcmap[c].blue_mult)
416 continue; // invalid stdcmap
419 proto.visualid = stdcmap[c].visualid;
423 XVisualInfo *vi = XGetVisualInfo(display, VisualIDMask | VisualScreenMask,
429 d->mode = ((vi[0].visual->c_class < StaticColor)
431 : ((vi[0].visual->c_class < TrueColor)
435 d->depth = vi[0].depth;
436 d->colormap = stdcmap[c].colormap;
437 d->defaultColormap = true;
438 d->visual = vi[0].visual;
439 d->defaultVisual = (d->visual == DefaultVisual(display, i));
441 d->r_max = stdcmap[c].red_max + 1;
442 d->g_max = stdcmap[c].green_max + 1;
443 d->b_max = stdcmap[c].blue_max + 1;
445 if (d->mode == Direct) {
447 d->r_shift = lowest_bit(d->visual->red_mask);
448 d->g_shift = lowest_bit(d->visual->green_mask);
449 d->b_shift = lowest_bit(d->visual->blue_mask);
465 switch (d->visual->c_class) {
469 d->r_max = d->g_max = d->b_max = d->visual->map_entries;
475 // follow precedent set in libXmu...
476 if (color_count != 0)
477 d->r_max = d->g_max = d->b_max = color_count;
478 else if (d->visual->map_entries > 65000)
479 d->r_max = d->g_max = d->b_max = 4096;
480 else if (d->visual->map_entries > 4000)
481 d->r_max = d->g_max = d->b_max = 512;
482 else if (d->visual->map_entries > 250)
483 d->r_max = d->g_max = d->b_max = 12;
485 d->r_max = d->g_max = d->b_max = 4;
491 d->r_max = right_align(d->visual->red_mask) + 1;
492 d->g_max = right_align(d->visual->green_mask) + 1;
493 d->b_max = right_align(d->visual->blue_mask) + 1;
499 // follow precedent set in libXmu...
500 if (color_count != 0)
501 d->r_max = d->g_max = d->b_max = cube_root(color_count);
502 else if (d->visual->map_entries > 65000)
503 d->r_max = d->g_max = d->b_max = 27;
504 else if (d->visual->map_entries > 4000)
505 d->r_max = d->g_max = d->b_max = 12;
506 else if (d->visual->map_entries > 250)
507 d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries - 125);
509 d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries);
516 d->r_max = right_align(d->visual->red_mask) + 1;
517 d->g_max = right_align(d->visual->green_mask) + 1;
518 d->b_max = right_align(d->visual->blue_mask) + 1;
520 d->r_shift = lowest_bit(d->visual->red_mask);
521 d->g_shift = lowest_bit(d->visual->green_mask);
522 d->b_shift = lowest_bit(d->visual->blue_mask);
527 bool ownColormap = false;
528 if (X11->colormap && i == DefaultScreen(display)) {
529 // only use the outside colormap on the default screen
530 d->colormap = X11->colormap;
531 d->defaultColormap = (d->colormap == DefaultColormap(display, i));
532 } else if ((!use_stdcmap
533 && (((d->visual->c_class & 1) && X11->custom_cmap)
534 || d->visual != DefaultVisual(display, i)))
535 || d->visual->c_class == DirectColor) {
536 // allocate custom colormap (we always do this when using DirectColor visuals)
538 XCreateColormap(display, RootWindow(display, i), d->visual,
539 d->visual->c_class == DirectColor ? AllocAll : AllocNone);
540 d->defaultColormap = false;
552 init_direct(d, ownColormap);
556 QX11InfoData *screen = X11->screens + i;
557 screen->depth = d->depth;
558 screen->visual = d->visual;
559 screen->defaultVisual = d->defaultVisual;
560 screen->colormap = d->colormap;
561 screen->defaultColormap = d->defaultColormap;
562 screen->cells = screen->visual->map_entries;
565 X11->argbVisuals[i] = argbVisual;
566 X11->argbColormaps[i] = XCreateColormap(display, RootWindow(display, i), argbVisual, AllocNone);
570 // We assume that 8bpp == pseudocolor, but this is not
571 // always the case (according to the X server), so we need
572 // to make sure that our internal data is setup in a way
573 // that is compatible with our assumptions
574 if (screen->visual->c_class == TrueColor && screen->depth == 8 && screen->cells == 8)
579 void QColormap::cleanup()
581 Display *display = QX11Info::display();
582 const int screens = ScreenCount(display);
584 for (int i = 0; i < screens; ++i)
592 QColormap QColormap::instance(int screen)
595 screen = QX11Info::appScreen();
596 return *cmaps[screen];
600 Constructs a new colormap.
602 QColormap::QColormap()
603 : d(new QColormapPrivate)
606 QColormap::QColormap(const QColormap &colormap)
610 QColormap::~QColormap()
612 if (!d->ref.deref()) {
613 if (!d->defaultColormap)
614 XFreeColormap(QX11Info::display(), d->colormap);
619 QColormap::Mode QColormap::mode() const
622 int QColormap::depth() const
625 int QColormap::size() const
627 return (d->mode == Gray
629 : (d->mode == Indexed
630 ? d->r_max * d->g_max * d->b_max
634 uint QColormap::pixel(const QColor &color) const
636 const QColor c = color.toRgb();
637 const uint r = (c.ct.argb.red * d->r_max) >> 16;
638 const uint g = (c.ct.argb.green * d->g_max) >> 16;
639 const uint b = (c.ct.argb.blue * d->b_max) >> 16;
640 if (d->mode != Direct) {
642 return d->pixels.at((r * 30 + g * 59 + b * 11) / 100);
643 return d->pixels.at(r * d->g_max * d->b_max + g * d->b_max + b);
645 return (r << d->r_shift) + (g << d->g_shift) + (b << d->b_shift);
648 const QColor QColormap::colorAt(uint pixel) const
650 if (d->mode != Direct) {
651 Q_ASSERT(pixel <= (uint)d->colors.size());
652 return d->colors.at(pixel);
655 const int r = (((pixel & d->visual->red_mask) >> d->r_shift) << 8) / d->r_max;
656 const int g = (((pixel & d->visual->green_mask) >> d->g_shift) << 8) / d->g_max;
657 const int b = (((pixel & d->visual->blue_mask) >> d->b_shift) << 8) / d->b_max;
658 return QColor(r, g, b);
661 const QVector<QColor> QColormap::colormap() const
662 { return d->colors; }
664 QColormap &QColormap::operator=(const QColormap &colormap)
666 qAtomicAssign(d, colormap.d);