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 QtQuick 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 "qsgdefaultglyphnode_p_p.h" |
41 | #include <private/qsgmaterialshader_p.h> |
42 | |
43 | #include <qopenglshaderprogram.h> |
44 | #include <qopenglframebufferobject.h> |
45 | |
46 | #include <QtGui/private/qguiapplication_p.h> |
47 | #include <qpa/qplatformintegration.h> |
48 | #include <private/qfontengine_p.h> |
49 | #include <private/qopenglextensions_p.h> |
50 | |
51 | #include <QtQuick/qquickwindow.h> |
52 | #include <QtQuick/private/qsgtexture_p.h> |
53 | #include <QtQuick/private/qsgdefaultrendercontext_p.h> |
54 | |
55 | #include <private/qrawfont_p.h> |
56 | #include <QtCore/qmath.h> |
57 | |
58 | QT_BEGIN_NAMESPACE |
59 | |
60 | #ifndef GL_FRAMEBUFFER_SRGB |
61 | #define GL_FRAMEBUFFER_SRGB 0x8DB9 |
62 | #endif |
63 | |
64 | #ifndef GL_FRAMEBUFFER_SRGB_CAPABLE |
65 | #define GL_FRAMEBUFFER_SRGB_CAPABLE 0x8DBA |
66 | #endif |
67 | |
68 | static inline QVector4D qsg_premultiply(const QVector4D &c, float globalOpacity) |
69 | { |
70 | float o = c.w() * globalOpacity; |
71 | return QVector4D(c.x() * o, c.y() * o, c.z() * o, o); |
72 | } |
73 | |
74 | static inline qreal qt_sRGB_to_linear_RGB(qreal f) |
75 | { |
76 | return f > 0.04045 ? qPow((f + 0.055) / 1.055, 2.4) : f / 12.92; |
77 | } |
78 | |
79 | static inline QVector4D qt_sRGB_to_linear_RGB(const QVector4D &color) |
80 | { |
81 | return QVector4D(qt_sRGB_to_linear_RGB(color.x()), |
82 | qt_sRGB_to_linear_RGB(color.y()), |
83 | qt_sRGB_to_linear_RGB(color.z()), |
84 | color.w()); |
85 | } |
86 | |
87 | static inline qreal fontSmoothingGamma() |
88 | { |
89 | static qreal fontSmoothingGamma = QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FontSmoothingGamma).toReal(); |
90 | return fontSmoothingGamma; |
91 | } |
92 | |
93 | |
94 | // ***** legacy (GL) material shader implementations |
95 | |
96 | static inline qreal qsg_device_pixel_ratio(QOpenGLContext *ctx) |
97 | { |
98 | qreal devicePixelRatio = 1; |
99 | if (ctx->surface()->surfaceClass() == QSurface::Window) { |
100 | QWindow *w = static_cast<QWindow *>(ctx->surface()); |
101 | if (QQuickWindow *qw = qobject_cast<QQuickWindow *>(w)) |
102 | devicePixelRatio = qw->effectiveDevicePixelRatio(); |
103 | else |
104 | devicePixelRatio = w->devicePixelRatio(); |
105 | } else { |
106 | devicePixelRatio = ctx->screen() ? ctx->screen()->devicePixelRatio() : qGuiApp->devicePixelRatio(); |
107 | } |
108 | return devicePixelRatio; |
109 | } |
110 | |
111 | class QSGTextMaskShader : public QSGMaterialShader |
112 | { |
113 | public: |
114 | QSGTextMaskShader(QFontEngine::GlyphFormat glyphFormat); |
115 | |
116 | void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; |
117 | char const *const *attributeNames() const override; |
118 | |
119 | protected: |
120 | void initialize() override; |
121 | |
122 | int m_matrix_id; |
123 | int m_color_id; |
124 | int m_textureScale_id; |
125 | float m_devicePixelRatio; |
126 | |
127 | QFontEngine::GlyphFormat m_glyphFormat; |
128 | }; |
129 | |
130 | char const *const *QSGTextMaskShader::attributeNames() const |
131 | { |
132 | static char const *const attr[] = { "vCoord" , "tCoord" , nullptr }; |
133 | return attr; |
134 | } |
135 | |
136 | QSGTextMaskShader::QSGTextMaskShader(QFontEngine::GlyphFormat glyphFormat) |
137 | : QSGMaterialShader(*new QSGMaterialShaderPrivate) |
138 | , m_matrix_id(-1) |
139 | , m_color_id(-1) |
140 | , m_textureScale_id(-1) |
141 | , m_glyphFormat(glyphFormat) |
142 | { |
143 | setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/textmask.vert" )); |
144 | setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/textmask.frag" )); |
145 | } |
146 | |
147 | void QSGTextMaskShader::initialize() |
148 | { |
149 | m_matrix_id = program()->uniformLocation("matrix" ); |
150 | m_color_id = program()->uniformLocation("color" ); |
151 | m_textureScale_id = program()->uniformLocation("textureScale" ); |
152 | m_devicePixelRatio = (float) qsg_device_pixel_ratio(QOpenGLContext::currentContext()); |
153 | program()->setUniformValue("dpr" , m_devicePixelRatio); |
154 | } |
155 | |
156 | void QSGTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) |
157 | { |
158 | QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect); |
159 | QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect); |
160 | Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); |
161 | bool updated = material->ensureUpToDate(); |
162 | Q_ASSERT(material->texture()); |
163 | |
164 | Q_ASSERT(oldMaterial == nullptr || oldMaterial->texture()); |
165 | if (updated |
166 | || oldMaterial == nullptr |
167 | || oldMaterial->texture()->textureId() != material->texture()->textureId()) { |
168 | program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->openglGlyphCache()->width(), |
169 | 1.0 / material->openglGlyphCache()->height())); |
170 | QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); |
171 | funcs->glBindTexture(GL_TEXTURE_2D, material->texture()->textureId()); |
172 | |
173 | // Set the mag/min filters to be nearest. We only need to do this when the texture |
174 | // has been recreated. |
175 | if (updated) { |
176 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
177 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
178 | } |
179 | } |
180 | |
181 | float devicePixelRatio = (float) qsg_device_pixel_ratio(QOpenGLContext::currentContext()); |
182 | if (m_devicePixelRatio != devicePixelRatio) { |
183 | m_devicePixelRatio = devicePixelRatio; |
184 | program()->setUniformValue("dpr" , m_devicePixelRatio); |
185 | } |
186 | |
187 | if (state.isMatrixDirty()) |
188 | program()->setUniformValue(m_matrix_id, state.combinedMatrix()); |
189 | } |
190 | |
191 | class QSG8BitTextMaskShader : public QSGTextMaskShader |
192 | { |
193 | public: |
194 | QSG8BitTextMaskShader(QFontEngine::GlyphFormat glyphFormat) |
195 | : QSGTextMaskShader(glyphFormat) |
196 | { |
197 | setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/8bittextmask.frag" )); |
198 | } |
199 | |
200 | void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; |
201 | }; |
202 | |
203 | void QSG8BitTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) |
204 | { |
205 | QSGTextMaskShader::updateState(state, newEffect, oldEffect); |
206 | QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect); |
207 | QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect); |
208 | |
209 | if (oldMaterial == nullptr || material->color() != oldMaterial->color() || state.isOpacityDirty()) { |
210 | QVector4D color = qsg_premultiply(material->color(), state.opacity()); |
211 | program()->setUniformValue(m_color_id, color); |
212 | } |
213 | } |
214 | |
215 | class QSG24BitTextMaskShader : public QSGTextMaskShader |
216 | { |
217 | public: |
218 | QSG24BitTextMaskShader(QFontEngine::GlyphFormat glyphFormat) |
219 | : QSGTextMaskShader(glyphFormat) |
220 | , m_useSRGB(false) |
221 | { |
222 | setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/24bittextmask.frag" )); |
223 | } |
224 | |
225 | void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; |
226 | void initialize() override; |
227 | void activate() override; |
228 | void deactivate() override; |
229 | |
230 | bool useSRGB() const; |
231 | uint m_useSRGB : 1; |
232 | }; |
233 | |
234 | void QSG24BitTextMaskShader::initialize() |
235 | { |
236 | QSGTextMaskShader::initialize(); |
237 | // 0.25 was found to be acceptable error margin by experimentation. On Mac, the gamma is 2.0, |
238 | // but using sRGB looks okay. |
239 | if (QOpenGLContext::currentContext()->hasExtension(QByteArrayLiteral("GL_ARB_framebuffer_sRGB" )) |
240 | && m_glyphFormat == QFontEngine::Format_A32 |
241 | && qAbs(fontSmoothingGamma() - 2.2) < 0.25) { |
242 | QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); |
243 | GLint srgbCapable = 0; |
244 | funcs->glGetIntegerv(GL_FRAMEBUFFER_SRGB_CAPABLE, &srgbCapable); |
245 | if (srgbCapable) |
246 | m_useSRGB = true; |
247 | } |
248 | } |
249 | |
250 | bool QSG24BitTextMaskShader::useSRGB() const |
251 | { |
252 | #ifdef Q_OS_MACOS |
253 | if (!m_useSRGB) |
254 | return false; |
255 | |
256 | // m_useSRGB is true, but if some QOGLFBO was bound check it's texture format: |
257 | QOpenGLContext *ctx = QOpenGLContext::currentContext(); |
258 | QOpenGLFramebufferObject *qfbo = QOpenGLContextPrivate::get(ctx)->qgl_current_fbo; |
259 | bool fboInvalid = QOpenGLContextPrivate::get(ctx)->qgl_current_fbo_invalid; |
260 | return !qfbo || fboInvalid || qfbo->format().internalTextureFormat() == GL_SRGB8_ALPHA8_EXT; |
261 | #else |
262 | return m_useSRGB; |
263 | #endif |
264 | } |
265 | |
266 | void QSG24BitTextMaskShader::activate() |
267 | { |
268 | QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); |
269 | funcs->glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR); |
270 | if (useSRGB()) |
271 | funcs->glEnable(GL_FRAMEBUFFER_SRGB); |
272 | } |
273 | |
274 | void QSG24BitTextMaskShader::deactivate() |
275 | { |
276 | QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); |
277 | funcs->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
278 | if (useSRGB()) |
279 | funcs->glDisable(GL_FRAMEBUFFER_SRGB); |
280 | } |
281 | |
282 | void QSG24BitTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) |
283 | { |
284 | QSGTextMaskShader::updateState(state, newEffect, oldEffect); |
285 | QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect); |
286 | QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect); |
287 | |
288 | if (oldMaterial == nullptr || material->color() != oldMaterial->color() || state.isOpacityDirty()) { |
289 | QVector4D color = material->color(); |
290 | if (useSRGB()) |
291 | color = qt_sRGB_to_linear_RGB(color); |
292 | QOpenGLContext::currentContext()->functions()->glBlendColor(color.x(), color.y(), color.z(), color.w()); |
293 | color = qsg_premultiply(color, state.opacity()); |
294 | program()->setUniformValue(m_color_id, color.w()); |
295 | } |
296 | } |
297 | |
298 | class QSG32BitColorTextShader : public QSGTextMaskShader |
299 | { |
300 | public: |
301 | QSG32BitColorTextShader(QFontEngine::GlyphFormat glyphFormat) |
302 | : QSGTextMaskShader(glyphFormat) |
303 | { |
304 | setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/32bitcolortext.frag" )); |
305 | } |
306 | |
307 | void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; |
308 | }; |
309 | |
310 | void QSG32BitColorTextShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) |
311 | { |
312 | QSGTextMaskShader::updateState(state, newEffect, oldEffect); |
313 | QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect); |
314 | QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect); |
315 | |
316 | if (oldMaterial == nullptr || material->color() != oldMaterial->color() || state.isOpacityDirty()) { |
317 | float opacity = material->color().w() * state.opacity(); |
318 | program()->setUniformValue(m_color_id, opacity); |
319 | } |
320 | } |
321 | |
322 | class QSGStyledTextShader : public QSG8BitTextMaskShader |
323 | { |
324 | public: |
325 | QSGStyledTextShader(QFontEngine::GlyphFormat glyphFormat) |
326 | : QSG8BitTextMaskShader(glyphFormat) |
327 | { |
328 | setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/styledtext.vert" )); |
329 | setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/styledtext.frag" )); |
330 | } |
331 | |
332 | void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; |
333 | |
334 | private: |
335 | void initialize() override; |
336 | |
337 | int m_shift_id; |
338 | int m_styleColor_id; |
339 | }; |
340 | |
341 | void QSGStyledTextShader::initialize() |
342 | { |
343 | QSG8BitTextMaskShader::initialize(); |
344 | m_shift_id = program()->uniformLocation("shift" ); |
345 | m_styleColor_id = program()->uniformLocation("styleColor" ); |
346 | } |
347 | |
348 | void QSGStyledTextShader::updateState(const RenderState &state, |
349 | QSGMaterial *newEffect, |
350 | QSGMaterial *oldEffect) |
351 | { |
352 | Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); |
353 | |
354 | QSGStyledTextMaterial *material = static_cast<QSGStyledTextMaterial *>(newEffect); |
355 | QSGStyledTextMaterial *oldMaterial = static_cast<QSGStyledTextMaterial *>(oldEffect); |
356 | |
357 | if (oldMaterial == nullptr || oldMaterial->styleShift() != material->styleShift()) |
358 | program()->setUniformValue(m_shift_id, material->styleShift()); |
359 | |
360 | if (oldMaterial == nullptr || material->color() != oldMaterial->color() || state.isOpacityDirty()) { |
361 | QVector4D color = qsg_premultiply(material->color(), state.opacity()); |
362 | program()->setUniformValue(m_color_id, color); |
363 | } |
364 | |
365 | if (oldMaterial == nullptr || material->styleColor() != oldMaterial->styleColor() || state.isOpacityDirty()) { |
366 | QVector4D styleColor = qsg_premultiply(material->styleColor(), state.opacity()); |
367 | program()->setUniformValue(m_styleColor_id, styleColor); |
368 | } |
369 | |
370 | bool updated = material->ensureUpToDate(); |
371 | Q_ASSERT(material->texture()); |
372 | |
373 | Q_ASSERT(oldMaterial == nullptr || oldMaterial->texture()); |
374 | if (updated |
375 | || oldMaterial == nullptr |
376 | || oldMaterial->texture()->textureId() != material->texture()->textureId()) { |
377 | program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->openglGlyphCache()->width(), |
378 | 1.0 / material->openglGlyphCache()->height())); |
379 | QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); |
380 | funcs->glBindTexture(GL_TEXTURE_2D, material->texture()->textureId()); |
381 | |
382 | // Set the mag/min filters to be nearest. We only need to do this when the texture |
383 | // has been recreated. |
384 | if (updated) { |
385 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
386 | funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
387 | } |
388 | } |
389 | |
390 | if (state.isMatrixDirty()) |
391 | program()->setUniformValue(m_matrix_id, state.combinedMatrix()); |
392 | } |
393 | |
394 | class QSGOutlinedTextShader : public QSGStyledTextShader |
395 | { |
396 | public: |
397 | QSGOutlinedTextShader(QFontEngine::GlyphFormat glyphFormat) |
398 | : QSGStyledTextShader(glyphFormat) |
399 | { |
400 | setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/outlinedtext.vert" )); |
401 | setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/outlinedtext.frag" )); |
402 | } |
403 | }; |
404 | |
405 | |
406 | // ***** RHI shader implementations |
407 | |
408 | class QSGTextMaskRhiShader : public QSGMaterialRhiShader |
409 | { |
410 | public: |
411 | QSGTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat); |
412 | |
413 | bool updateUniformData(const RenderState &state, |
414 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; |
415 | void updateSampledImage(const RenderState &state, int binding, QSGTexture **texture, |
416 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; |
417 | |
418 | protected: |
419 | QFontEngine::GlyphFormat m_glyphFormat; |
420 | }; |
421 | |
422 | QSGTextMaskRhiShader::QSGTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat) |
423 | : m_glyphFormat(glyphFormat) |
424 | { |
425 | setShaderFileName(VertexStage, |
426 | QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/textmask.vert.qsb" )); |
427 | setShaderFileName(FragmentStage, |
428 | QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/textmask.frag.qsb" )); |
429 | } |
430 | |
431 | bool QSGTextMaskRhiShader::updateUniformData(const RenderState &state, |
432 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) |
433 | { |
434 | Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type()); |
435 | QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial); |
436 | QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial); |
437 | |
438 | // updateUniformData() is called before updateSampledImage() by the |
439 | // renderer. Hence updating the glyph cache stuff here. |
440 | const bool updated = mat->ensureUpToDate(); |
441 | Q_ASSERT(mat->texture()); |
442 | Q_ASSERT(oldMat == nullptr || oldMat->texture()); |
443 | |
444 | bool changed = false; |
445 | QByteArray *buf = state.uniformData(); |
446 | Q_ASSERT(buf->size() >= 92); |
447 | |
448 | if (state.isMatrixDirty()) { |
449 | const QMatrix4x4 m = state.combinedMatrix(); |
450 | memcpy(buf->data(), m.constData(), 64); |
451 | changed = true; |
452 | } |
453 | |
454 | if (updated || !oldMat || oldMat->texture()->rhiTexture() != mat->texture()->rhiTexture()) { |
455 | const QVector2D textureScale = QVector2D(1.0f / mat->rhiGlyphCache()->width(), |
456 | 1.0f / mat->rhiGlyphCache()->height()); |
457 | memcpy(buf->data() + 64 + 16, &textureScale, 8); |
458 | changed = true; |
459 | } |
460 | |
461 | if (!oldMat) { |
462 | float dpr = state.devicePixelRatio(); |
463 | memcpy(buf->data() + 64 + 16 + 8, &dpr, 4); |
464 | } |
465 | |
466 | // move texture uploads/copies onto the renderer's soon-to-be-committed list |
467 | mat->rhiGlyphCache()->commitResourceUpdates(state.resourceUpdateBatch()); |
468 | |
469 | return changed; |
470 | } |
471 | |
472 | void QSGTextMaskRhiShader::updateSampledImage(const RenderState &state, int binding, QSGTexture **texture, |
473 | QSGMaterial *newMaterial, QSGMaterial *) |
474 | { |
475 | Q_UNUSED(state); |
476 | if (binding != 1) |
477 | return; |
478 | |
479 | QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial); |
480 | QSGTexture *t = mat->texture(); |
481 | t->setFiltering(QSGTexture::Nearest); |
482 | *texture = t; |
483 | } |
484 | |
485 | class QSG8BitTextMaskRhiShader : public QSGTextMaskRhiShader |
486 | { |
487 | public: |
488 | QSG8BitTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat, bool alphaTexture) |
489 | : QSGTextMaskRhiShader(glyphFormat) |
490 | { |
491 | if (alphaTexture) |
492 | setShaderFileName(FragmentStage, |
493 | QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/8bittextmask_a.frag.qsb" )); |
494 | else |
495 | setShaderFileName(FragmentStage, |
496 | QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/8bittextmask.frag.qsb" )); |
497 | } |
498 | |
499 | bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; |
500 | }; |
501 | |
502 | bool QSG8BitTextMaskRhiShader::updateUniformData(const RenderState &state, |
503 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) |
504 | { |
505 | bool changed = QSGTextMaskRhiShader::updateUniformData(state, newMaterial, oldMaterial); |
506 | |
507 | QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial); |
508 | QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial); |
509 | |
510 | QByteArray *buf = state.uniformData(); |
511 | Q_ASSERT(buf->size() >= 80); |
512 | |
513 | if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) { |
514 | const QVector4D color = qsg_premultiply(mat->color(), state.opacity()); |
515 | memcpy(buf->data() + 64, &color, 16); |
516 | changed = true; |
517 | } |
518 | |
519 | return changed; |
520 | } |
521 | |
522 | class QSG24BitTextMaskRhiShader : public QSGTextMaskRhiShader |
523 | { |
524 | public: |
525 | QSG24BitTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat) |
526 | : QSGTextMaskRhiShader(glyphFormat) |
527 | { |
528 | setFlag(UpdatesGraphicsPipelineState, true); |
529 | setShaderFileName(FragmentStage, |
530 | QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/24bittextmask.frag.qsb" )); |
531 | } |
532 | |
533 | bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; |
534 | bool updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps, |
535 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; |
536 | }; |
537 | |
538 | // ### gamma correction (sRGB) Unsurprisingly, the GL approach is not portable |
539 | // to anything else - it just does not work that way, there is no opt-in/out |
540 | // switch and magic winsys-provided maybe-sRGB buffers. When requesting an sRGB |
541 | // QRhiSwapChain (which we do not do), it is full sRGB, with the sRGB |
542 | // framebuffer update and blending always on... Could we do gamma correction in |
543 | // the shader for text? (but that's bad for blending?) |
544 | |
545 | bool QSG24BitTextMaskRhiShader::updateUniformData(const RenderState &state, |
546 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) |
547 | { |
548 | bool changed = QSGTextMaskRhiShader::updateUniformData(state, newMaterial, oldMaterial); |
549 | |
550 | QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial); |
551 | QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial); |
552 | |
553 | QByteArray *buf = state.uniformData(); |
554 | Q_ASSERT(buf->size() >= 92); |
555 | |
556 | if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) { |
557 | // shader takes vec4 but uses alpha only; coloring happens via the blend constant |
558 | const QVector4D color = qsg_premultiply(mat->color(), state.opacity()); |
559 | memcpy(buf->data() + 64, &color, 16); |
560 | changed = true; |
561 | } |
562 | |
563 | return changed; |
564 | } |
565 | |
566 | bool QSG24BitTextMaskRhiShader::updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps, |
567 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) |
568 | { |
569 | Q_UNUSED(state); |
570 | Q_UNUSED(oldMaterial); |
571 | QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial); |
572 | |
573 | ps->blendEnable = true; |
574 | ps->srcColor = GraphicsPipelineState::ConstantColor; |
575 | ps->dstColor = GraphicsPipelineState::OneMinusSrcColor; |
576 | |
577 | QVector4D color = qsg_premultiply(mat->color(), state.opacity()); |
578 | // if (useSRGB()) |
579 | // color = qt_sRGB_to_linear_RGB(color); |
580 | |
581 | // this is dynamic state but it's - magic! - taken care of by the renderer |
582 | ps->blendConstant = QColor::fromRgbF(color.x(), color.y(), color.z(), color.w()); |
583 | |
584 | return true; |
585 | } |
586 | |
587 | class QSG32BitColorTextRhiShader : public QSGTextMaskRhiShader |
588 | { |
589 | public: |
590 | QSG32BitColorTextRhiShader(QFontEngine::GlyphFormat glyphFormat) |
591 | : QSGTextMaskRhiShader(glyphFormat) |
592 | { |
593 | setShaderFileName(FragmentStage, |
594 | QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/32bitcolortext.frag.qsb" )); |
595 | } |
596 | |
597 | bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; |
598 | }; |
599 | |
600 | bool QSG32BitColorTextRhiShader::updateUniformData(const RenderState &state, |
601 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) |
602 | { |
603 | bool changed = QSGTextMaskRhiShader::updateUniformData(state, newMaterial, oldMaterial); |
604 | |
605 | QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial); |
606 | QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial); |
607 | |
608 | QByteArray *buf = state.uniformData(); |
609 | Q_ASSERT(buf->size() >= 92); |
610 | |
611 | if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) { |
612 | // shader takes vec4 but uses alpha only |
613 | const QVector4D color(0, 0, 0, mat->color().w() * state.opacity()); |
614 | memcpy(buf->data() + 64, &color, 16); |
615 | changed = true; |
616 | } |
617 | |
618 | return changed; |
619 | } |
620 | |
621 | class QSGStyledTextRhiShader : public QSG8BitTextMaskRhiShader |
622 | { |
623 | public: |
624 | QSGStyledTextRhiShader(QFontEngine::GlyphFormat glyphFormat, bool alphaTexture) |
625 | : QSG8BitTextMaskRhiShader(glyphFormat, alphaTexture) |
626 | { |
627 | setShaderFileName(VertexStage, |
628 | QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext.vert.qsb" )); |
629 | if (alphaTexture) |
630 | setShaderFileName(FragmentStage, |
631 | QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext_a.frag.qsb" )); |
632 | else |
633 | setShaderFileName(FragmentStage, |
634 | QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext.frag.qsb" )); |
635 | } |
636 | |
637 | bool updateUniformData(const RenderState &state, |
638 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; |
639 | }; |
640 | |
641 | bool QSGStyledTextRhiShader::updateUniformData(const RenderState &state, |
642 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) |
643 | { |
644 | bool changed = QSG8BitTextMaskRhiShader::updateUniformData(state, newMaterial, oldMaterial); |
645 | |
646 | QSGStyledTextMaterial *mat = static_cast<QSGStyledTextMaterial *>(newMaterial); |
647 | QSGStyledTextMaterial *oldMat = static_cast<QSGStyledTextMaterial *>(oldMaterial); |
648 | |
649 | QByteArray *buf = state.uniformData(); |
650 | Q_ASSERT(buf->size() >= 120); |
651 | |
652 | // matrix..dpr + 1 float padding (vec4 must be aligned to 16) |
653 | const int startOffset = 64 + 16 + 8 + 4 + 4; |
654 | |
655 | if (oldMat == nullptr || mat->styleColor() != oldMat->styleColor() || state.isOpacityDirty()) { |
656 | const QVector4D styleColor = qsg_premultiply(mat->styleColor(), state.opacity()); |
657 | memcpy(buf->data() + startOffset, &styleColor, 16); |
658 | changed = true; |
659 | } |
660 | |
661 | if (oldMat == nullptr || oldMat->styleShift() != mat->styleShift()) { |
662 | const QVector2D v = mat->styleShift(); |
663 | memcpy(buf->data() + startOffset + 16, &v, 8); |
664 | changed = true; |
665 | } |
666 | |
667 | return changed; |
668 | } |
669 | |
670 | class QSGOutlinedTextRhiShader : public QSGStyledTextRhiShader |
671 | { |
672 | public: |
673 | QSGOutlinedTextRhiShader(QFontEngine::GlyphFormat glyphFormat, bool alphaTexture) |
674 | : QSGStyledTextRhiShader(glyphFormat, alphaTexture) |
675 | { |
676 | setShaderFileName(VertexStage, |
677 | QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext.vert.qsb" )); |
678 | if (alphaTexture) |
679 | setShaderFileName(FragmentStage, |
680 | QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext_a.frag.qsb" )); |
681 | else |
682 | setShaderFileName(FragmentStage, |
683 | QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext.frag.qsb" )); |
684 | } |
685 | }; |
686 | |
687 | |
688 | // ***** common material stuff |
689 | |
690 | QSGTextMaskMaterial::QSGTextMaskMaterial(QSGRenderContext *rc, const QVector4D &color, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat) |
691 | : m_rc(qobject_cast<QSGDefaultRenderContext *>(rc)) |
692 | , m_texture(nullptr) |
693 | , m_glyphCache(nullptr) |
694 | , m_font(font) |
695 | , m_color(color) |
696 | { |
697 | init(glyphFormat); |
698 | } |
699 | |
700 | QSGTextMaskMaterial::~QSGTextMaskMaterial() |
701 | { |
702 | delete m_texture; |
703 | } |
704 | |
705 | void QSGTextMaskMaterial::setColor(const QVector4D &color) |
706 | { |
707 | if (m_color == color) |
708 | return; |
709 | |
710 | m_color = color; |
711 | |
712 | // If it is an RGB cache, then the pen color is actually part of the cache key |
713 | // so it has to be updated |
714 | if (m_glyphCache != nullptr && m_glyphCache->glyphFormat() == QFontEngine::Format_ARGB) |
715 | updateCache(QFontEngine::Format_ARGB); |
716 | } |
717 | |
718 | void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat) |
719 | { |
720 | Q_ASSERT(m_font.isValid()); |
721 | |
722 | setFlag(SupportsRhiShader, true); |
723 | setFlag(Blending, true); |
724 | |
725 | Q_ASSERT(m_rc); |
726 | m_rhi = m_rc->rhi(); |
727 | |
728 | updateCache(glyphFormat); |
729 | } |
730 | |
731 | void QSGTextMaskMaterial::updateCache(QFontEngine::GlyphFormat glyphFormat) |
732 | { |
733 | // The following piece of code will read/write to the font engine's caches, |
734 | // potentially from different threads. However, this is safe because this |
735 | // code is only called from QQuickItem::updatePaintNode() which is called |
736 | // only when the GUI is blocked, and multiple threads will call it in |
737 | // sequence. See also QSGRenderContext::invalidate |
738 | |
739 | QRawFontPrivate *fontD = QRawFontPrivate::get(m_font); |
740 | if (QFontEngine *fontEngine = fontD->fontEngine) { |
741 | if (glyphFormat == QFontEngine::Format_None) { |
742 | glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None |
743 | ? fontEngine->glyphFormat |
744 | : QFontEngine::Format_A32; |
745 | } |
746 | |
747 | QOpenGLContext *ctx = nullptr; |
748 | qreal devicePixelRatio; |
749 | void *cacheKey; |
750 | if (m_rhi) { |
751 | cacheKey = m_rhi; |
752 | // ### no idea what the QWindow is (esp. since we are not even |
753 | // rendering at this point), and anyway is the original logic correct |
754 | // even... |
755 | devicePixelRatio = qGuiApp->devicePixelRatio(); |
756 | } else { |
757 | ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); |
758 | Q_ASSERT(ctx != nullptr); |
759 | cacheKey = ctx; |
760 | devicePixelRatio = qsg_device_pixel_ratio(ctx); |
761 | } |
762 | |
763 | QTransform glyphCacheTransform = QTransform::fromScale(devicePixelRatio, devicePixelRatio); |
764 | if (!fontEngine->supportsTransformation(glyphCacheTransform)) |
765 | glyphCacheTransform = QTransform(); |
766 | |
767 | QColor color = glyphFormat == QFontEngine::Format_ARGB ? QColor::fromRgbF(m_color.x(), m_color.y(), m_color.z(), m_color.w()) : QColor(); |
768 | m_glyphCache = fontEngine->glyphCache(cacheKey, glyphFormat, glyphCacheTransform, color); |
769 | if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) { |
770 | if (m_rhi) |
771 | m_glyphCache = new QSGRhiTextureGlyphCache(m_rhi, glyphFormat, glyphCacheTransform, color); |
772 | else |
773 | m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform, color); |
774 | |
775 | fontEngine->setGlyphCache(cacheKey, m_glyphCache.data()); |
776 | m_rc->registerFontengineForCleanup(fontEngine); |
777 | } |
778 | } |
779 | } |
780 | |
781 | void QSGTextMaskMaterial::populate(const QPointF &p, |
782 | const QVector<quint32> &glyphIndexes, |
783 | const QVector<QPointF> &glyphPositions, |
784 | QSGGeometry *geometry, |
785 | QRectF *boundingRect, |
786 | QPointF *baseLine, |
787 | const QMargins &margins) |
788 | { |
789 | Q_ASSERT(m_font.isValid()); |
790 | QVector<QFixedPoint> fixedPointPositions; |
791 | const int glyphPositionsSize = glyphPositions.size(); |
792 | fixedPointPositions.reserve(glyphPositionsSize); |
793 | for (int i=0; i < glyphPositionsSize; ++i) |
794 | fixedPointPositions.append(QFixedPoint::fromPointF(glyphPositions.at(i))); |
795 | |
796 | QTextureGlyphCache *cache = glyphCache(); |
797 | |
798 | QRawFontPrivate *fontD = QRawFontPrivate::get(m_font); |
799 | cache->populate(fontD->fontEngine, glyphIndexes.size(), glyphIndexes.constData(), |
800 | fixedPointPositions.data()); |
801 | cache->fillInPendingGlyphs(); |
802 | |
803 | int margin = fontD->fontEngine->glyphMargin(cache->glyphFormat()); |
804 | |
805 | qreal glyphCacheScaleX = cache->transform().m11(); |
806 | qreal glyphCacheScaleY = cache->transform().m22(); |
807 | qreal glyphCacheInverseScaleX = 1.0 / glyphCacheScaleX; |
808 | qreal glyphCacheInverseScaleY = 1.0 / glyphCacheScaleY; |
809 | |
810 | Q_ASSERT(geometry->indexType() == GL_UNSIGNED_SHORT); |
811 | geometry->allocate(glyphIndexes.size() * 4, glyphIndexes.size() * 6); |
812 | QVector4D *vp = (QVector4D *)geometry->vertexDataAsTexturedPoint2D(); |
813 | Q_ASSERT(geometry->sizeOfVertex() == sizeof(QVector4D)); |
814 | ushort *ip = geometry->indexDataAsUShort(); |
815 | |
816 | QPointF position(p.x(), p.y() - m_font.ascent()); |
817 | bool supportsSubPixelPositions = fontD->fontEngine->supportsSubPixelPositions(); |
818 | for (int i=0; i<glyphIndexes.size(); ++i) { |
819 | QFixed subPixelPosition; |
820 | if (supportsSubPixelPositions) |
821 | subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPositions.at(i).x())); |
822 | |
823 | QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i), subPixelPosition); |
824 | const QTextureGlyphCache::Coord &c = cache->coords.value(glyph); |
825 | |
826 | QPointF glyphPosition = glyphPositions.at(i) + position; |
827 | |
828 | // On a retina screen the glyph positions are not pre-scaled (as opposed to |
829 | // eg. the raster paint engine). To ensure that we get the same behavior as |
830 | // the raster engine (and CoreText itself) when it comes to rounding of the |
831 | // coordinates, we need to apply the scale factor before rounding, and then |
832 | // apply the inverse scale to get back to the coordinate system of the node. |
833 | |
834 | qreal x = (qFloor(glyphPosition.x() * glyphCacheScaleX) * glyphCacheInverseScaleX) + |
835 | (c.baseLineX * glyphCacheInverseScaleX) - margin; |
836 | qreal y = (qRound(glyphPosition.y() * glyphCacheScaleY) * glyphCacheInverseScaleY) - |
837 | (c.baseLineY * glyphCacheInverseScaleY) - margin; |
838 | |
839 | qreal w = c.w * glyphCacheInverseScaleX; |
840 | qreal h = c.h * glyphCacheInverseScaleY; |
841 | |
842 | *boundingRect |= QRectF(x + margin, y + margin, w, h); |
843 | |
844 | float cx1 = x - margins.left(); |
845 | float cx2 = x + w + margins.right(); |
846 | float cy1 = y - margins.top(); |
847 | float cy2 = y + h + margins.bottom(); |
848 | |
849 | float tx1 = c.x - margins.left(); |
850 | float tx2 = c.x + c.w + margins.right(); |
851 | float ty1 = c.y - margins.top(); |
852 | float ty2 = c.y + c.h + margins.bottom(); |
853 | |
854 | if (baseLine->isNull()) |
855 | *baseLine = glyphPosition; |
856 | |
857 | vp[4 * i + 0] = QVector4D(cx1, cy1, tx1, ty1); |
858 | vp[4 * i + 1] = QVector4D(cx2, cy1, tx2, ty1); |
859 | vp[4 * i + 2] = QVector4D(cx1, cy2, tx1, ty2); |
860 | vp[4 * i + 3] = QVector4D(cx2, cy2, tx2, ty2); |
861 | |
862 | int o = i * 4; |
863 | ip[6 * i + 0] = o + 0; |
864 | ip[6 * i + 1] = o + 2; |
865 | ip[6 * i + 2] = o + 3; |
866 | ip[6 * i + 3] = o + 3; |
867 | ip[6 * i + 4] = o + 1; |
868 | ip[6 * i + 5] = o + 0; |
869 | } |
870 | } |
871 | |
872 | QSGMaterialType *QSGTextMaskMaterial::type() const |
873 | { |
874 | static QSGMaterialType argb, rgb, gray; |
875 | switch (glyphCache()->glyphFormat()) { |
876 | case QFontEngine::Format_ARGB: |
877 | return &argb; |
878 | case QFontEngine::Format_A32: |
879 | return &rgb; |
880 | case QFontEngine::Format_A8: |
881 | default: |
882 | return &gray; |
883 | } |
884 | } |
885 | |
886 | QTextureGlyphCache *QSGTextMaskMaterial::glyphCache() const |
887 | { |
888 | return static_cast<QTextureGlyphCache *>(m_glyphCache.data()); |
889 | } |
890 | |
891 | QOpenGLTextureGlyphCache *QSGTextMaskMaterial::openglGlyphCache() const |
892 | { |
893 | return static_cast<QOpenGLTextureGlyphCache *>(glyphCache()); |
894 | } |
895 | |
896 | QSGRhiTextureGlyphCache *QSGTextMaskMaterial::rhiGlyphCache() const |
897 | { |
898 | return static_cast<QSGRhiTextureGlyphCache *>(glyphCache()); |
899 | } |
900 | |
901 | QSGMaterialShader *QSGTextMaskMaterial::createShader() const |
902 | { |
903 | if (flags().testFlag(RhiShaderWanted)) { |
904 | QSGRhiTextureGlyphCache *gc = rhiGlyphCache(); |
905 | const QFontEngine::GlyphFormat glyphFormat = gc->glyphFormat(); |
906 | switch (glyphFormat) { |
907 | case QFontEngine::Format_ARGB: |
908 | return new QSG32BitColorTextRhiShader(glyphFormat); |
909 | case QFontEngine::Format_A32: |
910 | return new QSG24BitTextMaskRhiShader(glyphFormat); |
911 | case QFontEngine::Format_A8: |
912 | default: |
913 | return new QSG8BitTextMaskRhiShader(glyphFormat, gc->eightBitFormatIsAlphaSwizzled()); |
914 | } |
915 | } else { |
916 | switch (QFontEngine::GlyphFormat glyphFormat = glyphCache()->glyphFormat()) { |
917 | case QFontEngine::Format_ARGB: |
918 | return new QSG32BitColorTextShader(glyphFormat); |
919 | case QFontEngine::Format_A32: |
920 | return new QSG24BitTextMaskShader(glyphFormat); |
921 | case QFontEngine::Format_A8: |
922 | default: |
923 | return new QSG8BitTextMaskShader(glyphFormat); |
924 | } |
925 | } |
926 | } |
927 | |
928 | static inline int qsg_colorDiff(const QVector4D &a, const QVector4D &b) |
929 | { |
930 | if (a.x() != b.x()) |
931 | return a.x() > b.x() ? 1 : -1; |
932 | if (a.y() != b.y()) |
933 | return a.y() > b.y() ? 1 : -1; |
934 | if (a.z() != b.z()) |
935 | return a.z() > b.z() ? 1 : -1; |
936 | if (a.w() != b.w()) |
937 | return a.w() > b.w() ? 1 : -1; |
938 | return 0; |
939 | } |
940 | |
941 | int QSGTextMaskMaterial::compare(const QSGMaterial *o) const |
942 | { |
943 | Q_ASSERT(o && type() == o->type()); |
944 | const QSGTextMaskMaterial *other = static_cast<const QSGTextMaskMaterial *>(o); |
945 | if (m_glyphCache != other->m_glyphCache) |
946 | return m_glyphCache.data() < other->m_glyphCache.data() ? -1 : 1; |
947 | return qsg_colorDiff(m_color, other->m_color); |
948 | } |
949 | |
950 | bool QSGTextMaskMaterial::ensureUpToDate() |
951 | { |
952 | if (m_rhi) { |
953 | QSGRhiTextureGlyphCache *gc = rhiGlyphCache(); |
954 | QSize glyphCacheSize(gc->width(), gc->height()); |
955 | if (glyphCacheSize != m_size) { |
956 | if (m_texture) |
957 | delete m_texture; |
958 | m_texture = new QSGPlainTexture; |
959 | m_texture->setTexture(gc->texture()); |
960 | m_texture->setTextureSize(QSize(gc->width(), gc->height())); |
961 | m_texture->setOwnsTexture(false); |
962 | m_size = glyphCacheSize; |
963 | return true; |
964 | } |
965 | return false; |
966 | |
967 | } else { |
968 | QSize glyphCacheSize(openglGlyphCache()->width(), openglGlyphCache()->height()); |
969 | if (glyphCacheSize != m_size) { |
970 | if (m_texture) |
971 | delete m_texture; |
972 | m_texture = new QSGPlainTexture(); |
973 | m_texture->setTextureId(openglGlyphCache()->texture()); |
974 | m_texture->setTextureSize(QSize(openglGlyphCache()->width(), openglGlyphCache()->height())); |
975 | m_texture->setOwnsTexture(false); |
976 | m_size = glyphCacheSize; |
977 | return true; |
978 | } |
979 | return false; |
980 | } |
981 | } |
982 | |
983 | |
984 | QSGStyledTextMaterial::QSGStyledTextMaterial(QSGRenderContext *rc, const QRawFont &font) |
985 | : QSGTextMaskMaterial(rc, QVector4D(), font, QFontEngine::Format_A8) |
986 | { |
987 | } |
988 | |
989 | QSGMaterialType *QSGStyledTextMaterial::type() const |
990 | { |
991 | static QSGMaterialType type; |
992 | return &type; |
993 | } |
994 | |
995 | QSGMaterialShader *QSGStyledTextMaterial::createShader() const |
996 | { |
997 | if (flags().testFlag(RhiShaderWanted)) { |
998 | QSGRhiTextureGlyphCache *gc = rhiGlyphCache(); |
999 | return new QSGStyledTextRhiShader(gc->glyphFormat(), gc->eightBitFormatIsAlphaSwizzled()); |
1000 | } else { |
1001 | return new QSGStyledTextShader(glyphCache()->glyphFormat()); |
1002 | } |
1003 | } |
1004 | |
1005 | int QSGStyledTextMaterial::compare(const QSGMaterial *o) const |
1006 | { |
1007 | const QSGStyledTextMaterial *other = static_cast<const QSGStyledTextMaterial *>(o); |
1008 | |
1009 | if (m_styleShift != other->m_styleShift) |
1010 | return m_styleShift.y() - other->m_styleShift.y(); |
1011 | |
1012 | int diff = qsg_colorDiff(m_styleColor, other->m_styleColor); |
1013 | if (diff == 0) |
1014 | return QSGTextMaskMaterial::compare(o); |
1015 | return diff; |
1016 | } |
1017 | |
1018 | |
1019 | QSGOutlinedTextMaterial::QSGOutlinedTextMaterial(QSGRenderContext *rc, const QRawFont &font) |
1020 | : QSGStyledTextMaterial(rc, font) |
1021 | { |
1022 | } |
1023 | |
1024 | QSGMaterialType *QSGOutlinedTextMaterial::type() const |
1025 | { |
1026 | static QSGMaterialType type; |
1027 | return &type; |
1028 | } |
1029 | |
1030 | QSGMaterialShader *QSGOutlinedTextMaterial::createShader() const |
1031 | { |
1032 | if (flags().testFlag(RhiShaderWanted)) { |
1033 | QSGRhiTextureGlyphCache *gc = rhiGlyphCache(); |
1034 | return new QSGOutlinedTextRhiShader(gc->glyphFormat(), gc->eightBitFormatIsAlphaSwizzled()); |
1035 | } else { |
1036 | return new QSGOutlinedTextShader(glyphCache()->glyphFormat()); |
1037 | } |
1038 | } |
1039 | |
1040 | QT_END_NAMESPACE |
1041 | |