Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcolormap_x11.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <QVarLengthArray>
6
7#include <private/qguiapplication_p.h>
8
11#include "qt_x11_p.h"
12
14
48
49static uint right_align(uint v)
50{
51 while (!(v & 0x1))
52 v >>= 1;
53 return v;
54}
55
56static int cube_root(int v)
57{
58 if (v == 1)
59 return 1;
60 // brute force algorithm
61 int i = 1;
62 for (;;) {
63 const int b = i * i * i;
64 if (b <= v) {
65 ++i;
66 } else {
67 --i;
68 break;
69 }
70 }
71 return i;
72}
73
74static Visual *find_visual(Display *display,
75 int screen,
76 int visual_class,
77 int visual_id,
78 int *depth,
79 bool *defaultVisual)
80{
81 XVisualInfo *vi, rvi;
82 int count;
83
84 uint mask = VisualScreenMask;
85 rvi.screen = screen;
86
87 if (visual_class != -1) {
88 rvi.c_class = visual_class;
89 mask |= VisualClassMask;
90 }
91 if (visual_id != -1) {
92 rvi.visualid = visual_id;
93 mask |= VisualIDMask;
94 }
95
96 Visual *visual = DefaultVisual(display, screen);
97 *defaultVisual = true;
98 *depth = DefaultDepth(display, screen);
99
100 vi = XGetVisualInfo(display, mask, &rvi, &count);
101 if (vi) {
102 int best = 0;
103 for (int x = 0; x < count; ++x) {
104 if (vi[x].depth > vi[best].depth)
105 best = x;
106 }
107 if (best >= 0 && best <= count && vi[best].visualid != XVisualIDFromVisual(visual)) {
108 visual = vi[best].visual;
109 *defaultVisual = (visual == DefaultVisual(display, screen));
110 *depth = vi[best].depth;
111 }
112 }
113 if (vi)
114 XFree((char *)vi);
115 return visual;
116}
117
118static void query_colormap(QXcbColormapPrivate *d, int screen)
119{
120 Display *display = X11->display;
121
122 // query existing colormap
123 int q_colors = (((1u << d->depth) > 256u) ? 256u : (1u << d->depth));
124 XColor queried[256];
125 memset(queried, 0, sizeof(queried));
126 for (int x = 0; x < q_colors; ++x)
127 queried[x].pixel = x;
128 XQueryColors(display, d->colormap, queried, q_colors);
129
130 d->colors.resize(q_colors);
131 for (int x = 0; x < q_colors; ++x) {
132 if (queried[x].red == 0
133 && queried[x].green == 0
134 && queried[x].blue == 0
135 && queried[x].pixel != BlackPixel(display, screen)) {
136 // unallocated color cell, skip it
137 continue;
138 }
139
140 d->colors[x] = QColor::fromRgbF(queried[x].red / float(USHRT_MAX),
141 queried[x].green / float(USHRT_MAX),
142 queried[x].blue / float(USHRT_MAX));
143 }
144
145 // for missing colors, find the closest color in the existing colormap
146 Q_ASSERT(d->pixels.size());
147 for (int x = 0; x < d->pixels.size(); ++x) {
148 if (d->pixels.at(x) != -1)
149 continue;
150
151 QRgb rgb;
153 const int r = (x / (d->g_max * d->b_max)) % d->r_max;
154 const int g = (x / d->b_max) % d->g_max;
155 const int b = x % d->b_max;
156 rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
157 (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
158 (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
159 } else {
160 rgb = qRgb(x, x, x);
161 }
162
163 // find closest color
164 int mindist = INT_MAX, best = -1;
165 for (int y = 0; y < q_colors; ++y) {
166 int r = qRed(rgb) - (queried[y].red >> 8);
167 int g = qGreen(rgb) - (queried[y].green >> 8);
168 int b = qBlue(rgb) - (queried[y].blue >> 8);
169 int dist = (r * r) + (g * g) + (b * b);
170 if (dist < mindist) {
171 mindist = dist;
172 best = y;
173 }
174 }
175
176 Q_ASSERT(best >= 0 && best < q_colors);
177 if (d->visual->c_class & 1) {
178 XColor xcolor;
179 xcolor.red = queried[best].red;
180 xcolor.green = queried[best].green;
181 xcolor.blue = queried[best].blue;
182 xcolor.pixel = queried[best].pixel;
183
184 if (XAllocColor(display, d->colormap, &xcolor)) {
185 d->pixels[x] = xcolor.pixel;
186 } else {
187 // some weird stuff is going on...
188 d->pixels[x] = (qGray(rgb) < 127
189 ? BlackPixel(display, screen)
190 : WhitePixel(display, screen));
191 }
192 } else {
193 d->pixels[x] = best;
194 }
195 }
196}
197
198static void init_gray(QXcbColormapPrivate *d, int screen)
199{
200 d->pixels.resize(d->r_max);
201
202 for (int g = 0; g < d->g_max; ++g) {
203 const int gray = (g * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1);
204 const QRgb rgb = qRgb(gray, gray, gray);
205
206 d->pixels[g] = -1;
207
208 if (d->visual->c_class & 1) {
209 XColor xcolor;
210 xcolor.red = qRed(rgb) * 0x101;
211 xcolor.green = qGreen(rgb) * 0x101;
212 xcolor.blue = qBlue(rgb) * 0x101;
213 xcolor.pixel = 0ul;
214
215 if (XAllocColor(X11->display, d->colormap, &xcolor))
216 d->pixels[g] = xcolor.pixel;
217 }
218 }
219
220 query_colormap(d, screen);
221}
222
223static void init_indexed(QXcbColormapPrivate *d, int screen)
224{
225 d->pixels.resize(d->r_max * d->g_max * d->b_max);
226
227 // create color cube
228 for (int x = 0, r = 0; r < d->r_max; ++r) {
229 for (int g = 0; g < d->g_max; ++g) {
230 for (int b = 0; b < d->b_max; ++b, ++x) {
231 const QRgb rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
232 (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
233 (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
234
235 d->pixels[x] = -1;
236
237 if (d->visual->c_class & 1) {
238 XColor xcolor;
239 xcolor.red = qRed(rgb) * 0x101;
240 xcolor.green = qGreen(rgb) * 0x101;
241 xcolor.blue = qBlue(rgb) * 0x101;
242 xcolor.pixel = 0ul;
243
244 if (XAllocColor(X11->display, d->colormap, &xcolor))
245 d->pixels[x] = xcolor.pixel;
246 }
247 }
248 }
249 }
250
251 query_colormap(d, screen);
252}
253
254static void init_direct(QXcbColormapPrivate *d, bool ownColormap)
255{
256 if (d->visual->c_class != DirectColor || !ownColormap)
257 return;
258
259 // preallocate 768 on the stack, so that we don't have to malloc
260 // for the common case (<= 24 bpp)
261 QVarLengthArray<XColor, 768> colorTable(d->r_max + d->g_max + d->b_max);
262 int i = 0;
263
264 for (int r = 0; r < d->r_max; ++r) {
265 colorTable[i].red = r << 8 | r;
266 colorTable[i].pixel = r << d->r_shift;
267 colorTable[i].flags = DoRed;
268 ++i;
269 }
270
271 for (int g = 0; g < d->g_max; ++g) {
272 colorTable[i].green = g << 8 | g;
273 colorTable[i].pixel = g << d->g_shift;
274 colorTable[i].flags = DoGreen;
275 ++i;
276 }
277
278 for (int b = 0; b < d->b_max; ++b) {
279 colorTable[i].blue = (b << 8 | b);
280 colorTable[i].pixel = b << d->b_shift;
281 colorTable[i].flags = DoBlue;
282 ++i;
283 }
284
285 XStoreColors(X11->display, d->colormap, colorTable.data(), colorTable.count());
286}
287
288static QXcbColormap **cmaps = nullptr;
289
291{
292 Display *display = X11->display;
293 const int screens = ScreenCount(display);
294
295 cmaps = new QXcbColormap*[screens];
296
297 for (int i = 0; i < screens; ++i) {
298 cmaps[i] = new QXcbColormap;
299 QXcbColormapPrivate * const d = cmaps[i]->d;
300
301 bool use_stdcmap = false;
302 int color_count = X11->color_count;
303
304 // defaults
305 d->depth = DefaultDepth(display, i);
306 d->colormap = DefaultColormap(display, i);
307 d->defaultColormap = true;
308 d->visual = DefaultVisual(display, i);
309 d->defaultVisual = true;
310
311 Visual *argbVisual = nullptr;
312
313 if (X11->visual && i == DefaultScreen(display)) {
314 // only use the outside colormap on the default screen
315 d->visual = find_visual(display, i, X11->visual->c_class,
316 XVisualIDFromVisual(X11->visual),
318 } else if ((X11->visual_class != -1 && X11->visual_class >= 0 && X11->visual_class < 6)
319 || (X11->visual_id != -1)) {
320 // look for a specific visual or type of visual
323 } else if (!X11->custom_cmap) {
324 XStandardColormap *stdcmap = nullptr;
325 int ncmaps = 0;
326
327#if QT_CONFIG(xrender)
328 if (X11->use_xrender) {
329 int nvi;
330 XVisualInfo templ;
331 templ.screen = i;
332 templ.depth = 32;
333 templ.c_class = TrueColor;
334 XVisualInfo *xvi = XGetVisualInfo(X11->display, VisualScreenMask |
335 VisualDepthMask |
336 VisualClassMask, &templ, &nvi);
337 for (int idx = 0; idx < nvi; ++idx) {
338 XRenderPictFormat *format = XRenderFindVisualFormat(X11->display,
339 xvi[idx].visual);
340 if (format->type == PictTypeDirect && format->direct.alphaMask) {
341 argbVisual = xvi[idx].visual;
342 break;
343 }
344 }
345 XFree(xvi);
346 }
347#endif
348 if (XGetRGBColormaps(display, RootWindow(display, i),
349 &stdcmap, &ncmaps, XA_RGB_DEFAULT_MAP)) {
350 if (stdcmap) {
351 for (int c = 0; c < ncmaps; ++c) {
352 if (!stdcmap[c].red_max ||
353 !stdcmap[c].green_max ||
354 !stdcmap[c].blue_max ||
355 !stdcmap[c].red_mult ||
356 !stdcmap[c].green_mult ||
357 !stdcmap[c].blue_mult)
358 continue; // invalid stdcmap
359
360 XVisualInfo proto;
361 proto.visualid = stdcmap[c].visualid;
362 proto.screen = i;
363
364 int nvisuals = 0;
365 XVisualInfo *vi = XGetVisualInfo(display, VisualIDMask | VisualScreenMask,
366 &proto, &nvisuals);
367 if (vi) {
368 if (nvisuals > 0) {
369 use_stdcmap = true;
370
371 d->mode = ((vi[0].visual->c_class < StaticColor)
372 ? Gray
373 : ((vi[0].visual->c_class < TrueColor)
374 ? Indexed
375 : Direct));
376
377 d->depth = vi[0].depth;
378 d->colormap = stdcmap[c].colormap;
379 d->defaultColormap = true;
380 d->visual = vi[0].visual;
381 d->defaultVisual = (d->visual == DefaultVisual(display, i));
382
383 d->r_max = stdcmap[c].red_max + 1;
384 d->g_max = stdcmap[c].green_max + 1;
385 d->b_max = stdcmap[c].blue_max + 1;
386
387 if (d->mode == Direct) {
388 // calculate offsets
389 d->r_shift = lowest_bit(d->visual->red_mask);
390 d->g_shift = lowest_bit(d->visual->green_mask);
391 d->b_shift = lowest_bit(d->visual->blue_mask);
392 } else {
393 d->r_shift = 0;
394 d->g_shift = 0;
395 d->b_shift = 0;
396 }
397 }
398 XFree(vi);
399 }
400 break;
401 }
402 XFree(stdcmap);
403 }
404 }
405 }
406 if (!use_stdcmap) {
407 switch (d->visual->c_class) {
408 case StaticGray:
409 d->mode = Gray;
410
411 d->r_max = d->g_max = d->b_max = d->visual->map_entries;
412 break;
413
414 case XGrayScale:
415 d->mode = Gray;
416
417 // follow precedent set in libXmu...
418 if (color_count != 0)
419 d->r_max = d->g_max = d->b_max = color_count;
420 else if (d->visual->map_entries > 65000)
421 d->r_max = d->g_max = d->b_max = 4096;
422 else if (d->visual->map_entries > 4000)
423 d->r_max = d->g_max = d->b_max = 512;
424 else if (d->visual->map_entries > 250)
425 d->r_max = d->g_max = d->b_max = 12;
426 else
427 d->r_max = d->g_max = d->b_max = 4;
428 break;
429
430 case StaticColor:
431 d->mode = Indexed;
432
433 d->r_max = right_align(d->visual->red_mask) + 1;
434 d->g_max = right_align(d->visual->green_mask) + 1;
435 d->b_max = right_align(d->visual->blue_mask) + 1;
436 break;
437
438 case PseudoColor:
439 d->mode = Indexed;
440
441 // follow precedent set in libXmu...
442 if (color_count != 0)
443 d->r_max = d->g_max = d->b_max = cube_root(color_count);
444 else if (d->visual->map_entries > 65000)
445 d->r_max = d->g_max = d->b_max = 27;
446 else if (d->visual->map_entries > 4000)
447 d->r_max = d->g_max = d->b_max = 12;
448 else if (d->visual->map_entries > 250)
449 d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries - 125);
450 else
451 d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries);
452 break;
453
454 case TrueColor:
455 case DirectColor:
456 d->mode = Direct;
457
458 d->r_max = right_align(d->visual->red_mask) + 1;
459 d->g_max = right_align(d->visual->green_mask) + 1;
460 d->b_max = right_align(d->visual->blue_mask) + 1;
461
462 d->r_shift = lowest_bit(d->visual->red_mask);
463 d->g_shift = lowest_bit(d->visual->green_mask);
464 d->b_shift = lowest_bit(d->visual->blue_mask);
465 break;
466 }
467 }
468
469 bool ownColormap = false;
470 if (X11->colormap && i == DefaultScreen(display)) {
471 // only use the outside colormap on the default screen
473 d->defaultColormap = (d->colormap == DefaultColormap(display, i));
474 } else if ((!use_stdcmap
475 && (((d->visual->c_class & 1) && X11->custom_cmap)
476 || d->visual != DefaultVisual(display, i)))
477 || d->visual->c_class == DirectColor) {
478 // allocate custom colormap (we always do this when using DirectColor visuals)
479 d->colormap =
480 XCreateColormap(display, RootWindow(display, i), d->visual,
481 d->visual->c_class == DirectColor ? AllocAll : AllocNone);
482 d->defaultColormap = false;
483 ownColormap = true;
484 }
485
486 switch (d->mode) {
487 case Gray:
488 init_gray(d, i);
489 break;
490 case Indexed:
492 break;
493 case Direct:
494 init_direct(d, ownColormap);
495 break;
496 }
497
498 QX11InfoData *screen = X11->screens + i;
499 screen->depth = d->depth;
500 screen->visual = d->visual;
502 screen->colormap = d->colormap;
504 screen->cells = screen->visual->map_entries;
505
506 if (argbVisual) {
507 X11->argbVisuals[i] = argbVisual;
508 X11->argbColormaps[i] = XCreateColormap(display, RootWindow(display, i), argbVisual, AllocNone);
509 }
510
511 // ###
512 // We assume that 8bpp == pseudocolor, but this is not
513 // always the case (according to the X server), so we need
514 // to make sure that our internal data is setup in a way
515 // that is compatible with our assumptions
516 if (screen->visual->c_class == TrueColor && screen->depth == 8 && screen->cells == 8)
517 screen->cells = 256;
518 }
519}
520
522{
523 Display *display = X11->display;
524 const int screens = ScreenCount(display);
525
526 for (int i = 0; i < screens; ++i)
527 delete cmaps[i];
528
529 delete [] cmaps;
530 cmaps = 0;
531}
532
533
535{
536 if (screen == -1)
538 return *cmaps[screen];
539}
540
541/*! \internal
542 Constructs a new colormap.
543*/
544QXcbColormap::QXcbColormap()
545 : d(new QXcbColormapPrivate)
546{}
547
549 :d (colormap.d)
550{ d->ref.ref(); }
551
553{
554 if (!d->ref.deref()) {
555 if (!d->defaultColormap)
556 XFreeColormap(X11->display, d->colormap);
557 delete d;
558 }
559}
560
562{ return d->mode; }
563
564int QXcbColormap::depth() const
565{ return d->depth; }
566
567int QXcbColormap::size() const
568{
569 return (d->mode == Gray
570 ? d->r_max
571 : (d->mode == Indexed
572 ? d->r_max * d->g_max * d->b_max
573 : -1));
574}
575
576uint QXcbColormap::pixel(const QColor &color) const
577{
578 const QRgba64 rgba64 = color.rgba64();
579 // XXX We emulate the raster engine here by getting the
580 // 8-bit values, but we could instead use the 16-bit
581 // values for slightly better color accuracy
582 const uint r = (rgba64.red8() * d->r_max) >> 8;
583 const uint g = (rgba64.green8() * d->g_max) >> 8;
584 const uint b = (rgba64.blue8() * d->b_max) >> 8;
585 if (d->mode != Direct) {
586 if (d->mode == Gray)
587 return d->pixels.at((r * 30 + g * 59 + b * 11) / 100);
588 return d->pixels.at(r * d->g_max * d->b_max + g * d->b_max + b);
589 }
590 return (r << d->r_shift) + (g << d->g_shift) + (b << d->b_shift);
591}
592
593const QColor QXcbColormap::colorAt(uint pixel) const
594{
595 if (d->mode != Direct) {
596 Q_ASSERT(pixel <= (uint)d->colors.size());
597 return d->colors.at(pixel);
598 }
599
600 const int r = (((pixel & d->visual->red_mask) >> d->r_shift) << 8) / d->r_max;
601 const int g = (((pixel & d->visual->green_mask) >> d->g_shift) << 8) / d->g_max;
602 const int b = (((pixel & d->visual->blue_mask) >> d->b_shift) << 8) / d->b_max;
603 return QColor(r, g, b);
604}
605
607{ return d->colors; }
608
610{
611 qAtomicAssign(d, colormap.d);
612 return *this;
613}
614
615QT_END_NAMESPACE
QXcbColormap::Mode mode
QList< QColor > colors
const QList< QColor > colormap() const
int depth() const
Mode mode() const
static QXcbColormap instance(int screen=-1)
static void initialize()
const QColor colorAt(uint pixel) const
static void cleanup()
int size() const
QXcbColormap & operator=(const QXcbColormap &colormap)
uint pixel(const QColor &color) const
QXcbColormap(const QXcbColormap &colormap)
static int appScreen()
static void query_colormap(QXcbColormapPrivate *d, int screen)
static Visual * find_visual(Display *display, int screen, int visual_class, int visual_id, int *depth, bool *defaultVisual)
static void init_direct(QXcbColormapPrivate *d, bool ownColormap)
static void init_indexed(QXcbColormapPrivate *d, int screen)
static int cube_root(int v)
static QXcbColormap ** cmaps
static uint right_align(uint v)
static void init_gray(QXcbColormapPrivate *d, int screen)
#define X11
XID Colormap
Visual * visual
Definition qt_x11_p.h:150
bool defaultColormap
Definition qt_x11_p.h:151
Colormap colormap
Definition qt_x11_p.h:149
bool defaultVisual
Definition qt_x11_p.h:152
bool custom_cmap
Definition qt_x11_p.h:108
Visual ** argbVisuals
Definition qt_x11_p.h:99
Display * display
Definition qt_x11_p.h:91
int visual_id
Definition qt_x11_p.h:106
int color_count
Definition qt_x11_p.h:107
Visual * visual
Definition qt_x11_p.h:111
Colormap * argbColormaps
Definition qt_x11_p.h:100
int visual_class
Definition qt_x11_p.h:105
Colormap colormap
Definition qt_x11_p.h:112
QX11InfoData * screens
Definition qt_x11_p.h:98