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