1 | // Copyright (C) 2018 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 "qopengltextureuploader_p.h" |
5 | |
6 | #include <qimage.h> |
7 | #include <qmath.h> |
8 | #include <qopenglfunctions.h> |
9 | #include <private/qopenglcontext_p.h> |
10 | #include <private/qopenglextensions_p.h> |
11 | |
12 | #ifndef GL_HALF_FLOAT |
13 | #define GL_HALF_FLOAT 0x140B |
14 | #endif |
15 | |
16 | #ifndef GL_RED |
17 | #define GL_RED 0x1903 |
18 | #endif |
19 | |
20 | #ifndef GL_GREEN |
21 | #define GL_GREEN 0x1904 |
22 | #endif |
23 | |
24 | #ifndef GL_BLUE |
25 | #define GL_BLUE 0x1905 |
26 | #endif |
27 | |
28 | #ifndef GL_RGB10_A2 |
29 | #define GL_RGB10_A2 0x8059 |
30 | #endif |
31 | |
32 | #ifndef GL_RGBA16 |
33 | #define GL_RGBA16 0x805B |
34 | #endif |
35 | |
36 | #ifndef GL_BGR |
37 | #define GL_BGR 0x80E0 |
38 | #endif |
39 | |
40 | #ifndef GL_BGRA |
41 | #define GL_BGRA 0x80E1 |
42 | #endif |
43 | |
44 | #ifndef GL_UNSIGNED_INT_8_8_8_8_REV |
45 | #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 |
46 | #endif |
47 | |
48 | #ifndef GL_UNSIGNED_INT_2_10_10_10_REV |
49 | #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 |
50 | #endif |
51 | |
52 | #ifndef GL_RGBA16F |
53 | #define GL_RGBA16F 0x881A |
54 | #endif |
55 | |
56 | #ifndef GL_RGBA32F |
57 | #define GL_RGBA32F 0x8814 |
58 | #endif |
59 | |
60 | #ifndef GL_TEXTURE_SWIZZLE_R |
61 | #define GL_TEXTURE_SWIZZLE_R 0x8E42 |
62 | #endif |
63 | |
64 | #ifndef GL_TEXTURE_SWIZZLE_G |
65 | #define GL_TEXTURE_SWIZZLE_G 0x8E43 |
66 | #endif |
67 | |
68 | #ifndef GL_TEXTURE_SWIZZLE_B |
69 | #define GL_TEXTURE_SWIZZLE_B 0x8E44 |
70 | #endif |
71 | |
72 | #ifndef GL_TEXTURE_SWIZZLE_A |
73 | #define GL_TEXTURE_SWIZZLE_A 0x8E45 |
74 | #endif |
75 | |
76 | #ifndef GL_SRGB |
77 | #define GL_SRGB 0x8C40 |
78 | #endif |
79 | #ifndef GL_SRGB_ALPHA |
80 | #define GL_SRGB_ALPHA 0x8C42 |
81 | #endif |
82 | |
83 | QT_BEGIN_NAMESPACE |
84 | |
85 | qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &image, QOpenGLTextureUploader::BindOptions options, QSize maxSize) |
86 | { |
87 | QOpenGLContext *context = QOpenGLContext::currentContext(); |
88 | QOpenGLExtensions *funcs = static_cast<QOpenGLExtensions*>(context->functions()); |
89 | |
90 | QImage tx; |
91 | GLenum externalFormat; |
92 | GLenum internalFormat; |
93 | GLuint pixelType; |
94 | QImage::Format targetFormat = QImage::Format_Invalid; |
95 | const bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2); |
96 | const bool isOpenGLES3orBetter = context->isOpenGLES() && context->format().majorVersion() >= 3; |
97 | const bool sRgbBinding = (options & SRgbBindOption); |
98 | Q_ASSERT(isOpenGL12orBetter || context->isOpenGLES()); |
99 | Q_ASSERT((options & (SRgbBindOption | UseRedForAlphaAndLuminanceBindOption)) != (SRgbBindOption | UseRedForAlphaAndLuminanceBindOption)); |
100 | |
101 | switch (image.format()) { |
102 | case QImage::Format_RGB32: |
103 | case QImage::Format_ARGB32: |
104 | case QImage::Format_ARGB32_Premultiplied: |
105 | if (isOpenGL12orBetter) { |
106 | externalFormat = GL_BGRA; |
107 | internalFormat = GL_RGBA; |
108 | pixelType = GL_UNSIGNED_INT_8_8_8_8_REV; |
109 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
110 | // Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian: |
111 | } else if (funcs->hasOpenGLExtension(extension: QOpenGLExtensions::BGRATextureFormat) && !sRgbBinding) { |
112 | // The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external. |
113 | externalFormat = internalFormat = GL_BGRA; |
114 | pixelType = GL_UNSIGNED_BYTE; |
115 | } else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888" ))) { |
116 | // Is only allowed as an external format like OpenGL. |
117 | externalFormat = GL_BGRA; |
118 | internalFormat = GL_RGBA; |
119 | pixelType = GL_UNSIGNED_BYTE; |
120 | #endif |
121 | } else if (funcs->hasOpenGLExtension(extension: QOpenGLExtensions::TextureSwizzle)) { |
122 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
123 | funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_BLUE); |
124 | funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED); |
125 | #else |
126 | funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_GREEN); |
127 | funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, GL_BLUE); |
128 | funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_ALPHA); |
129 | funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, GL_RED); |
130 | #endif |
131 | externalFormat = internalFormat = GL_RGBA; |
132 | pixelType = GL_UNSIGNED_BYTE; |
133 | } else { |
134 | // No support for direct ARGB32 upload. |
135 | break; |
136 | } |
137 | targetFormat = image.format(); |
138 | break; |
139 | case QImage::Format_BGR30: |
140 | case QImage::Format_A2BGR30_Premultiplied: |
141 | if (sRgbBinding) { |
142 | // Always needs conversion |
143 | break; |
144 | } else if (isOpenGL12orBetter || isOpenGLES3orBetter) { |
145 | pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; |
146 | externalFormat = GL_RGBA; |
147 | internalFormat = GL_RGB10_A2; |
148 | targetFormat = image.format(); |
149 | } |
150 | break; |
151 | case QImage::Format_RGB30: |
152 | case QImage::Format_A2RGB30_Premultiplied: |
153 | if (sRgbBinding) { |
154 | // Always needs conversion |
155 | break; |
156 | } else if (isOpenGL12orBetter) { |
157 | pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; |
158 | externalFormat = GL_BGRA; |
159 | internalFormat = GL_RGB10_A2; |
160 | targetFormat = image.format(); |
161 | } else if (funcs->hasOpenGLExtension(extension: QOpenGLExtensions::TextureSwizzle)) { |
162 | funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED); |
163 | funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_BLUE); |
164 | pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; |
165 | externalFormat = GL_RGBA; |
166 | internalFormat = GL_RGB10_A2; |
167 | targetFormat = image.format(); |
168 | } |
169 | break; |
170 | case QImage::Format_RGB444: |
171 | case QImage::Format_RGB555: |
172 | case QImage::Format_RGB16: |
173 | if (isOpenGL12orBetter || context->isOpenGLES()) { |
174 | externalFormat = internalFormat = GL_RGB; |
175 | pixelType = GL_UNSIGNED_SHORT_5_6_5; |
176 | targetFormat = QImage::Format_RGB16; |
177 | } |
178 | break; |
179 | case QImage::Format_RGB666: |
180 | case QImage::Format_RGB888: |
181 | externalFormat = internalFormat = GL_RGB; |
182 | pixelType = GL_UNSIGNED_BYTE; |
183 | targetFormat = QImage::Format_RGB888; |
184 | break; |
185 | case QImage::Format_BGR888: |
186 | if (isOpenGL12orBetter) { |
187 | externalFormat = GL_BGR; |
188 | internalFormat = GL_RGB; |
189 | pixelType = GL_UNSIGNED_BYTE; |
190 | targetFormat = QImage::Format_BGR888; |
191 | } else if (funcs->hasOpenGLExtension(extension: QOpenGLExtensions::TextureSwizzle)) { |
192 | funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED); |
193 | funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_BLUE); |
194 | externalFormat = internalFormat = GL_RGB; |
195 | pixelType = GL_UNSIGNED_BYTE; |
196 | targetFormat = QImage::Format_BGR888; |
197 | } |
198 | break; |
199 | case QImage::Format_RGBX8888: |
200 | case QImage::Format_RGBA8888: |
201 | case QImage::Format_RGBA8888_Premultiplied: |
202 | externalFormat = internalFormat = GL_RGBA; |
203 | pixelType = GL_UNSIGNED_BYTE; |
204 | targetFormat = image.format(); |
205 | break; |
206 | case QImage::Format_RGBX64: |
207 | case QImage::Format_RGBA64: |
208 | case QImage::Format_RGBA64_Premultiplied: |
209 | externalFormat = internalFormat = GL_RGBA; |
210 | if (isOpenGL12orBetter || (context->isOpenGLES() && context->format().majorVersion() >= 3)) |
211 | internalFormat = GL_RGBA16; |
212 | pixelType = GL_UNSIGNED_SHORT; |
213 | targetFormat = image.format(); |
214 | break; |
215 | case QImage::Format_RGBX16FPx4: |
216 | case QImage::Format_RGBA16FPx4: |
217 | case QImage::Format_RGBA16FPx4_Premultiplied: |
218 | if (context->format().majorVersion() >= 3) { |
219 | externalFormat = GL_RGBA; |
220 | internalFormat = GL_RGBA16F; |
221 | pixelType = GL_HALF_FLOAT; |
222 | targetFormat = image.format(); |
223 | } |
224 | break; |
225 | case QImage::Format_RGBX32FPx4: |
226 | case QImage::Format_RGBA32FPx4: |
227 | case QImage::Format_RGBA32FPx4_Premultiplied: |
228 | externalFormat = internalFormat = GL_RGBA; |
229 | if (context->format().majorVersion() >= 3) |
230 | internalFormat = GL_RGBA32F; |
231 | pixelType = GL_FLOAT; |
232 | targetFormat = image.format(); |
233 | break; |
234 | case QImage::Format_Indexed8: |
235 | if (sRgbBinding) { |
236 | // Always needs conversion |
237 | break; |
238 | } else if (options & UseRedForAlphaAndLuminanceBindOption) { |
239 | externalFormat = internalFormat = GL_RED; |
240 | pixelType = GL_UNSIGNED_BYTE; |
241 | targetFormat = image.format(); |
242 | } |
243 | break; |
244 | case QImage::Format_Alpha8: |
245 | if (sRgbBinding) { |
246 | // Always needs conversion |
247 | break; |
248 | } else if (options & UseRedForAlphaAndLuminanceBindOption) { |
249 | externalFormat = internalFormat = GL_RED; |
250 | pixelType = GL_UNSIGNED_BYTE; |
251 | targetFormat = image.format(); |
252 | } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) { |
253 | externalFormat = internalFormat = GL_ALPHA; |
254 | pixelType = GL_UNSIGNED_BYTE; |
255 | targetFormat = image.format(); |
256 | } else if (funcs->hasOpenGLExtension(extension: QOpenGLExtensions::TextureSwizzle)) { |
257 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ALPHA); |
258 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ZERO); |
259 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ZERO); |
260 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ZERO); |
261 | externalFormat = internalFormat = GL_RED; |
262 | pixelType = GL_UNSIGNED_BYTE; |
263 | targetFormat = image.format(); |
264 | } |
265 | break; |
266 | case QImage::Format_Grayscale8: |
267 | if (sRgbBinding) { |
268 | // Always needs conversion |
269 | break; |
270 | } else if (options & UseRedForAlphaAndLuminanceBindOption) { |
271 | externalFormat = internalFormat = GL_RED; |
272 | pixelType = GL_UNSIGNED_BYTE; |
273 | targetFormat = image.format(); |
274 | } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) { |
275 | externalFormat = internalFormat = GL_LUMINANCE; |
276 | pixelType = GL_UNSIGNED_BYTE; |
277 | targetFormat = image.format(); |
278 | } else if (funcs->hasOpenGLExtension(extension: QOpenGLExtensions::TextureSwizzle)) { |
279 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); |
280 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); |
281 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); |
282 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE); |
283 | externalFormat = internalFormat = GL_RED; |
284 | pixelType = GL_UNSIGNED_BYTE; |
285 | targetFormat = image.format(); |
286 | } |
287 | break; |
288 | case QImage::Format_Grayscale16: |
289 | if (sRgbBinding) { |
290 | // Always needs conversion |
291 | break; |
292 | } else if (options & UseRedForAlphaAndLuminanceBindOption) { |
293 | externalFormat = internalFormat = GL_RED; |
294 | pixelType = GL_UNSIGNED_SHORT; |
295 | targetFormat = image.format(); |
296 | } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) { |
297 | externalFormat = internalFormat = GL_LUMINANCE; |
298 | pixelType = GL_UNSIGNED_SHORT; |
299 | targetFormat = image.format(); |
300 | } else if (funcs->hasOpenGLExtension(extension: QOpenGLExtensions::TextureSwizzle)) { |
301 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); |
302 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); |
303 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); |
304 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE); |
305 | externalFormat = internalFormat = GL_RED; |
306 | pixelType = GL_UNSIGNED_SHORT; |
307 | targetFormat = image.format(); |
308 | } |
309 | break; |
310 | default: |
311 | break; |
312 | } |
313 | |
314 | // If no direct upload was detected above, convert to RGBA8888 and upload that |
315 | if (targetFormat == QImage::Format_Invalid) { |
316 | externalFormat = internalFormat = GL_RGBA; |
317 | pixelType = GL_UNSIGNED_BYTE; |
318 | if (!image.hasAlphaChannel()) |
319 | targetFormat = QImage::Format_RGBX8888; |
320 | else |
321 | targetFormat = QImage::Format_RGBA8888; |
322 | } |
323 | |
324 | if (options & PremultipliedAlphaBindOption) { |
325 | if (targetFormat == QImage::Format_ARGB32) |
326 | targetFormat = QImage::Format_ARGB32_Premultiplied; |
327 | else if (targetFormat == QImage::Format_RGBA8888) |
328 | targetFormat = QImage::Format_RGBA8888_Premultiplied; |
329 | else if (targetFormat == QImage::Format_RGBA64) |
330 | targetFormat = QImage::Format_RGBA64_Premultiplied; |
331 | else if (targetFormat == QImage::Format_RGBA16FPx4) |
332 | targetFormat = QImage::Format_RGBA16FPx4_Premultiplied; |
333 | else if (targetFormat == QImage::Format_RGBA32FPx4) |
334 | targetFormat = QImage::Format_RGBA32FPx4_Premultiplied; |
335 | } else { |
336 | if (targetFormat == QImage::Format_ARGB32_Premultiplied) |
337 | targetFormat = QImage::Format_ARGB32; |
338 | else if (targetFormat == QImage::Format_RGBA8888_Premultiplied) |
339 | targetFormat = QImage::Format_RGBA8888; |
340 | else if (targetFormat == QImage::Format_RGBA64_Premultiplied) |
341 | targetFormat = QImage::Format_RGBA64; |
342 | else if (targetFormat == QImage::Format_RGBA16FPx4_Premultiplied) |
343 | targetFormat = QImage::Format_RGBA16FPx4; |
344 | else if (targetFormat == QImage::Format_RGBA32FPx4_Premultiplied) |
345 | targetFormat = QImage::Format_RGBA32FPx4; |
346 | } |
347 | |
348 | if (sRgbBinding) { |
349 | Q_ASSERT(internalFormat == GL_RGBA || internalFormat == GL_RGB); |
350 | if (image.hasAlphaChannel()) |
351 | internalFormat = GL_SRGB_ALPHA; |
352 | else |
353 | internalFormat = GL_SRGB; |
354 | } |
355 | |
356 | if (image.format() != targetFormat) |
357 | tx = image.convertToFormat(f: targetFormat); |
358 | else |
359 | tx = image; |
360 | |
361 | QSize newSize = tx.size(); |
362 | if (!maxSize.isEmpty()) |
363 | newSize = newSize.boundedTo(otherSize: maxSize); |
364 | if (options & PowerOfTwoBindOption) { |
365 | newSize.setWidth(qNextPowerOfTwo(v: newSize.width() - 1)); |
366 | newSize.setHeight(qNextPowerOfTwo(v: newSize.height() - 1)); |
367 | } |
368 | |
369 | if (newSize != tx.size()) |
370 | tx = tx.scaled(s: newSize, aspectMode: Qt::IgnoreAspectRatio, mode: Qt::SmoothTransformation); |
371 | |
372 | // Handle cases where the QImage is actually a sub image of its image data: |
373 | qsizetype naturalBpl = ((qsizetype(tx.width()) * tx.depth() + 31) >> 5) << 2; |
374 | if (tx.bytesPerLine() != naturalBpl) |
375 | tx = tx.copy(rect: tx.rect()); |
376 | |
377 | funcs->glTexImage2D(target, level: 0, internalformat: internalFormat, width: tx.width(), height: tx.height(), border: 0, format: externalFormat, type: pixelType, pixels: tx.constBits()); |
378 | |
379 | qsizetype cost = qint64(tx.width()) * tx.height() * tx.depth() / 8; |
380 | |
381 | return cost; |
382 | } |
383 | |
384 | QT_END_NAMESPACE |
385 | |