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

source code of qtbase/src/plugins/platforms/xcb/qxcbimage.cpp