Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qopengltextureglyphcache.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
6#include <private/qopenglpaintengine_p.h>
7#include "private/qopenglengineshadersource_p.h"
8#include <private/qopenglextensions_p.h>
9#include <qrgb.h>
10#include <private/qdrawhelper_p.h>
11
13
14
16{
17 Q_CONSTINIT static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
18 return 1 + serial.fetchAndAddRelaxed(1);
19}
20
21QOpenGLTextureGlyphCache::QOpenGLTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix, const QColor &color)
22 : QImageTextureGlyphCache(format, matrix, color)
23 , m_textureResource(nullptr)
24 , pex(nullptr)
25 , m_blitProgram(nullptr)
26 , m_filterMode(Nearest)
27 , m_serialNumber(next_qopengltextureglyphcache_serial_number())
28 , m_buffer(QOpenGLBuffer::VertexBuffer)
29{
30#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG
31 qDebug(" -> QOpenGLTextureGlyphCache() %p for context %p.", this, QOpenGLContext::currentContext());
32#endif
33 m_vertexCoordinateArray[0] = -1.0f;
34 m_vertexCoordinateArray[1] = -1.0f;
35 m_vertexCoordinateArray[2] = 1.0f;
36 m_vertexCoordinateArray[3] = -1.0f;
37 m_vertexCoordinateArray[4] = 1.0f;
38 m_vertexCoordinateArray[5] = 1.0f;
39 m_vertexCoordinateArray[6] = -1.0f;
40 m_vertexCoordinateArray[7] = 1.0f;
41
42 m_textureCoordinateArray[0] = 0.0f;
43 m_textureCoordinateArray[1] = 0.0f;
44 m_textureCoordinateArray[2] = 1.0f;
45 m_textureCoordinateArray[3] = 0.0f;
46 m_textureCoordinateArray[4] = 1.0f;
47 m_textureCoordinateArray[5] = 1.0f;
48 m_textureCoordinateArray[6] = 0.0f;
49 m_textureCoordinateArray[7] = 1.0f;
50}
51
52QOpenGLTextureGlyphCache::~QOpenGLTextureGlyphCache()
53{
54#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG
55 qDebug(" -> ~QOpenGLTextureGlyphCache() %p.", this);
56#endif
57 clear();
58}
59
60#if !QT_CONFIG(opengles2)
61static inline bool isCoreProfile()
62{
63 return QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile;
64}
65#endif
66
67void QOpenGLTextureGlyphCache::createTextureData(int width, int height)
68{
69 QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
70 if (ctx == nullptr) {
71 qWarning("QOpenGLTextureGlyphCache::createTextureData: Called with no context");
72 return;
73 }
74
75 // create in QImageTextureGlyphCache baseclass is meant to be called
76 // only to create the initial image and does not preserve the content,
77 // so we don't call when this function is called from resize.
78 if (ctx->d_func()->workaround_brokenFBOReadBack && image().isNull())
79 QImageTextureGlyphCache::createTextureData(width, height);
80
81 // Make the lower glyph texture size 16 x 16.
82 if (width < 16)
83 width = 16;
84 if (height < 16)
85 height = 16;
86
87 if (m_textureResource && !m_textureResource->m_texture) {
88 delete m_textureResource;
89 m_textureResource = nullptr;
90 }
91
92 if (!m_textureResource)
93 m_textureResource = new QOpenGLGlyphTexture(ctx);
94
95 QOpenGLFunctions *funcs = ctx->functions();
96 funcs->glGenTextures(1, &m_textureResource->m_texture);
97 funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);
98
99 m_textureResource->m_width = width;
100 m_textureResource->m_height = height;
101
102 if (m_format == QFontEngine::Format_A32 || m_format == QFontEngine::Format_ARGB) {
103 QVarLengthArray<uchar> data(width * height * 4);
104 for (int i = 0; i < data.size(); ++i)
105 data[i] = 0;
106 funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]);
107 } else {
108 QVarLengthArray<uchar> data(width * height);
109 for (int i = 0; i < data.size(); ++i)
110 data[i] = 0;
111#if !QT_CONFIG(opengles2)
112 const GLint internalFormat = isCoreProfile() ? GL_R8 : GL_ALPHA;
113 const GLenum format = isCoreProfile() ? GL_RED : GL_ALPHA;
114#else
115 const GLint internalFormat = GL_ALPHA;
116 const GLenum format = GL_ALPHA;
117#endif
118 funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, &data[0]);
119 }
120
121 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
122 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
123 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
124 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
125 m_filterMode = Nearest;
126
127 if (!m_buffer.isCreated()) {
128 m_buffer.create();
129 m_buffer.bind();
130 static GLfloat buf[sizeof(m_vertexCoordinateArray) + sizeof(m_textureCoordinateArray)];
131 memcpy(buf, m_vertexCoordinateArray, sizeof(m_vertexCoordinateArray));
132 memcpy(buf + (sizeof(m_vertexCoordinateArray) / sizeof(GLfloat)),
133 m_textureCoordinateArray,
134 sizeof(m_textureCoordinateArray));
135 m_buffer.allocate(buf, sizeof(buf));
136 m_buffer.release();
137 }
138
139 if (!m_vao.isCreated())
140 m_vao.create();
141}
142
143void QOpenGLTextureGlyphCache::setupVertexAttribs()
144{
145 m_buffer.bind();
146 m_blitProgram->setAttributeBuffer(int(QT_VERTEX_COORDS_ATTR), GL_FLOAT, 0, 2);
147 m_blitProgram->setAttributeBuffer(int(QT_TEXTURE_COORDS_ATTR), GL_FLOAT, sizeof(m_vertexCoordinateArray), 2);
148 m_blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
149 m_blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
150 m_buffer.release();
151}
152
153static void load_glyph_image_to_texture(QOpenGLContext *ctx,
154 QImage &img,
155 GLuint texture,
156 int tx, int ty)
157{
158 QOpenGLFunctions *funcs = ctx->functions();
159
160 const int imgWidth = img.width();
161 const int imgHeight = img.height();
162
163 if (img.format() == QImage::Format_Mono) {
164 img = img.convertToFormat(QImage::Format_Grayscale8);
165 } else if (img.depth() == 32) {
166 if (img.format() == QImage::Format_RGB32
167 // We need to make the alpha component equal to the average of the RGB values.
168 // This is needed when drawing sub-pixel antialiased text on translucent targets.
169#if Q_BYTE_ORDER == Q_BIG_ENDIAN
170 || img.format() == QImage::Format_ARGB32_Premultiplied
171#else
172 || (img.format() == QImage::Format_ARGB32_Premultiplied
173 && ctx->isOpenGLES())
174#endif
175 ) {
176 for (int y = 0; y < imgHeight; ++y) {
177 QRgb *src = (QRgb *) img.scanLine(y);
178 for (int x = 0; x < imgWidth; ++x) {
179 int r = qRed(src[x]);
180 int g = qGreen(src[x]);
181 int b = qBlue(src[x]);
182 int avg;
183 if (img.format() == QImage::Format_RGB32)
184 avg = (r + g + b + 1) / 3; // "+1" for rounding.
185 else // Format_ARGB_Premultiplied
186 avg = qAlpha(src[x]);
187
188 src[x] = qRgba(r, g, b, avg);
189 // swizzle the bits to accommodate for the GL_RGBA upload.
190#if Q_BYTE_ORDER != Q_BIG_ENDIAN
191 if (ctx->isOpenGLES())
192#endif
193 src[x] = ARGB2RGBA(src[x]);
194 }
195 }
196 }
197 }
198
199 funcs->glBindTexture(GL_TEXTURE_2D, texture);
200 if (img.depth() == 32) {
201#if QT_CONFIG(opengles2)
202 GLenum fmt = GL_RGBA;
203#else
204 GLenum fmt = ctx->isOpenGLES() ? GL_RGBA : GL_BGRA;
205#endif // QT_CONFIG(opengles2)
206
207#if Q_BYTE_ORDER == Q_BIG_ENDIAN
208 fmt = GL_RGBA;
209#endif
210 funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, tx, ty, imgWidth, imgHeight, fmt, GL_UNSIGNED_BYTE, img.constBits());
211 } else {
212 // The scanlines in image are 32-bit aligned, even for mono or 8-bit formats. This
213 // is good because it matches the default of 4 bytes for GL_UNPACK_ALIGNMENT.
214#if !QT_CONFIG(opengles2)
215 const GLenum format = isCoreProfile() ? GL_RED : GL_ALPHA;
216#else
217 const GLenum format = GL_ALPHA;
218#endif
219 funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, tx, ty, imgWidth, imgHeight, format, GL_UNSIGNED_BYTE, img.constBits());
220 }
221}
222
223static void load_glyph_image_region_to_texture(QOpenGLContext *ctx,
224 const QImage &srcImg,
225 int x, int y,
226 int w, int h,
227 GLuint texture,
228 int tx, int ty)
229{
230 Q_ASSERT(x + w <= srcImg.width() && y + h <= srcImg.height());
231
232 QImage img;
233 if (x != 0 || y != 0 || w != srcImg.width() || h != srcImg.height())
234 img = srcImg.copy(x, y, w, h);
235 else
236 img = srcImg;
237
238 load_glyph_image_to_texture(ctx, img, texture, tx, ty);
239}
240
241void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height)
242{
243 QOpenGLContext *ctx = QOpenGLContext::currentContext();
244 if (ctx == nullptr) {
245 qWarning("QOpenGLTextureGlyphCache::resizeTextureData: Called with no context");
246 return;
247 }
248
249 QOpenGLFunctions *funcs = ctx->functions();
250 GLint oldFbo;
251 funcs->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFbo);
252
253 int oldWidth = m_textureResource->m_width;
254 int oldHeight = m_textureResource->m_height;
255
256 // Make the lower glyph texture size 16 x 16.
257 if (width < 16)
258 width = 16;
259 if (height < 16)
260 height = 16;
261
262 GLuint oldTexture = m_textureResource->m_texture;
263 createTextureData(width, height);
264
265 if (ctx->d_func()->workaround_brokenFBOReadBack) {
266 QImageTextureGlyphCache::resizeTextureData(width, height);
267 load_glyph_image_region_to_texture(ctx, image(), 0, 0, qMin(oldWidth, width), qMin(oldHeight, height),
268 m_textureResource->m_texture, 0, 0);
269 return;
270 }
271
272 // ### the QTextureGlyphCache API needs to be reworked to allow
273 // ### resizeTextureData to fail
274
275 funcs->glBindFramebuffer(GL_FRAMEBUFFER, m_textureResource->m_fbo);
276
277 GLuint tmp_texture;
278 funcs->glGenTextures(1, &tmp_texture);
279 funcs->glBindTexture(GL_TEXTURE_2D, tmp_texture);
280 funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0,
281 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
282 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
283 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
284 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
285 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
286 m_filterMode = Nearest;
287 funcs->glBindTexture(GL_TEXTURE_2D, 0);
288 funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
289 GL_TEXTURE_2D, tmp_texture, 0);
290
291 funcs->glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
292 funcs->glBindTexture(GL_TEXTURE_2D, oldTexture);
293
294 if (pex != nullptr)
295 pex->transferMode(BrushDrawingMode);
296
297 funcs->glDisable(GL_STENCIL_TEST);
298 funcs->glDisable(GL_DEPTH_TEST);
299 funcs->glDisable(GL_SCISSOR_TEST);
300 funcs->glDisable(GL_BLEND);
301
302 funcs->glViewport(0, 0, oldWidth, oldHeight);
303
304 QOpenGLShaderProgram *blitProgram = nullptr;
305 if (pex == nullptr) {
306 if (m_blitProgram == nullptr) {
307 m_blitProgram = new QOpenGLShaderProgram;
308 const bool isCoreProfile = ctx->format().profile() == QSurfaceFormat::CoreProfile;
309
310 {
311 QString source;
312#ifdef Q_OS_WASM
313 source.append(QLatin1StringView(isCoreProfile ? qopenglslUntransformedPositionVertexShader_core : qopenglslUntransformedPositionVertexShader));
314 source.append(QLatin1StringView(isCoreProfile ? qopenglslMainWithTexCoordsVertexShader_core : qopenglslMainWithTexCoordsVertexShader));
315#else
316 source.append(QLatin1StringView(isCoreProfile ? qopenglslMainWithTexCoordsVertexShader_core : qopenglslMainWithTexCoordsVertexShader));
317 source.append(QLatin1StringView(isCoreProfile ? qopenglslUntransformedPositionVertexShader_core : qopenglslUntransformedPositionVertexShader));
318#endif
319 m_blitProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, source);
320 }
321
322 {
323 QString source;
324#ifdef Q_OS_WASM
325 source.append(QLatin1StringView(isCoreProfile ? qopenglslImageSrcFragmentShader_core : qopenglslImageSrcFragmentShader));
326 source.append(QLatin1StringView(isCoreProfile ? qopenglslMainFragmentShader_core : qopenglslMainFragmentShader));
327#else
328 source.append(QLatin1StringView(isCoreProfile ? qopenglslMainFragmentShader_core : qopenglslMainFragmentShader));
329 source.append(QLatin1StringView(isCoreProfile ? qopenglslImageSrcFragmentShader_core : qopenglslImageSrcFragmentShader));
330#endif
331 m_blitProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, source);
332 }
333
334 m_blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
335 m_blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
336
337 m_blitProgram->link();
338
339 if (m_vao.isCreated()) {
340 m_vao.bind();
341 setupVertexAttribs();
342 }
343 }
344
345 if (m_vao.isCreated())
346 m_vao.bind();
347 else
348 setupVertexAttribs();
349
350 m_blitProgram->bind();
351 blitProgram = m_blitProgram;
352
353 } else {
354 pex->uploadData(QT_VERTEX_COORDS_ATTR, m_vertexCoordinateArray, 8);
355 pex->uploadData(QT_TEXTURE_COORDS_ATTR, m_textureCoordinateArray, 8);
356
357 pex->shaderManager->useBlitProgram();
358 blitProgram = pex->shaderManager->blitProgram();
359 }
360
361 blitProgram->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT);
362
363 funcs->glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
364
365 funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);
366
367 funcs->glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
368
369 funcs->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
370 GL_RENDERBUFFER, 0);
371 funcs->glDeleteTextures(1, &tmp_texture);
372 funcs->glDeleteTextures(1, &oldTexture);
373
374 funcs->glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)oldFbo);
375
376 if (pex != nullptr) {
377 funcs->glViewport(0, 0, pex->width, pex->height);
378 pex->updateClipScissorTest();
379 } else {
380 if (m_vao.isCreated()) {
381 m_vao.release();
382 } else {
383 m_blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
384 m_blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
385 }
386 }
387}
388
389void QOpenGLTextureGlyphCache::fillTexture(const Coord &c,
390 glyph_t glyph,
391 const QFixedPoint &subPixelPosition)
392{
393 QOpenGLContext *ctx = QOpenGLContext::currentContext();
394 if (ctx == nullptr) {
395 qWarning("QOpenGLTextureGlyphCache::fillTexture: Called with no context");
396 return;
397 }
398
399 if (ctx->d_func()->workaround_brokenFBOReadBack) {
400 QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition);
401 load_glyph_image_region_to_texture(ctx, image(), c.x, c.y, c.w, c.h, m_textureResource->m_texture, c.x, c.y);
402 return;
403 }
404
405 QImage mask = textureMapForGlyph(glyph, subPixelPosition);
406 load_glyph_image_to_texture(ctx, mask, m_textureResource->m_texture, c.x, c.y);
407}
408
409int QOpenGLTextureGlyphCache::glyphPadding() const
410{
411 if (m_format == QFontEngine::Format_Mono)
412 return 8;
413 else
414 return 1;
415}
416
417int QOpenGLTextureGlyphCache::maxTextureWidth() const
418{
419 QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
420 if (ctx == nullptr)
421 return QImageTextureGlyphCache::maxTextureWidth();
422 else
423 return ctx->d_func()->maxTextureSize();
424}
425
426int QOpenGLTextureGlyphCache::maxTextureHeight() const
427{
428 QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
429 if (ctx == nullptr)
430 return QImageTextureGlyphCache::maxTextureHeight();
431
432 if (ctx->d_func()->workaround_brokenTexSubImage)
433 return qMin(1024, ctx->d_func()->maxTextureSize());
434 else
435 return ctx->d_func()->maxTextureSize();
436}
437
438void QOpenGLTextureGlyphCache::clear()
439{
440 if (m_textureResource)
441 m_textureResource->free();
442 m_textureResource = nullptr;
443
444 delete m_blitProgram;
445 m_blitProgram = nullptr;
446
447 m_w = 0;
448 m_h = 0;
449 m_cx = 0;
450 m_cy = 0;
451 m_currentRowHeight = 0;
452 coords.clear();
453}
454
455QT_END_NAMESPACE
Combined button and popup list for selecting options.
static void load_glyph_image_region_to_texture(QOpenGLContext *ctx, const QImage &srcImg, int x, int y, int w, int h, GLuint texture, int tx, int ty)
static void load_glyph_image_to_texture(QOpenGLContext *ctx, QImage &img, GLuint texture, int tx, int ty)
static QT_BEGIN_NAMESPACE int next_qopengltextureglyphcache_serial_number()
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:14
constexpr int qRed(QRgb rgb)
Definition qrgb.h:19
constexpr int qGreen(QRgb rgb)
Definition qrgb.h:22
constexpr QRgb qRgba(int r, int g, int b, int a)
Definition qrgb.h:34
constexpr int qBlue(QRgb rgb)
Definition qrgb.h:25
constexpr int qAlpha(QRgb rgb)
Definition qrgb.h:28