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