1/****************************************************************************
2**
3** Copyright (C) 2019 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 "qsgdistancefieldglyphnode_p_p.h"
41#include "qsgrhidistancefieldglyphcache_p.h"
42#include <QtGui/qopenglfunctions.h>
43#include <QtGui/qsurface.h>
44#include <QtGui/qwindow.h>
45#include <qmath.h>
46
47QT_BEGIN_NAMESPACE
48
49static float qt_sg_envFloat(const char *name, float defaultValue)
50{
51 if (Q_LIKELY(!qEnvironmentVariableIsSet(name)))
52 return defaultValue;
53 bool ok = false;
54 const float value = qgetenv(name).toFloat(&ok);
55 return ok ? value : defaultValue;
56}
57
58static float thresholdFunc(float glyphScale)
59{
60 static const float base = qt_sg_envFloat("QT_DF_BASE", 0.5f);
61 static const float baseDev = qt_sg_envFloat("QT_DF_BASEDEVIATION", 0.065f);
62 static const float devScaleMin = qt_sg_envFloat("QT_DF_SCALEFORMAXDEV", 0.15f);
63 static const float devScaleMax = qt_sg_envFloat("QT_DF_SCALEFORNODEV", 0.3f);
64 return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev);
65}
66
67static float spreadFunc(float glyphScale)
68{
69 static const float range = qt_sg_envFloat("QT_DF_RANGE", 0.06f);
70 return range / glyphScale;
71}
72
73class QSGDistanceFieldTextMaterialShader : public QSGMaterialShader
74{
75public:
76 QSGDistanceFieldTextMaterialShader();
77
78 void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
79 char const *const *attributeNames() const override;
80
81protected:
82 void initialize() override;
83
84 void updateAlphaRange();
85 void updateColor(const QVector4D &c);
86 void updateTextureScale(const QVector2D &ts);
87
88 float m_fontScale = 1.0;
89 float m_matrixScale = 1.0;
90
91 int m_matrix_id = -1;
92 int m_textureScale_id = -1;
93 int m_alphaMin_id = -1;
94 int m_alphaMax_id = -1;
95 int m_color_id = -1;
96
97 QVector2D m_lastTextureScale;
98 QVector4D m_lastColor;
99 float m_lastAlphaMin = -1;
100 float m_lastAlphaMax = -1;
101};
102
103char const *const *QSGDistanceFieldTextMaterialShader::attributeNames() const {
104 static char const *const attr[] = { "vCoord", "tCoord", nullptr };
105 return attr;
106}
107
108QSGDistanceFieldTextMaterialShader::QSGDistanceFieldTextMaterialShader()
109{
110 setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldtext.vert"));
111 setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldtext.frag"));
112}
113
114void QSGDistanceFieldTextMaterialShader::updateAlphaRange()
115{
116 float combinedScale = m_fontScale * m_matrixScale;
117 float base = thresholdFunc(combinedScale);
118 float range = spreadFunc(combinedScale);
119 float alphaMin = qMax(0.0f, base - range);
120 float alphaMax = qMin(base + range, 1.0f);
121 if (alphaMin != m_lastAlphaMin) {
122 program()->setUniformValue(m_alphaMin_id, GLfloat(alphaMin));
123 m_lastAlphaMin = alphaMin;
124 }
125 if (alphaMax != m_lastAlphaMax) {
126 program()->setUniformValue(m_alphaMax_id, GLfloat(alphaMax));
127 m_lastAlphaMax = alphaMax;
128 }
129}
130
131void QSGDistanceFieldTextMaterialShader::updateColor(const QVector4D &c)
132{
133 if (m_lastColor != c) {
134 program()->setUniformValue(m_color_id, c);
135 m_lastColor = c;
136 }
137}
138
139void QSGDistanceFieldTextMaterialShader::updateTextureScale(const QVector2D &ts)
140{
141 if (m_lastTextureScale != ts) {
142 program()->setUniformValue(m_textureScale_id, ts);
143 m_lastTextureScale = ts;
144 }
145}
146
147void QSGDistanceFieldTextMaterialShader::initialize()
148{
149 QSGMaterialShader::initialize();
150 m_matrix_id = program()->uniformLocation("matrix");
151 m_textureScale_id = program()->uniformLocation("textureScale");
152 m_color_id = program()->uniformLocation("color");
153 m_alphaMin_id = program()->uniformLocation("alphaMin");
154 m_alphaMax_id = program()->uniformLocation("alphaMax");
155}
156
157void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
158{
159 Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type());
160 QSGDistanceFieldTextMaterial *material = static_cast<QSGDistanceFieldTextMaterial *>(newEffect);
161 QSGDistanceFieldTextMaterial *oldMaterial = static_cast<QSGDistanceFieldTextMaterial *>(oldEffect);
162
163 bool updated = material->updateTextureSize();
164
165 if (oldMaterial == nullptr
166 || material->color() != oldMaterial->color()
167 || state.isOpacityDirty()) {
168 QVector4D color = material->color();
169 color *= state.opacity();
170 updateColor(color);
171 }
172
173 bool updateRange = false;
174 if (oldMaterial == nullptr
175 || material->fontScale() != oldMaterial->fontScale()) {
176 m_fontScale = material->fontScale();
177 updateRange = true;
178 }
179 if (state.isMatrixDirty()) {
180 program()->setUniformValue(m_matrix_id, state.combinedMatrix());
181 m_matrixScale = qSqrt(qAbs(state.determinant())) * state.devicePixelRatio();
182 updateRange = true;
183 }
184 if (updateRange) {
185 updateAlphaRange();
186 }
187
188 Q_ASSERT(material->glyphCache());
189
190 if (updated
191 || oldMaterial == nullptr
192 || oldMaterial->texture()->textureId != material->texture()->textureId) {
193 updateTextureScale(QVector2D(1.0 / material->textureSize().width(),
194 1.0 / material->textureSize().height()));
195
196 QOpenGLFunctions *funcs = state.context()->functions();
197 funcs->glBindTexture(GL_TEXTURE_2D, material->texture()->textureId);
198
199 if (updated) {
200 // Set the mag/min filters to be linear. We only need to do this when the texture
201 // has been recreated.
202 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
203 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
204 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
205 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
206 }
207 }
208}
209
210class QSGDistanceFieldTextMaterialRhiShader : public QSGMaterialRhiShader
211{
212public:
213 QSGDistanceFieldTextMaterialRhiShader(bool alphaTexture);
214
215 bool updateUniformData(const RenderState &state,
216 QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
217
218 void updateSampledImage(const RenderState &state, int binding, QSGTexture **texture,
219 QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
220
221protected:
222 float m_fontScale = 1.0;
223 float m_matrixScale = 1.0;
224};
225
226QSGDistanceFieldTextMaterialRhiShader::QSGDistanceFieldTextMaterialRhiShader(bool alphaTexture)
227{
228 setShaderFileName(VertexStage,
229 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.vert.qsb"));
230 if (alphaTexture)
231 setShaderFileName(FragmentStage,
232 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext_a.frag.qsb"));
233 else
234 setShaderFileName(FragmentStage,
235 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.frag.qsb"));
236}
237
238bool QSGDistanceFieldTextMaterialRhiShader::updateUniformData(const RenderState &state,
239 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
240{
241 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
242 QSGDistanceFieldTextMaterial *mat = static_cast<QSGDistanceFieldTextMaterial *>(newMaterial);
243 QSGDistanceFieldTextMaterial *oldMat = static_cast<QSGDistanceFieldTextMaterial *>(oldMaterial);
244
245 // updateUniformData() is called before updateSampledImage() by the
246 // renderer. Hence updating the glyph cache stuff here.
247 const bool textureUpdated = mat->updateTextureSizeAndWrapper();
248 Q_ASSERT(mat->wrapperTexture());
249 Q_ASSERT(oldMat == nullptr || oldMat->texture());
250
251 bool changed = false;
252 QByteArray *buf = state.uniformData();
253 Q_ASSERT(buf->size() >= 104);
254
255 bool updateRange = false;
256 if (!oldMat || mat->fontScale() != oldMat->fontScale()) {
257 m_fontScale = mat->fontScale();
258 updateRange = true;
259 }
260 if (state.isMatrixDirty()) {
261 const QMatrix4x4 m = state.combinedMatrix();
262 memcpy(buf->data(), m.constData(), 64);
263 changed = true;
264 m_matrixScale = qSqrt(qAbs(state.determinant())) * state.devicePixelRatio();
265 updateRange = true;
266 }
267 if (textureUpdated || !oldMat || oldMat->texture()->texture != mat->texture()->texture) {
268 const QVector2D ts(1.0f / mat->textureSize().width(), 1.0f / mat->textureSize().height());
269 Q_ASSERT(sizeof(ts) == 8);
270 memcpy(buf->data() + 64, &ts, 8);
271 changed = true;
272 }
273 if (!oldMat || mat->color() != oldMat->color() || state.isOpacityDirty()) {
274 const QVector4D color = mat->color() * state.opacity();
275 Q_ASSERT(sizeof(color) == 16);
276 memcpy(buf->data() + 80, &color, 16);
277 changed = true;
278 }
279 if (updateRange) { // deferred because depends on m_fontScale and m_matrixScale
280 const float combinedScale = m_fontScale * m_matrixScale;
281 const float base = thresholdFunc(combinedScale);
282 const float range = spreadFunc(combinedScale);
283 const QVector2D alphaMinMax(qMax(0.0f, base - range), qMin(base + range, 1.0f));
284 memcpy(buf->data() + 96, &alphaMinMax, 8);
285 changed = true;
286 }
287
288 // move texture uploads/copies onto the renderer's soon-to-be-committed list
289 static_cast<QSGRhiDistanceFieldGlyphCache *>(mat->glyphCache())->commitResourceUpdates(state.resourceUpdateBatch());
290
291 return changed;
292}
293
294void QSGDistanceFieldTextMaterialRhiShader::updateSampledImage(const RenderState &state, int binding, QSGTexture **texture,
295 QSGMaterial *newMaterial, QSGMaterial *)
296{
297 Q_UNUSED(state);
298 if (binding != 1)
299 return;
300
301 QSGDistanceFieldTextMaterial *mat = static_cast<QSGDistanceFieldTextMaterial *>(newMaterial);
302 QSGTexture *t = mat->wrapperTexture();
303 t->setFiltering(QSGTexture::Linear);
304 *texture = t;
305}
306
307QSGDistanceFieldTextMaterial::QSGDistanceFieldTextMaterial()
308 : m_glyph_cache(nullptr)
309 , m_texture(nullptr)
310 , m_fontScale(1.0)
311 , m_sgTexture(nullptr)
312{
313 setFlag(Blending | RequiresDeterminant | SupportsRhiShader, true);
314}
315
316QSGDistanceFieldTextMaterial::~QSGDistanceFieldTextMaterial()
317{
318 delete m_sgTexture;
319}
320
321QSGMaterialType *QSGDistanceFieldTextMaterial::type() const
322{
323 static QSGMaterialType type;
324 return &type;
325}
326
327void QSGDistanceFieldTextMaterial::setColor(const QColor &color)
328{
329 m_color = QVector4D(color.redF() * color.alphaF(),
330 color.greenF() * color.alphaF(),
331 color.blueF() * color.alphaF(),
332 color.alphaF());
333}
334
335QSGMaterialShader *QSGDistanceFieldTextMaterial::createShader() const
336{
337 if (flags().testFlag(RhiShaderWanted))
338 return new QSGDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
339 else
340 return new QSGDistanceFieldTextMaterialShader;
341}
342
343bool QSGDistanceFieldTextMaterial::updateTextureSize()
344{
345 if (!m_texture)
346 m_texture = m_glyph_cache->glyphTexture(0); // invalid texture
347
348 if (m_texture->size != m_size) {
349 m_size = m_texture->size;
350 return true;
351 }
352
353 return false;
354}
355
356// When using the RHI we need a QSGTexture wrapping the QRhiTexture, just
357// exposing a QRhiTexture * (which would be the equivalent of GLuint textureId)
358// is not sufficient to play nice with the material.
359bool QSGDistanceFieldTextMaterial::updateTextureSizeAndWrapper()
360{
361 bool updated = updateTextureSize();
362 if (updated) {
363 if (m_sgTexture)
364 delete m_sgTexture;
365 m_sgTexture = new QSGPlainTexture;
366 m_sgTexture->setTexture(m_texture->texture);
367 m_sgTexture->setTextureSize(m_size);
368 m_sgTexture->setOwnsTexture(false);
369 }
370 return updated;
371}
372
373int QSGDistanceFieldTextMaterial::compare(const QSGMaterial *o) const
374{
375 Q_ASSERT(o && type() == o->type());
376 const QSGDistanceFieldTextMaterial *other = static_cast<const QSGDistanceFieldTextMaterial *>(o);
377 if (m_glyph_cache != other->m_glyph_cache)
378 return m_glyph_cache - other->m_glyph_cache;
379 if (m_fontScale != other->m_fontScale) {
380 return int(other->m_fontScale < m_fontScale) - int(m_fontScale < other->m_fontScale);
381 }
382 if (m_color != other->m_color)
383 return &m_color < &other->m_color ? -1 : 1;
384 int t0 = m_texture ? (m_texture->rhiBased ? qintptr(m_texture->texture) : m_texture->textureId) : 0;
385 int t1 = other->m_texture ? (other->m_texture->rhiBased ? qintptr(other->m_texture->texture) : other->m_texture->textureId) : 0;
386 return t0 - t1;
387}
388
389
390class DistanceFieldStyledTextMaterialShader : public QSGDistanceFieldTextMaterialShader
391{
392public:
393 DistanceFieldStyledTextMaterialShader();
394
395 void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
396
397protected:
398 void initialize() override;
399
400 int m_styleColor_id = -1;
401};
402
403DistanceFieldStyledTextMaterialShader::DistanceFieldStyledTextMaterialShader()
404 : QSGDistanceFieldTextMaterialShader()
405{
406}
407
408void DistanceFieldStyledTextMaterialShader::initialize()
409{
410 QSGDistanceFieldTextMaterialShader::initialize();
411 m_styleColor_id = program()->uniformLocation("styleColor");
412}
413
414void DistanceFieldStyledTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
415{
416 QSGDistanceFieldTextMaterialShader::updateState(state, newEffect, oldEffect);
417
418 QSGDistanceFieldStyledTextMaterial *material = static_cast<QSGDistanceFieldStyledTextMaterial *>(newEffect);
419 QSGDistanceFieldStyledTextMaterial *oldMaterial = static_cast<QSGDistanceFieldStyledTextMaterial *>(oldEffect);
420
421 if (oldMaterial == nullptr
422 || material->styleColor() != oldMaterial->styleColor()
423 || (state.isOpacityDirty())) {
424 QVector4D color = material->styleColor();
425 color *= state.opacity();
426 program()->setUniformValue(m_styleColor_id, color);
427 }
428}
429
430class DistanceFieldStyledTextMaterialRhiShader : public QSGDistanceFieldTextMaterialRhiShader
431{
432public:
433 DistanceFieldStyledTextMaterialRhiShader(bool alphaTexture);
434
435 bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
436};
437
438DistanceFieldStyledTextMaterialRhiShader::DistanceFieldStyledTextMaterialRhiShader(bool alphaTexture)
439 : QSGDistanceFieldTextMaterialRhiShader(alphaTexture)
440{
441}
442
443bool DistanceFieldStyledTextMaterialRhiShader::updateUniformData(const RenderState &state,
444 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
445{
446 bool changed = QSGDistanceFieldTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
447 QSGDistanceFieldStyledTextMaterial *mat = static_cast<QSGDistanceFieldStyledTextMaterial *>(newMaterial);
448 QSGDistanceFieldStyledTextMaterial *oldMat = static_cast<QSGDistanceFieldStyledTextMaterial *>(oldMaterial);
449
450 QByteArray *buf = state.uniformData();
451 Q_ASSERT(buf->size() >= 128);
452
453 if (!oldMat || mat->styleColor() != oldMat->styleColor() || state.isOpacityDirty()) {
454 QVector4D styleColor = mat->styleColor();
455 styleColor *= state.opacity();
456 memcpy(buf->data() + 112, &styleColor, 16);
457 changed = true;
458 }
459
460 return changed;
461}
462
463QSGDistanceFieldStyledTextMaterial::QSGDistanceFieldStyledTextMaterial()
464 : QSGDistanceFieldTextMaterial()
465{
466}
467
468QSGDistanceFieldStyledTextMaterial::~QSGDistanceFieldStyledTextMaterial()
469{
470}
471
472void QSGDistanceFieldStyledTextMaterial::setStyleColor(const QColor &color)
473{
474 m_styleColor = QVector4D(color.redF() * color.alphaF(),
475 color.greenF() * color.alphaF(),
476 color.blueF() * color.alphaF(),
477 color.alphaF());
478}
479
480int QSGDistanceFieldStyledTextMaterial::compare(const QSGMaterial *o) const
481{
482 Q_ASSERT(o && type() == o->type());
483 const QSGDistanceFieldStyledTextMaterial *other = static_cast<const QSGDistanceFieldStyledTextMaterial *>(o);
484 if (m_styleColor != other->m_styleColor)
485 return &m_styleColor < &other->m_styleColor ? -1 : 1;
486 return QSGDistanceFieldTextMaterial::compare(o);
487}
488
489
490class DistanceFieldOutlineTextMaterialShader : public DistanceFieldStyledTextMaterialShader
491{
492public:
493 DistanceFieldOutlineTextMaterialShader();
494
495 void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
496
497protected:
498 void initialize() override;
499
500 void updateOutlineAlphaRange(int dfRadius);
501
502 int m_outlineAlphaMax0_id = -1;
503 int m_outlineAlphaMax1_id = -1;
504};
505
506DistanceFieldOutlineTextMaterialShader::DistanceFieldOutlineTextMaterialShader()
507 : DistanceFieldStyledTextMaterialShader()
508{
509 setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldoutlinetext.frag"));
510}
511
512void DistanceFieldOutlineTextMaterialShader::initialize()
513{
514 DistanceFieldStyledTextMaterialShader::initialize();
515 m_outlineAlphaMax0_id = program()->uniformLocation("outlineAlphaMax0");
516 m_outlineAlphaMax1_id = program()->uniformLocation("outlineAlphaMax1");
517}
518
519void DistanceFieldOutlineTextMaterialShader::updateOutlineAlphaRange(int dfRadius)
520{
521 float combinedScale = m_fontScale * m_matrixScale;
522 float base = thresholdFunc(combinedScale);
523 float range = spreadFunc(combinedScale);
524 float outlineLimit = qMax(0.2f, base - 0.5f / dfRadius / m_fontScale);
525
526 float alphaMin = qMax(0.0f, base - range);
527 float styleAlphaMin0 = qMax(0.0f, outlineLimit - range);
528 float styleAlphaMin1 = qMin(outlineLimit + range, alphaMin);
529 program()->setUniformValue(m_outlineAlphaMax0_id, GLfloat(styleAlphaMin0));
530 program()->setUniformValue(m_outlineAlphaMax1_id, GLfloat(styleAlphaMin1));
531}
532
533void DistanceFieldOutlineTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
534{
535 DistanceFieldStyledTextMaterialShader::updateState(state, newEffect, oldEffect);
536
537 QSGDistanceFieldOutlineTextMaterial *material = static_cast<QSGDistanceFieldOutlineTextMaterial *>(newEffect);
538 QSGDistanceFieldOutlineTextMaterial *oldMaterial = static_cast<QSGDistanceFieldOutlineTextMaterial *>(oldEffect);
539
540 if (oldMaterial == nullptr
541 || material->fontScale() != oldMaterial->fontScale()
542 || state.isMatrixDirty())
543 updateOutlineAlphaRange(material->glyphCache()->distanceFieldRadius());
544}
545
546class DistanceFieldOutlineTextMaterialRhiShader : public DistanceFieldStyledTextMaterialRhiShader
547{
548public:
549 DistanceFieldOutlineTextMaterialRhiShader(bool alphaTexture);
550
551 bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
552};
553
554DistanceFieldOutlineTextMaterialRhiShader::DistanceFieldOutlineTextMaterialRhiShader(bool alphaTexture)
555 : DistanceFieldStyledTextMaterialRhiShader(alphaTexture)
556{
557 setShaderFileName(VertexStage,
558 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb"));
559 if (alphaTexture)
560 setShaderFileName(FragmentStage,
561 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsb"));
562 else
563 setShaderFileName(FragmentStage,
564 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsb"));
565}
566
567bool DistanceFieldOutlineTextMaterialRhiShader::updateUniformData(const RenderState &state,
568 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
569{
570 bool changed = DistanceFieldStyledTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
571 QSGDistanceFieldOutlineTextMaterial *mat = static_cast<QSGDistanceFieldOutlineTextMaterial *>(newMaterial);
572 QSGDistanceFieldOutlineTextMaterial *oldMat = static_cast<QSGDistanceFieldOutlineTextMaterial *>(oldMaterial);
573
574 QByteArray *buf = state.uniformData();
575 Q_ASSERT(buf->size() >= 136);
576
577 if (!oldMat || mat->fontScale() != oldMat->fontScale() || state.isMatrixDirty()) {
578 float dfRadius = mat->glyphCache()->distanceFieldRadius();
579 float combinedScale = m_fontScale * m_matrixScale;
580 float base = thresholdFunc(combinedScale);
581 float range = spreadFunc(combinedScale);
582 float outlineLimit = qMax(0.2f, base - 0.5f / dfRadius / m_fontScale);
583 float alphaMin = qMax(0.0f, base - range);
584 float styleAlphaMin0 = qMax(0.0f, outlineLimit - range);
585 float styleAlphaMin1 = qMin(outlineLimit + range, alphaMin);
586 memcpy(buf->data() + 128, &styleAlphaMin0, 4);
587 memcpy(buf->data() + 132, &styleAlphaMin1, 4);
588 changed = true;
589 }
590
591 return changed;
592}
593
594QSGDistanceFieldOutlineTextMaterial::QSGDistanceFieldOutlineTextMaterial()
595 : QSGDistanceFieldStyledTextMaterial()
596{
597}
598
599QSGDistanceFieldOutlineTextMaterial::~QSGDistanceFieldOutlineTextMaterial()
600{
601}
602
603QSGMaterialType *QSGDistanceFieldOutlineTextMaterial::type() const
604{
605 static QSGMaterialType type;
606 return &type;
607}
608
609QSGMaterialShader *QSGDistanceFieldOutlineTextMaterial::createShader() const
610{
611 if (flags().testFlag(RhiShaderWanted))
612 return new DistanceFieldOutlineTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
613 else
614 return new DistanceFieldOutlineTextMaterialShader;
615}
616
617
618class DistanceFieldShiftedStyleTextMaterialShader : public DistanceFieldStyledTextMaterialShader
619{
620public:
621 DistanceFieldShiftedStyleTextMaterialShader();
622
623 void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
624
625protected:
626 void initialize() override;
627
628 void updateShift(qreal fontScale, const QPointF& shift);
629
630 int m_shift_id = -1;
631};
632
633DistanceFieldShiftedStyleTextMaterialShader::DistanceFieldShiftedStyleTextMaterialShader()
634 : DistanceFieldStyledTextMaterialShader()
635{
636 setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldshiftedtext.vert"));
637 setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldshiftedtext.frag"));
638}
639
640void DistanceFieldShiftedStyleTextMaterialShader::initialize()
641{
642 DistanceFieldStyledTextMaterialShader::initialize();
643 m_shift_id = program()->uniformLocation("shift");
644}
645
646void DistanceFieldShiftedStyleTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
647{
648 DistanceFieldStyledTextMaterialShader::updateState(state, newEffect, oldEffect);
649
650 QSGDistanceFieldShiftedStyleTextMaterial *material = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(newEffect);
651 QSGDistanceFieldShiftedStyleTextMaterial *oldMaterial = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(oldEffect);
652
653 if (oldMaterial == nullptr
654 || oldMaterial->fontScale() != material->fontScale()
655 || oldMaterial->shift() != material->shift()
656 || oldMaterial->textureSize() != material->textureSize()) {
657 updateShift(material->fontScale(), material->shift());
658 }
659}
660
661void DistanceFieldShiftedStyleTextMaterialShader::updateShift(qreal fontScale, const QPointF &shift)
662{
663 QPointF texel(1.0 / fontScale * shift.x(),
664 1.0 / fontScale * shift.y());
665 program()->setUniformValue(m_shift_id, texel);
666}
667
668class DistanceFieldShiftedStyleTextMaterialRhiShader : public DistanceFieldStyledTextMaterialRhiShader
669{
670public:
671 DistanceFieldShiftedStyleTextMaterialRhiShader(bool alphaTexture);
672
673 bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
674};
675
676DistanceFieldShiftedStyleTextMaterialRhiShader::DistanceFieldShiftedStyleTextMaterialRhiShader(bool alphaTexture)
677 : DistanceFieldStyledTextMaterialRhiShader(alphaTexture)
678{
679 setShaderFileName(VertexStage,
680 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb"));
681 if (alphaTexture)
682 setShaderFileName(FragmentStage,
683 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsb"));
684 else
685 setShaderFileName(FragmentStage,
686 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsb"));
687}
688
689bool DistanceFieldShiftedStyleTextMaterialRhiShader::updateUniformData(const RenderState &state,
690 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
691{
692 bool changed = DistanceFieldStyledTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
693 QSGDistanceFieldShiftedStyleTextMaterial *mat = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(newMaterial);
694 QSGDistanceFieldShiftedStyleTextMaterial *oldMat = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(oldMaterial);
695
696 QByteArray *buf = state.uniformData();
697 Q_ASSERT(buf->size() >= 136);
698
699 if (!oldMat || oldMat->fontScale() != mat->fontScale() || oldMat->shift() != mat->shift()
700 || oldMat->textureSize() != mat->textureSize())
701 {
702 QPointF shift(1.0 / mat->fontScale() * mat->shift().x(),
703 1.0 / mat->fontScale() * mat->shift().y());
704 memcpy(buf->data() + 128, &shift, 8);
705 changed = true;
706 }
707
708 return changed;
709}
710
711QSGDistanceFieldShiftedStyleTextMaterial::QSGDistanceFieldShiftedStyleTextMaterial()
712 : QSGDistanceFieldStyledTextMaterial()
713{
714}
715
716QSGDistanceFieldShiftedStyleTextMaterial::~QSGDistanceFieldShiftedStyleTextMaterial()
717{
718}
719
720QSGMaterialType *QSGDistanceFieldShiftedStyleTextMaterial::type() const
721{
722 static QSGMaterialType type;
723 return &type;
724}
725
726QSGMaterialShader *QSGDistanceFieldShiftedStyleTextMaterial::createShader() const
727{
728 if (flags().testFlag(RhiShaderWanted))
729 return new DistanceFieldShiftedStyleTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
730 else
731 return new DistanceFieldShiftedStyleTextMaterialShader;
732}
733
734int QSGDistanceFieldShiftedStyleTextMaterial::compare(const QSGMaterial *o) const
735{
736 const QSGDistanceFieldShiftedStyleTextMaterial *other = static_cast<const QSGDistanceFieldShiftedStyleTextMaterial *>(o);
737 if (m_shift != other->m_shift)
738 return &m_shift < &other->m_shift ? -1 : 1;
739 return QSGDistanceFieldStyledTextMaterial::compare(o);
740}
741
742
743class QSGHiQSubPixelDistanceFieldTextMaterialShader : public QSGDistanceFieldTextMaterialShader
744{
745public:
746 QSGHiQSubPixelDistanceFieldTextMaterialShader();
747
748 void initialize() override;
749 void activate() override;
750 void deactivate() override;
751 void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
752
753private:
754 int m_fontScale_id = -1;
755 int m_vecDelta_id = -1;
756};
757
758QSGHiQSubPixelDistanceFieldTextMaterialShader::QSGHiQSubPixelDistanceFieldTextMaterialShader()
759 : QSGDistanceFieldTextMaterialShader()
760{
761 setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/hiqsubpixeldistancefieldtext.vert"));
762 setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/hiqsubpixeldistancefieldtext.frag"));
763}
764
765void QSGHiQSubPixelDistanceFieldTextMaterialShader::initialize()
766{
767 QSGDistanceFieldTextMaterialShader::initialize();
768 m_fontScale_id = program()->uniformLocation("fontScale");
769 m_vecDelta_id = program()->uniformLocation("vecDelta");
770}
771
772void QSGHiQSubPixelDistanceFieldTextMaterialShader::activate()
773{
774 QSGDistanceFieldTextMaterialShader::activate();
775 QOpenGLContext::currentContext()->functions()->glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR);
776}
777
778void QSGHiQSubPixelDistanceFieldTextMaterialShader::deactivate()
779{
780 QSGDistanceFieldTextMaterialShader::deactivate();
781 QOpenGLContext::currentContext()->functions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
782}
783
784void QSGHiQSubPixelDistanceFieldTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
785{
786 Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type());
787 QSGDistanceFieldTextMaterial *material = static_cast<QSGDistanceFieldTextMaterial *>(newEffect);
788 QSGDistanceFieldTextMaterial *oldMaterial = static_cast<QSGDistanceFieldTextMaterial *>(oldEffect);
789
790 if (oldMaterial == nullptr || material->color() != oldMaterial->color()) {
791 QVector4D c = material->color();
792 state.context()->functions()->glBlendColor(c.x(), c.y(), c.z(), 1.0f);
793 }
794
795 if (oldMaterial == nullptr || material->fontScale() != oldMaterial->fontScale())
796 program()->setUniformValue(m_fontScale_id, GLfloat(material->fontScale()));
797
798 if (oldMaterial == nullptr || state.isMatrixDirty()) {
799 int viewportWidth = state.viewportRect().width();
800 QMatrix4x4 mat = state.combinedMatrix().inverted();
801 program()->setUniformValue(m_vecDelta_id, mat.column(0) * (qreal(2) / viewportWidth));
802 }
803
804 QSGDistanceFieldTextMaterialShader::updateState(state, newEffect, oldEffect);
805}
806
807class QSGHiQSubPixelDistanceFieldTextMaterialRhiShader : public QSGDistanceFieldTextMaterialRhiShader
808{
809public:
810 QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture);
811
812 bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
813 bool updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps,
814 QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
815};
816
817QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture)
818 : QSGDistanceFieldTextMaterialRhiShader(alphaTexture)
819{
820 setFlag(UpdatesGraphicsPipelineState, true);
821
822 setShaderFileName(VertexStage,
823 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb"));
824 if (alphaTexture)
825 setShaderFileName(FragmentStage,
826 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb"));
827 else
828 setShaderFileName(FragmentStage,
829 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb"));
830}
831
832bool QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::updateUniformData(const RenderState &state,
833 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
834{
835 bool changed = QSGDistanceFieldTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
836 QSGHiQSubPixelDistanceFieldTextMaterial *mat = static_cast<QSGHiQSubPixelDistanceFieldTextMaterial *>(newMaterial);
837 QSGHiQSubPixelDistanceFieldTextMaterial *oldMat = static_cast<QSGHiQSubPixelDistanceFieldTextMaterial *>(oldMaterial);
838
839 QByteArray *buf = state.uniformData();
840 Q_ASSERT(buf->size() >= 128);
841
842 if (!oldMat || mat->fontScale() != oldMat->fontScale()) {
843 float fontScale = mat->fontScale();
844 memcpy(buf->data() + 104, &fontScale, 4);
845 changed = true;
846 }
847
848 if (!oldMat || state.isMatrixDirty()) {
849 int viewportWidth = state.viewportRect().width();
850 QMatrix4x4 mat = state.combinedMatrix().inverted();
851 QVector4D vecDelta = mat.column(0) * (qreal(2) / viewportWidth);
852 memcpy(buf->data() + 112, &vecDelta, 16);
853 }
854
855 return changed;
856}
857
858bool QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps,
859 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
860{
861 Q_UNUSED(state);
862 Q_UNUSED(oldMaterial);
863 QSGHiQSubPixelDistanceFieldTextMaterial *mat = static_cast<QSGHiQSubPixelDistanceFieldTextMaterial *>(newMaterial);
864
865 ps->blendEnable = true;
866 ps->srcColor = GraphicsPipelineState::ConstantColor;
867 ps->dstColor = GraphicsPipelineState::OneMinusSrcColor;
868
869 const QVector4D color = mat->color();
870 // this is dynamic state but it's - magic! - taken care of by the renderer
871 ps->blendConstant = QColor::fromRgbF(color.x(), color.y(), color.z(), 1.0f);
872
873 return true;
874}
875
876QSGMaterialType *QSGHiQSubPixelDistanceFieldTextMaterial::type() const
877{
878 static QSGMaterialType type;
879 return &type;
880}
881
882QSGMaterialShader *QSGHiQSubPixelDistanceFieldTextMaterial::createShader() const
883{
884 if (flags().testFlag(RhiShaderWanted))
885 return new QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
886 else
887 return new QSGHiQSubPixelDistanceFieldTextMaterialShader;
888}
889
890
891class QSGLoQSubPixelDistanceFieldTextMaterialShader : public QSGHiQSubPixelDistanceFieldTextMaterialShader
892{
893public:
894 QSGLoQSubPixelDistanceFieldTextMaterialShader();
895};
896
897QSGLoQSubPixelDistanceFieldTextMaterialShader::QSGLoQSubPixelDistanceFieldTextMaterialShader()
898 : QSGHiQSubPixelDistanceFieldTextMaterialShader()
899{
900 setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/loqsubpixeldistancefieldtext.vert"));
901 setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/loqsubpixeldistancefieldtext.frag"));
902}
903
904class QSGLoQSubPixelDistanceFieldTextMaterialRhiShader : public QSGHiQSubPixelDistanceFieldTextMaterialRhiShader
905{
906public:
907 QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture);
908};
909
910QSGLoQSubPixelDistanceFieldTextMaterialRhiShader::QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture)
911 : QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(alphaTexture)
912{
913 setShaderFileName(VertexStage,
914 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsb"));
915 if (alphaTexture)
916 setShaderFileName(FragmentStage,
917 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb"));
918 else
919 setShaderFileName(FragmentStage,
920 QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsb"));
921}
922
923QSGMaterialType *QSGLoQSubPixelDistanceFieldTextMaterial::type() const
924{
925 static QSGMaterialType type;
926 return &type;
927}
928
929QSGMaterialShader *QSGLoQSubPixelDistanceFieldTextMaterial::createShader() const
930{
931 if (flags().testFlag(RhiShaderWanted))
932 return new QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
933 else
934 return new QSGLoQSubPixelDistanceFieldTextMaterialShader;
935}
936
937QT_END_NAMESPACE
938