1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qcolormap.h"
43
44 #include "qapplication.h"
45 #include "qdebug.h"
46 #include "qdesktopwidget.h"
47 #include "qvarlengtharray.h"
48
49 #include "qx11info_x11.h"
50 #include <private/qt_x11_p.h>
51 #include <limits.h>
52
53 QT_BEGIN_NAMESPACE
54
55 class QColormapPrivate
56 {
57 public:
QColormapPrivate()58 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)
64 {}
65
66 QAtomicInt ref;
67
68 QColormap::Mode mode;
69 int depth;
70
71 Colormap colormap;
72 bool defaultColormap;
73
74 Visual *visual;
75 bool defaultVisual;
76
77 int r_max;
78 int g_max;
79 int b_max;
80
81 uint r_shift;
82 uint g_shift;
83 uint b_shift;
84
85 QVector<QColor> colors;
86 QVector<int> pixels;
87 };
88
89
right_align(uint v)90 static uint right_align(uint v)
91 {
92 while (!(v & 0x1))
93 v >>= 1;
94 return v;
95 }
96
lowest_bit(uint v)97 static int lowest_bit(uint v)
98 {
99 int i;
100 uint b = 1u;
101 for (i = 0; ((v & b) == 0u) && i < 32; ++i)
102 b <<= 1u;
103 return i == 32 ? -1 : i;
104 }
105
cube_root(int v)106 static int cube_root(int v)
107 {
108 if (v == 1)
109 return 1;
110 // brute force algorithm
111 int i = 1;
112 for (;;) {
113 const int b = i * i * i;
114 if (b <= v) {
115 ++i;
116 } else {
117 --i;
118 break;
119 }
120 }
121 return i;
122 }
123
find_visual(Display * display,int screen,int visual_class,int visual_id,int * depth,bool * defaultVisual)124 static Visual *find_visual(Display *display,
125 int screen,
126 int visual_class,
127 int visual_id,
128 int *depth,
129 bool *defaultVisual)
130 {
131 XVisualInfo *vi, rvi;
132 int count;
133
134 uint mask = VisualScreenMask;
135 rvi.screen = screen;
136
137 if (visual_class != -1) {
138 rvi.c_class = visual_class;
139 mask |= VisualClassMask;
140 }
141 if (visual_id != -1) {
142 rvi.visualid = visual_id;
143 mask |= VisualIDMask;
144 }
145
146 Visual *visual = DefaultVisual(display, screen);
147 *defaultVisual = true;
148 *depth = DefaultDepth(display, screen);
149
150 vi = XGetVisualInfo(display, mask, &rvi, &count);
151 if (vi) {
152 int best = 0;
153 for (int x = 0; x < count; ++x) {
154 if (vi[x].depth > vi[best].depth)
155 best = x;
156 }
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;
161 }
162 }
163 if (vi)
164 XFree((char *)vi);
165 return visual;
166 }
167
query_colormap(QColormapPrivate * d,int screen)168 static void query_colormap(QColormapPrivate *d, int screen)
169 {
170 Display *display = QX11Info::display();
171
172 // query existing colormap
173 int q_colors = (((1u << d->depth) > 256u) ? 256u : (1u << d->depth));
174 XColor queried[256];
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);
179
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
187 continue;
188 }
189
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));
193 }
194
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)
199 continue;
200
201 QRgb rgb;
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));
209 } else {
210 rgb = qRgb(x, x, x);
211 }
212
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) {
221 mindist = dist;
222 best = y;
223 }
224 }
225
226 Q_ASSERT(best >= 0 && best < q_colors);
227 if (d->visual->c_class & 1) {
228 XColor xcolor;
229 xcolor.red = queried[best].red;
230 xcolor.green = queried[best].green;
231 xcolor.blue = queried[best].blue;
232 xcolor.pixel = queried[best].pixel;
233
234 if (XAllocColor(display, d->colormap, &xcolor)) {
235 d->pixels[x] = xcolor.pixel;
236 } else {
237 // some weird stuff is going on...
238 d->pixels[x] = (qGray(rgb) < 127
239 ? BlackPixel(display, screen)
240 : WhitePixel(display, screen));
241 }
242 } else {
243 d->pixels[x] = best;
244 }
245 }
246 }
247
init_gray(QColormapPrivate * d,int screen)248 static void init_gray(QColormapPrivate *d, int screen)
249 {
250 d->pixels.resize(d->r_max);
251
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);
255
256 d->pixels[g] = -1;
257
258 if (d->visual->c_class & 1) {
259 XColor xcolor;
260 xcolor.red = qRed(rgb) * 0x101;
261 xcolor.green = qGreen(rgb) * 0x101;
262 xcolor.blue = qBlue(rgb) * 0x101;
263 xcolor.pixel = 0ul;
264
265 if (XAllocColor(QX11Info::display(), d->colormap, &xcolor))
266 d->pixels[g] = xcolor.pixel;
267 }
268 }
269
270 query_colormap(d, screen);
271 }
272
init_indexed(QColormapPrivate * d,int screen)273 static void init_indexed(QColormapPrivate *d, int screen)
274 {
275 d->pixels.resize(d->r_max * d->g_max * d->b_max);
276
277 // create color cube
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));
284
285 d->pixels[x] = -1;
286
287 if (d->visual->c_class & 1) {
288 XColor xcolor;
289 xcolor.red = qRed(rgb) * 0x101;
290 xcolor.green = qGreen(rgb) * 0x101;
291 xcolor.blue = qBlue(rgb) * 0x101;
292 xcolor.pixel = 0ul;
293
294 if (XAllocColor(QX11Info::display(), d->colormap, &xcolor))
295 d->pixels[x] = xcolor.pixel;
296 }
297 }
298 }
299 }
300
301 query_colormap(d, screen);
302 }
303
init_direct(QColormapPrivate * d,bool ownColormap)304 static void init_direct(QColormapPrivate *d, bool ownColormap)
305 {
306 if (d->visual->c_class != DirectColor || !ownColormap)
307 return;
308
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);
312 int i = 0;
313
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;
318 ++i;
319 }
320
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;
325 ++i;
326 }
327
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;
332 ++i;
333 }
334
335 XStoreColors(X11->display, d->colormap, colorTable.data(), colorTable.count());
336 }
337
338 static QColormap **cmaps = 0;
339
initialize()340 void QColormap::initialize()
341 {
342 Display *display = QX11Info::display();
343 const int screens = ScreenCount(display);
344
345 cmaps = new QColormap*[screens];
346
347 for (int i = 0; i < screens; ++i) {
348 cmaps[i] = new QColormap;
349 QColormapPrivate * const d = cmaps[i]->d;
350
351 bool use_stdcmap = false;
352 int color_count = X11->color_count;
353
354 // defaults
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;
360
361 Visual *argbVisual = 0;
362
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);
376 if (d->depth <= 8) {
377 d->visual = DefaultVisual(display, i);
378 d->defaultVisual = true;
379 color_count = 216;
380 }
381 } else if (!X11->custom_cmap) {
382 XStandardColormap *stdcmap = 0;
383 int ncmaps = 0;
384
385 #ifndef QT_NO_XRENDER
386 if (X11->use_xrender) {
387 int nvi;
388 XVisualInfo templ;
389 templ.screen = i;
390 templ.depth = 32;
391 templ.c_class = TrueColor;
392 XVisualInfo *xvi = XGetVisualInfo(X11->display, VisualScreenMask |
393 VisualDepthMask |
394 VisualClassMask, &templ, &nvi);
395 for (int idx = 0; idx < nvi; ++idx) {
396 XRenderPictFormat *format = XRenderFindVisualFormat(X11->display,
397 xvi[idx].visual);
398 if (format->type == PictTypeDirect && format->direct.alphaMask) {
399 argbVisual = xvi[idx].visual;
400 break;
401 }
402 }
403 XFree(xvi);
404 }
405 #endif
406 if (XGetRGBColormaps(display, RootWindow(display, i),
407 &stdcmap, &ncmaps, XA_RGB_DEFAULT_MAP)) {
408 if (stdcmap) {
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
417
418 XVisualInfo proto;
419 proto.visualid = stdcmap[c].visualid;
420 proto.screen = i;
421
422 int nvisuals = 0;
423 XVisualInfo *vi = XGetVisualInfo(display, VisualIDMask | VisualScreenMask,
424 &proto, &nvisuals);
425 if (vi) {
426 if (nvisuals > 0) {
427 use_stdcmap = true;
428
429 d->mode = ((vi[0].visual->c_class < StaticColor)
430 ? Gray
431 : ((vi[0].visual->c_class < TrueColor)
432 ? Indexed
433 : Direct));
434
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));
440
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;
444
445 if (d->mode == Direct) {
446 // calculate offsets
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);
450 } else {
451 d->r_shift = 0;
452 d->g_shift = 0;
453 d->b_shift = 0;
454 }
455 }
456 XFree(vi);
457 }
458 break;
459 }
460 XFree(stdcmap);
461 }
462 }
463 }
464 if (!use_stdcmap) {
465 switch (d->visual->c_class) {
466 case StaticGray:
467 d->mode = Gray;
468
469 d->r_max = d->g_max = d->b_max = d->visual->map_entries;
470 break;
471
472 case XGrayScale:
473 d->mode = Gray;
474
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;
484 else
485 d->r_max = d->g_max = d->b_max = 4;
486 break;
487
488 case StaticColor:
489 d->mode = Indexed;
490
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;
494 break;
495
496 case PseudoColor:
497 d->mode = Indexed;
498
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);
508 else
509 d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries);
510 break;
511
512 case TrueColor:
513 case DirectColor:
514 d->mode = Direct;
515
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;
519
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);
523 break;
524 }
525 }
526
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)
537 d->colormap =
538 XCreateColormap(display, RootWindow(display, i), d->visual,
539 d->visual->c_class == DirectColor ? AllocAll : AllocNone);
540 d->defaultColormap = false;
541 ownColormap = true;
542 }
543
544 switch (d->mode) {
545 case Gray:
546 init_gray(d, i);
547 break;
548 case Indexed:
549 init_indexed(d, i);
550 break;
551 case Direct:
552 init_direct(d, ownColormap);
553 break;
554 }
555
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;
563
564 if (argbVisual) {
565 X11->argbVisuals[i] = argbVisual;
566 X11->argbColormaps[i] = XCreateColormap(display, RootWindow(display, i), argbVisual, AllocNone);
567 }
568
569 // ###
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)
575 screen->cells = 256;
576 }
577 }
578
cleanup()579 void QColormap::cleanup()
580 {
581 Display *display = QX11Info::display();
582 const int screens = ScreenCount(display);
583
584 for (int i = 0; i < screens; ++i)
585 delete cmaps[i];
586
587 delete [] cmaps;
588 cmaps = 0;
589 }
590
591
instance(int screen)592 QColormap QColormap::instance(int screen)
593 {
594 if (screen == -1)
595 screen = QX11Info::appScreen();
596 return *cmaps[screen];
597 }
598
599 /*! \internal
600 Constructs a new colormap.
601 */
QColormap()602 QColormap::QColormap()
603 : d(new QColormapPrivate)
604 {}
605
QColormap(const QColormap & colormap)606 QColormap::QColormap(const QColormap &colormap)
607 :d (colormap.d)
608 { d->ref.ref(); }
609
~QColormap()610 QColormap::~QColormap()
611 {
612 if (!d->ref.deref()) {
613 if (!d->defaultColormap)
614 XFreeColormap(QX11Info::display(), d->colormap);
615 delete d;
616 }
617 }
618
mode() const619 QColormap::Mode QColormap::mode() const
620 { return d->mode; }
621
depth() const622 int QColormap::depth() const
623 { return d->depth; }
624
size() const625 int QColormap::size() const
626 {
627 return (d->mode == Gray
628 ? d->r_max
629 : (d->mode == Indexed
630 ? d->r_max * d->g_max * d->b_max
631 : -1));
632 }
633
pixel(const QColor & color) const634 uint QColormap::pixel(const QColor &color) const
635 {
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) {
641 if (d->mode == Gray)
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);
644 }
645 return (r << d->r_shift) + (g << d->g_shift) + (b << d->b_shift);
646 }
647
colorAt(uint pixel) const648 const QColor QColormap::colorAt(uint pixel) const
649 {
650 if (d->mode != Direct) {
651 Q_ASSERT(pixel <= (uint)d->colors.size());
652 return d->colors.at(pixel);
653 }
654
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);
659 }
660
colormap() const661 const QVector<QColor> QColormap::colormap() const
662 { return d->colors; }
663
operator =(const QColormap & colormap)664 QColormap &QColormap::operator=(const QColormap &colormap)
665 {
666 qAtomicAssign(d, colormap.d);
667 return *this;
668 }
669
670 QT_END_NAMESPACE
671