1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 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 "qxcbimage.h"
41 #include <QtCore/QtEndian>
42 #include <QtGui/QColor>
43 #include <QtGui/private/qimage_p.h>
44 #include <QtGui/private/qdrawhelper_p.h>
45 
46 #include <xcb/render.h>
47 #include <xcb/xcb_renderutil.h>
48 
49 #include "qxcbconnection.h"
50 #include "qxcbintegration.h"
51 
52 namespace {
53 
imageFormatForMasks(int depth,int bits_per_pixel,int red_mask,int blue_mask)54 QImage::Format imageFormatForMasks(int depth, int bits_per_pixel, int red_mask, int blue_mask)
55 {
56     if (bits_per_pixel == 32) {
57         switch (depth) {
58         case 32:
59             if (red_mask == 0xff0000 && blue_mask == 0xff)
60                 return QImage::Format_ARGB32_Premultiplied;
61 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
62             if (red_mask == 0xff && blue_mask == 0xff0000)
63                 return QImage::Format_RGBA8888_Premultiplied;
64 #else
65             if (unsigned(red_mask) == unsigned(0xff000000) && blue_mask == 0xff00)
66                 return QImage::Format_RGBA8888_Premultiplied;
67 #endif
68             if (red_mask == 0x3ff && blue_mask == 0x3ff00000)
69                 return QImage::Format_A2BGR30_Premultiplied;
70             if (red_mask == 0x3ff00000 && blue_mask == 0x3ff)
71                 return QImage::Format_A2RGB30_Premultiplied;
72             break;
73         case 30:
74             if (red_mask == 0x3ff && blue_mask == 0x3ff00000)
75                 return QImage::Format_BGR30;
76             if (blue_mask == 0x3ff && red_mask == 0x3ff00000)
77                 return QImage::Format_RGB30;
78             break;
79         case 24:
80             if (red_mask == 0xff0000 && blue_mask == 0xff)
81                 return QImage::Format_RGB32;
82 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
83             if (red_mask == 0xff && blue_mask == 0xff0000)
84                 return QImage::Format_RGBX8888;
85 #else
86             if (unsigned(red_mask) == unsigned(0xff000000) && blue_mask == 0xff00)
87                 return QImage::Format_RGBX8888;
88 #endif
89             break;
90         }
91     } else if (bits_per_pixel == 16) {
92         if (depth == 16 && red_mask == 0xf800 && blue_mask == 0x1f)
93             return QImage::Format_RGB16;
94         if (depth == 15 && red_mask == 0x7c00 && blue_mask == 0x1f)
95             return QImage::Format_RGB555;
96     }
97     return QImage::Format_Invalid;
98 }
99 
100 } // namespace
101 
102 QT_BEGIN_NAMESPACE
103 
qt_xcb_imageFormatForVisual(QXcbConnection * connection,uint8_t depth,const xcb_visualtype_t * visual,QImage::Format * imageFormat,bool * needsRgbSwap)104 bool qt_xcb_imageFormatForVisual(QXcbConnection *connection, uint8_t depth, const xcb_visualtype_t *visual,
105                                  QImage::Format *imageFormat, bool *needsRgbSwap)
106 {
107     Q_ASSERT(connection && visual && imageFormat);
108 
109     if (needsRgbSwap)
110         *needsRgbSwap = false;
111     *imageFormat = QImage::Format_Invalid;
112 
113     if (depth == 8) {
114         if (visual->_class == XCB_VISUAL_CLASS_GRAY_SCALE) {
115             *imageFormat = QImage::Format_Grayscale8;
116             return true;
117         }
118 #if QT_CONFIG(xcb_native_painting)
119         if (QXcbIntegration::instance() && QXcbIntegration::instance()->nativePaintingEnabled()) {
120             *imageFormat = QImage::Format_Indexed8;
121             return true;
122         }
123 #endif
124         return false;
125     }
126 
127     const xcb_format_t *format = connection->formatForDepth(depth);
128     if (!format)
129         return false;
130 
131     const bool connectionEndianSwap = connection->imageNeedsEndianSwap();
132     // We swap the masks and see if we can recognize it as a host format
133     const quint32 red_mask = connectionEndianSwap ? qbswap(visual->red_mask) : visual->red_mask;
134     const quint32 blue_mask = connectionEndianSwap ? qbswap(visual->blue_mask) : visual->blue_mask;
135 
136     *imageFormat = imageFormatForMasks(depth, format->bits_per_pixel, red_mask, blue_mask);
137     if (*imageFormat != QImage::Format_Invalid)
138         return true;
139 
140     if (needsRgbSwap) {
141         *imageFormat = imageFormatForMasks(depth, format->bits_per_pixel, blue_mask, red_mask);
142         if (*imageFormat != QImage::Format_Invalid) {
143             *needsRgbSwap = true;
144             return true;
145         }
146     }
147 
148     qWarning("Unsupported screen format: depth: %d, bits_per_pixel: %d, red_mask: %x, blue_mask: %x", depth, format->bits_per_pixel, red_mask, blue_mask);
149 
150     return false;
151 }
152 
qt_xcb_pixmapFromXPixmap(QXcbConnection * connection,xcb_pixmap_t pixmap,int width,int height,int depth,const xcb_visualtype_t * visual)153 QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap,
154                                  int width, int height, int depth,
155                                  const xcb_visualtype_t *visual)
156 {
157     xcb_connection_t *conn = connection->xcb_connection();
158 
159     auto image_reply = Q_XCB_REPLY_UNCHECKED(xcb_get_image, conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap,
160                                              0, 0, width, height, 0xffffffff);
161     if (!image_reply) {
162         return QPixmap();
163     }
164 
165     uint8_t *data = xcb_get_image_data(image_reply.get());
166     uint32_t length = xcb_get_image_data_length(image_reply.get());
167 
168     QPixmap result;
169 
170     QImage::Format format;
171     bool needsRgbSwap;
172     if (qt_xcb_imageFormatForVisual(connection, depth, visual, &format, &needsRgbSwap)) {
173         uint32_t bytes_per_line = length / height;
174         QImage image(const_cast<uint8_t *>(data), width, height, bytes_per_line, format);
175 
176         if (needsRgbSwap)
177             image = std::move(image).rgbSwapped();
178 
179         // fix-up alpha channel
180         if (format == QImage::Format_RGB32 || format == QImage::Format_RGBX8888) {
181             QRgb *p = (QRgb *)image.bits();
182             for (int y = 0; y < height; ++y) {
183                 for (int x = 0; x < width; ++x)
184                     p[x] |= 0xff000000;
185                 p += bytes_per_line / 4;
186             }
187         } else if (format == QImage::Format_BGR30 || format == QImage::Format_RGB30) {
188             QRgb *p = (QRgb *)image.bits();
189             for (int y = 0; y < height; ++y) {
190                 for (int x = 0; x < width; ++x)
191                     p[x] |= 0xc0000000;
192                 p += bytes_per_line / 4;
193             }
194         }
195 
196         result = QPixmap::fromImage(image.copy());
197     }
198 
199     return result;
200 }
201 
qt_xcb_XPixmapFromBitmap(QXcbScreen * screen,const QImage & image)202 xcb_pixmap_t qt_xcb_XPixmapFromBitmap(QXcbScreen *screen, const QImage &image)
203 {
204     xcb_connection_t *conn = screen->xcb_connection();
205     QImage bitmap = image.convertToFormat(QImage::Format_MonoLSB);
206     const QRgb c0 = QColor(Qt::black).rgb();
207     const QRgb c1 = QColor(Qt::white).rgb();
208     if (bitmap.color(0) == c0 && bitmap.color(1) == c1) {
209         bitmap.invertPixels();
210         bitmap.setColor(0, c1);
211         bitmap.setColor(1, c0);
212     }
213     const int width = bitmap.width();
214     const int height = bitmap.height();
215     const int bytesPerLine = bitmap.bytesPerLine();
216     int destLineSize = width / 8;
217     if (width % 8)
218         ++destLineSize;
219     const uchar *map = bitmap.bits();
220     uint8_t *buf = new uint8_t[height * destLineSize];
221     for (int i = 0; i < height; i++)
222         memcpy(buf + (destLineSize * i), map + (bytesPerLine * i), destLineSize);
223     xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(conn, screen->root(), buf,
224                                                          width, height, 1, 0, 0, nullptr);
225     delete[] buf;
226     return pm;
227 }
228 
qt_xcb_createCursorXRender(QXcbScreen * screen,const QImage & image,const QPoint & spot)229 xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image,
230                                         const QPoint &spot)
231 {
232     xcb_connection_t *conn = screen->xcb_connection();
233     const int w = image.width();
234     const int h = image.height();
235     auto formats = Q_XCB_REPLY(xcb_render_query_pict_formats, conn);
236     if (!formats) {
237         qWarning("qt_xcb_createCursorXRender: query_pict_formats failed");
238         return XCB_NONE;
239     }
240     xcb_render_pictforminfo_t *fmt = xcb_render_util_find_standard_format(formats.get(),
241                                                                           XCB_PICT_STANDARD_ARGB_32);
242     if (!fmt) {
243         qWarning("qt_xcb_createCursorXRender: Failed to find format PICT_STANDARD_ARGB_32");
244         return XCB_NONE;
245     }
246 
247     QImage img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
248     xcb_image_t *xi = xcb_image_create(w, h, XCB_IMAGE_FORMAT_Z_PIXMAP,
249                                        32, 32, 32, 32,
250                                        QSysInfo::ByteOrder == QSysInfo::BigEndian ? XCB_IMAGE_ORDER_MSB_FIRST : XCB_IMAGE_ORDER_LSB_FIRST,
251                                        XCB_IMAGE_ORDER_MSB_FIRST,
252                                        nullptr, 0, nullptr);
253     if (!xi) {
254         qWarning("qt_xcb_createCursorXRender: xcb_image_create failed");
255         return XCB_NONE;
256     }
257     xi->data = (uint8_t *) malloc(xi->stride * h);
258     if (!xi->data) {
259         qWarning("qt_xcb_createCursorXRender: Failed to malloc() image data");
260         xcb_image_destroy(xi);
261         return XCB_NONE;
262     }
263     memcpy(xi->data, img.constBits(), img.sizeInBytes());
264 
265     xcb_pixmap_t pix = xcb_generate_id(conn);
266     xcb_create_pixmap(conn, 32, pix, screen->root(), w, h);
267 
268     xcb_render_picture_t pic = xcb_generate_id(conn);
269     xcb_render_create_picture(conn, pic, pix, fmt->id, 0, nullptr);
270 
271     xcb_gcontext_t gc = xcb_generate_id(conn);
272     xcb_create_gc(conn, gc, pix, 0, nullptr);
273     xcb_image_put(conn, pix, gc, xi, 0, 0, 0);
274     xcb_free_gc(conn, gc);
275 
276     xcb_cursor_t cursor = xcb_generate_id(conn);
277     xcb_render_create_cursor(conn, cursor, pic, spot.x(), spot.y());
278 
279     free(xi->data);
280     xcb_image_destroy(xi);
281     xcb_render_free_picture(conn, pic);
282     xcb_free_pixmap(conn, pix);
283     return cursor;
284 }
285 
286 QT_END_NAMESPACE
287