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