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
52namespace {
53
54QImage::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
102QT_BEGIN_NAMESPACE
103
104bool 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
153QPixmap 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
202xcb_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, 0);
225 delete[] buf;
226 return pm;
227}
228
229xcb_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 0, 0, 0);
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, 0);
270
271 xcb_gcontext_t gc = xcb_generate_id(conn);
272 xcb_create_gc(conn, gc, pix, 0, 0);
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
286QT_END_NAMESPACE
287