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 <QtGui/qpa/qplatformgraphicsbuffer.h>
5
6#include "qplatformgraphicsbufferhelper.h"
7#include <QtCore/QDebug>
8#include <QtGui/qopengl.h>
9#include <QtGui/QImage>
10#include <QtGui/QOpenGLContext>
11#include <QtGui/QOpenGLFunctions>
12
13#ifndef GL_UNPACK_ROW_LENGTH
14#define GL_UNPACK_ROW_LENGTH 0x0CF2
15#endif
16
17#ifndef GL_RGB10_A2
18#define GL_RGB10_A2 0x8059
19#endif
20
21#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
22#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
23#endif
24
25QT_BEGIN_NAMESPACE
26
27/*!
28 \namespace QPlatformGraphicsBufferHelper
29 \inmodule QtGui
30 \internal
31*/
32
33/*!
34 Convenience function to both lock and bind the \a graphicsBuffer to a texture.
35 This function will first try to lock with texture read and texture write
36 access. If this succeeds it will use the bindToTexture function to bind the
37 content to the currently bound texture, and if \a premultiplied is provided,
38 it is set to false.
39
40 If it fails, it will try to lock with SWReadAccess and then use the
41 bindSWToTexture convenience function. If \a premultiplied is provided, it is
42 passed to the bindSWToTexture() function.
43
44 \a swizzle is meant to be used by the caller to figure out if the Red and
45 Blue color channels need to be swizzled when rendering.
46
47 \a rect is the subrect which is desired to be bounded to the texture. This
48 argument has a not less than semantic, meaning more (if not all) of the buffer
49 can be bounded to the texture. An empty QRect is interpreted as entire buffer
50 should be bound.
51
52 The user should use the AccessTypes returned by isLocked to figure out what
53 lock has been obtained.
54
55 Returns true if the buffer has successfully been bound to the currently
56 bound texture, otherwise returns false.
57*/
58bool QPlatformGraphicsBufferHelper::lockAndBindToTexture(QPlatformGraphicsBuffer *graphicsBuffer,
59 bool *swizzle, bool *premultiplied,
60 const QRect &rect)
61{
62 if (graphicsBuffer->lock(access: QPlatformGraphicsBuffer::TextureAccess)) {
63 if (!graphicsBuffer->bindToTexture(rect)) {
64 qWarning(msg: "Failed to bind %sgraphicsbuffer to texture", "");
65 return false;
66 }
67 if (swizzle)
68 *swizzle = false;
69 if (premultiplied)
70 *premultiplied = false;
71 } else if (graphicsBuffer->lock(access: QPlatformGraphicsBuffer::SWReadAccess)) {
72 if (!bindSWToTexture(graphicsBuffer, swizzleRandB: swizzle, premultipliedB: premultiplied, rect)) {
73 qWarning(msg: "Failed to bind %sgraphicsbuffer to texture", "SW ");
74 return false;
75 }
76 } else {
77 qWarning(msg: "Failed to lock");
78 return false;
79 }
80 return true;
81}
82
83/*!
84 Convenience function that uploads the current raster content to the currently
85 bound texture.
86
87 \a swizzleRandB is meant to be used by the caller to decide if the Red and
88 Blue color channels need to be swizzled when rendering. This is an
89 optimization. Qt often renders to software buffers interpreting pixels as
90 unsigned ints. When these buffers are uploaded to textures and each color
91 channel per pixel is interpreted as a byte (read sequentially), then the
92 Red and Blue channels are swapped. Conveniently, the Alpha buffer will be
93 correct, since Qt historically has had the alpha channel as the first
94 channel, while OpenGL typically expects the alpha channel to be the last
95 channel.
96
97 \a subRect is the region to be bound to the texture. This argument has a
98 not less than semantic, meaning more (if not all) of the buffer can be
99 bound to the texture. An empty QRect is interpreted as meaning the entire
100 buffer should be bound.
101
102 This function fails if the \a graphicsBuffer is not locked to SWAccess.
103
104 Returns true on success, otherwise false. If \a premultipliedB is
105 provided, it is set according to what happens, if the function returns
106 true.
107*/
108bool QPlatformGraphicsBufferHelper::bindSWToTexture(const QPlatformGraphicsBuffer *graphicsBuffer,
109 bool *swizzleRandB, bool *premultipliedB,
110 const QRect &subRect)
111{
112#ifndef QT_NO_OPENGL
113 QOpenGLContext *ctx = QOpenGLContext::currentContext();
114 if (!ctx)
115 return false;
116
117 if (!(graphicsBuffer->isLocked() & QPlatformGraphicsBuffer::SWReadAccess))
118 return false;
119
120 QSize size = graphicsBuffer->size();
121
122 Q_ASSERT(subRect.isEmpty() || QRect(QPoint(0,0), size).contains(subRect));
123
124 GLenum internalFormat = GL_RGBA;
125 GLuint pixelType = GL_UNSIGNED_BYTE;
126
127 bool needsConversion = false;
128 bool swizzle = false;
129 bool premultiplied = false;
130 QImage::Format imageformat = QImage::toImageFormat(format: graphicsBuffer->format());
131 QImage image(graphicsBuffer->data(), size.width(), size.height(), graphicsBuffer->bytesPerLine(), imageformat);
132 switch (imageformat) {
133 case QImage::Format_ARGB32_Premultiplied:
134 premultiplied = true;
135 Q_FALLTHROUGH();
136 case QImage::Format_RGB32:
137 case QImage::Format_ARGB32:
138 swizzle = true;
139 break;
140 case QImage::Format_RGBA8888_Premultiplied:
141 premultiplied = true;
142 Q_FALLTHROUGH();
143 case QImage::Format_RGBX8888:
144 case QImage::Format_RGBA8888:
145 break;
146 case QImage::Format_BGR30:
147 case QImage::Format_A2BGR30_Premultiplied:
148 if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
149 pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
150 internalFormat = GL_RGB10_A2;
151 premultiplied = true;
152 } else {
153 needsConversion = true;
154 }
155 break;
156 case QImage::Format_RGB30:
157 case QImage::Format_A2RGB30_Premultiplied:
158 if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
159 pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
160 internalFormat = GL_RGB10_A2;
161 premultiplied = true;
162 swizzle = true;
163 } else {
164 needsConversion = true;
165 }
166 break;
167 default:
168 needsConversion = true;
169 break;
170 }
171 if (!needsConversion && image.bytesPerLine() != (size.width() * 4) && ctx->isOpenGLES() && ctx->format().majorVersion() < 3)
172 needsConversion = true;
173 if (needsConversion)
174 image.convertTo(f: QImage::Format_RGBA8888);
175
176 bool needsRowLength = (image.bytesPerLine() != image.width() * 4);
177 QOpenGLFunctions *funcs = ctx->functions();
178
179 QRect rect = subRect;
180 if (rect.isNull() || rect == QRect(QPoint(0,0),size)) {
181 if (needsRowLength)
182 funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, param: GLint(image.bytesPerLine() / 4));
183 funcs->glTexImage2D(GL_TEXTURE_2D, level: 0, internalformat: internalFormat, width: size.width(), height: size.height(), border: 0, GL_RGBA, type: pixelType, pixels: image.constBits());
184 if (needsRowLength)
185 funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, param: 0);
186 } else {
187 if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
188 // OpenGL 2.1+ or OpenGL ES/3+
189 funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, param: GLint(image.bytesPerLine() / 4));
190 funcs->glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: rect.x(), yoffset: rect.y(), width: rect.width(), height: rect.height(), GL_RGBA, type: pixelType,
191 pixels: image.constScanLine(rect.y()) + rect.x() * 4);
192 funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, param: 0);
193 } else
194 {
195 // if the rect is wide enough it's cheaper to just
196 // extend it instead of doing an image copy
197 if (rect.width() >= size.width() / 2) {
198 rect.setX(0);
199 rect.setWidth(size.width());
200 }
201
202 // if the sub-rect is full-width we can pass the image data directly to
203 // OpenGL instead of copying, since there's no gap between scanlines
204
205 if (rect.width() == image.bytesPerLine() / 4) {
206 funcs->glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: 0, yoffset: rect.y(), width: rect.width(), height: rect.height(), GL_RGBA, type: pixelType,
207 pixels: image.constScanLine(rect.y()));
208 } else {
209 funcs->glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: rect.x(), yoffset: rect.y(), width: rect.width(), height: rect.height(), GL_RGBA, type: pixelType,
210 pixels: image.copy(rect).constBits());
211 }
212 }
213 }
214 if (swizzleRandB)
215 *swizzleRandB = swizzle;
216 if (premultipliedB)
217 *premultipliedB = premultiplied;
218
219 return true;
220
221#else
222 Q_UNUSED(graphicsBuffer);
223 Q_UNUSED(swizzleRandB);
224 Q_UNUSED(premultipliedB);
225 Q_UNUSED(subRect);
226 return false;
227#endif // QT_NO_OPENGL
228}
229
230QT_END_NAMESPACE
231

source code of qtbase/src/gui/kernel/qplatformgraphicsbufferhelper.cpp