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