Avoid direct GL calls in Quick
[qt:qtdeclarative.git] / src / quick / scenegraph / qsgdefaultglyphnode_p.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgdefaultglyphnode_p_p.h"
43 #include <private/qsgmaterialshader_p.h>
44
45 #include <qopenglshaderprogram.h>
46
47 #include <QtGui/private/qguiapplication_p.h>
48 #include <qpa/qplatformintegration.h>
49 #include <private/qfontengine_p.h>
50 #include <private/qopenglextensions_p.h>
51
52 #include <QtQuick/private/qsgtexture_p.h>
53
54 #include <private/qrawfont_p.h>
55 #include <QtCore/qmath.h>
56
57 QT_BEGIN_NAMESPACE
58
59 #ifndef GL_FRAMEBUFFER_SRGB
60 #define GL_FRAMEBUFFER_SRGB 0x8DB9
61 #endif
62
63 static inline QVector4D qsg_premultiply(const QVector4D &c, float globalOpacity)
64 {
65     float o = c.w() * globalOpacity;
66     return QVector4D(c.x() * o, c.y() * o, c.z() * o, o);
67 }
68
69 class QSGTextMaskShader : public QSGMaterialShader
70 {
71 public:
72     QSGTextMaskShader(QFontEngine::GlyphFormat glyphFormat);
73
74     virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
75     virtual char const *const *attributeNames() const;
76
77 protected:
78     virtual void initialize();
79
80     int m_matrix_id;
81     int m_color_id;
82     int m_textureScale_id;
83
84     QFontEngine::GlyphFormat m_glyphFormat;
85 };
86
87 char const *const *QSGTextMaskShader::attributeNames() const
88 {
89     static char const *const attr[] = { "vCoord", "tCoord", 0 };
90     return attr;
91 }
92
93 QSGTextMaskShader::QSGTextMaskShader(QFontEngine::GlyphFormat glyphFormat)
94     : QSGMaterialShader(*new QSGMaterialShaderPrivate),
95       m_glyphFormat(glyphFormat)
96 {
97     setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/scenegraph/shaders/textmask.vert"));
98     setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/scenegraph/shaders/textmask.frag"));
99 }
100
101 static inline qreal fontSmoothingGamma()
102 {
103     static qreal fontSmoothingGamma = QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FontSmoothingGamma).toReal();
104     return fontSmoothingGamma;
105 }
106
107 void QSGTextMaskShader::initialize()
108 {
109     m_matrix_id = program()->uniformLocation("matrix");
110     m_color_id = program()->uniformLocation("color");
111     m_textureScale_id = program()->uniformLocation("textureScale");
112 }
113
114 void QSGTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
115 {
116     QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect);
117     QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect);
118     Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type());
119     bool updated = material->ensureUpToDate();
120     Q_ASSERT(material->texture());
121
122     Q_ASSERT(oldMaterial == 0 || oldMaterial->texture());
123     if (updated
124             || oldMaterial == 0
125             || oldMaterial->texture()->textureId() != material->texture()->textureId()) {
126         program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->cacheTextureWidth(),
127                                                                1.0 / material->cacheTextureHeight()));
128         QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
129         funcs->glBindTexture(GL_TEXTURE_2D, material->texture()->textureId());
130
131         // Set the mag/min filters to be nearest. We only need to do this when the texture
132         // has been recreated.
133         if (updated) {
134             funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
135             funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
136         }
137     }
138
139     if (state.isMatrixDirty()) {
140         QMatrix4x4 transform = state.modelViewMatrix();
141         qreal xTranslation = transform(0, 3);
142         qreal yTranslation = transform(1, 3);
143
144         // Remove translation and check identity to see if matrix is only translating.
145         // If it is, we can round the translation to make sure the text is pixel aligned,
146         // which is the only thing that works with GL_NEAREST filtering. Adding rotations
147         // and scales to native rendered text is not a prioritized use case, since the
148         // default rendering type is designed for that.
149         transform(0, 3) = 0.0;
150         transform(1, 3) = 0.0;
151         if (transform.isIdentity()) {
152             transform(0, 3) = qRound(xTranslation);
153             transform(1, 3) = qRound(yTranslation);
154
155             transform = state.projectionMatrix() * transform;
156             program()->setUniformValue(m_matrix_id, transform);
157         } else {
158             program()->setUniformValue(m_matrix_id, state.combinedMatrix());
159         }
160     }
161 }
162
163 class QSG8BitTextMaskShader : public QSGTextMaskShader
164 {
165 public:
166     QSG8BitTextMaskShader(QFontEngine::GlyphFormat glyphFormat)
167         : QSGTextMaskShader(glyphFormat)
168     {
169         setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/scenegraph/shaders/8bittextmask.frag"));
170     }
171
172     virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
173 };
174
175 void QSG8BitTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
176 {
177     QSGTextMaskShader::updateState(state, newEffect, oldEffect);
178     QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect);
179     QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect);
180
181     if (oldMaterial == 0 || material->color() != oldMaterial->color() || state.isOpacityDirty()) {
182         QVector4D color = qsg_premultiply(material->color(), state.opacity());
183         program()->setUniformValue(m_color_id, color);
184     }
185 }
186
187 class QSG24BitTextMaskShader : public QSGTextMaskShader
188 {
189 public:
190     QSG24BitTextMaskShader(QFontEngine::GlyphFormat glyphFormat)
191         : QSGTextMaskShader(glyphFormat)
192         , m_useSRGB(false)
193     {
194         setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/scenegraph/shaders/24bittextmask.frag"));
195     }
196
197     virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
198     virtual void initialize();
199     void activate();
200     void deactivate();
201
202     uint m_useSRGB : 1;
203 };
204
205 void QSG24BitTextMaskShader::initialize()
206 {
207     QSGTextMaskShader::initialize();
208     // 0.25 was found to be acceptable error margin by experimentation. On Mac, the gamma is 2.0,
209     // but using sRGB looks okay.
210     if (QOpenGLContext::currentContext()->hasExtension(QByteArrayLiteral("GL_ARB_framebuffer_sRGB"))
211             && m_glyphFormat == QFontEngine::Format_A32
212             && qAbs(fontSmoothingGamma() - 2.2) < 0.25) {
213         m_useSRGB = true;
214     }
215 }
216
217 void QSG24BitTextMaskShader::activate()
218 {
219     QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
220     funcs->glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR);
221     if (m_useSRGB)
222         funcs->glEnable(GL_FRAMEBUFFER_SRGB);
223 }
224
225 void QSG24BitTextMaskShader::deactivate()
226 {
227     QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
228     funcs->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
229     if (m_useSRGB)
230         funcs->glDisable(GL_FRAMEBUFFER_SRGB);
231 }
232
233 static inline qreal qt_sRGB_to_linear_RGB(qreal f)
234 {
235     return f > 0.04045 ? qPow((f + 0.055) / 1.055, 2.4) : f / 12.92;
236 }
237
238 static inline QVector4D qt_sRGB_to_linear_RGB(const QVector4D &color)
239 {
240     return QVector4D(qt_sRGB_to_linear_RGB(color.x()),
241                      qt_sRGB_to_linear_RGB(color.y()),
242                      qt_sRGB_to_linear_RGB(color.z()),
243                      color.w());
244 }
245
246 void QSG24BitTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
247 {
248     QSGTextMaskShader::updateState(state, newEffect, oldEffect);
249     QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect);
250     QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect);
251
252     if (oldMaterial == 0 || material->color() != oldMaterial->color() || state.isOpacityDirty()) {
253         QVector4D color = material->color();
254         if (m_useSRGB)
255             color = qt_sRGB_to_linear_RGB(color);
256         QOpenGLContext::currentContext()->functions()->glBlendColor(color.x(), color.y(), color.z(), color.w());
257         color = qsg_premultiply(color, state.opacity());
258         program()->setUniformValue(m_color_id, color.w());
259     }
260 }
261
262 class QSGStyledTextShader : public QSG8BitTextMaskShader
263 {
264 public:
265     QSGStyledTextShader(QFontEngine::GlyphFormat glyphFormat)
266         : QSG8BitTextMaskShader(glyphFormat)
267     {
268         setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/scenegraph/shaders/styledtext.vert"));
269         setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/scenegraph/shaders/styledtext.frag"));
270     }
271
272     virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
273
274 private:
275     virtual void initialize();
276
277     int m_shift_id;
278     int m_styleColor_id;
279 };
280
281 void QSGStyledTextShader::initialize()
282 {
283     QSG8BitTextMaskShader::initialize();
284     m_shift_id = program()->uniformLocation("shift");
285     m_styleColor_id = program()->uniformLocation("styleColor");
286 }
287
288 void QSGStyledTextShader::updateState(const RenderState &state,
289                                       QSGMaterial *newEffect,
290                                       QSGMaterial *oldEffect)
291 {
292     Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type());
293
294     QSGStyledTextMaterial *material = static_cast<QSGStyledTextMaterial *>(newEffect);
295     QSGStyledTextMaterial *oldMaterial = static_cast<QSGStyledTextMaterial *>(oldEffect);
296
297     if (oldMaterial == 0 || oldMaterial->styleShift() != material->styleShift())
298         program()->setUniformValue(m_shift_id, material->styleShift());
299
300     if (oldMaterial == 0 || material->color() != oldMaterial->color() || state.isOpacityDirty()) {
301         QVector4D color = qsg_premultiply(material->color(), state.opacity());
302         program()->setUniformValue(m_color_id, color);
303     }
304
305     if (oldMaterial == 0 || material->styleColor() != oldMaterial->styleColor() || state.isOpacityDirty()) {
306         QVector4D styleColor = qsg_premultiply(material->styleColor(), state.opacity());
307         program()->setUniformValue(m_styleColor_id, styleColor);
308     }
309
310     bool updated = material->ensureUpToDate();
311     Q_ASSERT(material->texture());
312
313     Q_ASSERT(oldMaterial == 0 || oldMaterial->texture());
314     if (updated
315             || oldMaterial == 0
316             || oldMaterial->texture()->textureId() != material->texture()->textureId()) {
317         program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->cacheTextureWidth(),
318                                                                 1.0 / material->cacheTextureHeight()));
319         QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
320         funcs->glBindTexture(GL_TEXTURE_2D, material->texture()->textureId());
321
322         // Set the mag/min filters to be linear. We only need to do this when the texture
323         // has been recreated.
324         if (updated) {
325             funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
326             funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
327         }
328     }
329
330     if (state.isMatrixDirty())
331         program()->setUniformValue(m_matrix_id, state.combinedMatrix());
332 }
333
334 class QSGOutlinedTextShader : public QSGStyledTextShader
335 {
336 public:
337     QSGOutlinedTextShader(QFontEngine::GlyphFormat glyphFormat)
338         : QSGStyledTextShader(glyphFormat)
339     {
340         setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/scenegraph/shaders/outlinedtext.vert"));
341         setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/scenegraph/shaders/outlinedtext.frag"));
342     }
343 };
344
345 QSGTextMaskMaterial::QSGTextMaskMaterial(const QRawFont &font, QFontEngine::GlyphFormat glyphFormat)
346     : m_texture(0)
347     , m_glyphCache(0)
348     , m_font(font)
349 {
350     init(glyphFormat);
351 }
352
353 QSGTextMaskMaterial::~QSGTextMaskMaterial()
354 {
355 }
356
357 void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat)
358 {
359     Q_ASSERT(m_font.isValid());
360
361     setFlag(Blending, true);
362
363     QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
364     Q_ASSERT(ctx != 0);
365
366     // The following piece of code will read/write to the font engine's caches,
367     // potentially from different threads. However, this is safe because this
368     // code is only called from QQuickItem::updatePaintNode() which is called
369     // only when the GUI is blocked, and multiple threads will call it in
370     // sequence. See also QSGRenderContext::invalidate
371
372     QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
373     if (QFontEngine *fontEngine = fontD->fontEngine) {
374         if (glyphFormat == QFontEngine::Format_None) {
375             glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None
376                         ? fontEngine->glyphFormat
377                         : QFontEngine::Format_A32;
378         }
379
380         qreal devicePixelRatio = ctx->surface()->surfaceClass() == QSurface::Window ?
381             static_cast<QWindow *>(ctx->surface())->devicePixelRatio() : ctx->screen()->devicePixelRatio();
382
383         QTransform glyphCacheTransform = QTransform::fromScale(devicePixelRatio, devicePixelRatio);
384         if (!fontEngine->supportsTransformation(glyphCacheTransform))
385             glyphCacheTransform = QTransform();
386
387         m_glyphCache = fontEngine->glyphCache(ctx, glyphFormat, glyphCacheTransform);
388         if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) {
389             m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform);
390             fontEngine->setGlyphCache(ctx, m_glyphCache.data());
391             QSGRenderContext *sg = QSGRenderContext::from(ctx);
392             Q_ASSERT(sg);
393             sg->registerFontengineForCleanup(fontEngine);
394         }
395     }
396 }
397
398 void QSGTextMaskMaterial::populate(const QPointF &p,
399                                    const QVector<quint32> &glyphIndexes,
400                                    const QVector<QPointF> &glyphPositions,
401                                    QSGGeometry *geometry,
402                                    QRectF *boundingRect,
403                                    QPointF *baseLine,
404                                    const QMargins &margins)
405 {
406     Q_ASSERT(m_font.isValid());
407     QVector<QFixedPoint> fixedPointPositions;
408     for (int i=0; i<glyphPositions.size(); ++i)
409         fixedPointPositions.append(QFixedPoint::fromPointF(glyphPositions.at(i)));
410
411     QTextureGlyphCache *cache = glyphCache();
412
413     QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
414     cache->populate(fontD->fontEngine, glyphIndexes.size(), glyphIndexes.constData(),
415                     fixedPointPositions.data());
416     cache->fillInPendingGlyphs();
417
418     int margin = fontD->fontEngine->glyphMargin(cache->glyphFormat());
419
420     qreal glyphCacheScaleX = cache->transform().m11();
421     qreal glyphCacheScaleY = cache->transform().m22();
422     qreal glyphCacheInverseScaleX = 1.0 / glyphCacheScaleX;
423     qreal glyphCacheInverseScaleY = 1.0 / glyphCacheScaleY;
424
425     Q_ASSERT(geometry->indexType() == GL_UNSIGNED_SHORT);
426     geometry->allocate(glyphIndexes.size() * 4, glyphIndexes.size() * 6);
427     QVector4D *vp = (QVector4D *)geometry->vertexDataAsTexturedPoint2D();
428     Q_ASSERT(geometry->sizeOfVertex() == sizeof(QVector4D));
429     ushort *ip = geometry->indexDataAsUShort();
430
431     QPointF position(p.x(), p.y() - m_font.ascent());
432     bool supportsSubPixelPositions = fontD->fontEngine->supportsSubPixelPositions();
433     for (int i=0; i<glyphIndexes.size(); ++i) {
434          QFixed subPixelPosition;
435          if (supportsSubPixelPositions)
436              subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPositions.at(i).x()));
437
438          QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i), subPixelPosition);
439          const QTextureGlyphCache::Coord &c = cache->coords.value(glyph);
440
441          QPointF glyphPosition = glyphPositions.at(i) + position;
442
443          // On a retina screen the glyph positions are not pre-scaled (as opposed to
444          // eg. the raster paint engine). To ensure that we get the same behavior as
445          // the raster engine (and CoreText itself) when it comes to rounding of the
446          // coordinates, we need to apply the scale factor before rounding, and then
447          // apply the inverse scale to get back to the coordinate system of the node.
448
449          qreal x = (qFloor(glyphPosition.x() * glyphCacheScaleX) * glyphCacheInverseScaleX) +
450                         (c.baseLineX * glyphCacheInverseScaleX) - margin;
451          qreal y = (qRound(glyphPosition.y() * glyphCacheScaleY) * glyphCacheInverseScaleY) -
452                         (c.baseLineY * glyphCacheInverseScaleY) - margin;
453
454          qreal w = c.w * glyphCacheInverseScaleX;
455          qreal h = c.h * glyphCacheInverseScaleY;
456
457          *boundingRect |= QRectF(x + margin, y + margin, w, h);
458
459          float cx1 = x - margins.left();
460          float cx2 = x + w + margins.right();
461          float cy1 = y - margins.top();
462          float cy2 = y + h + margins.bottom();
463
464          float tx1 = c.x - margins.left();
465          float tx2 = c.x + c.w + margins.right();
466          float ty1 = c.y - margins.top();
467          float ty2 = c.y + c.h + margins.bottom();
468
469          if (baseLine->isNull())
470              *baseLine = glyphPosition;
471
472          vp[4 * i + 0] = QVector4D(cx1, cy1, tx1, ty1);
473          vp[4 * i + 1] = QVector4D(cx2, cy1, tx2, ty1);
474          vp[4 * i + 2] = QVector4D(cx1, cy2, tx1, ty2);
475          vp[4 * i + 3] = QVector4D(cx2, cy2, tx2, ty2);
476
477          int o = i * 4;
478          ip[6 * i + 0] = o + 0;
479          ip[6 * i + 1] = o + 2;
480          ip[6 * i + 2] = o + 3;
481          ip[6 * i + 3] = o + 3;
482          ip[6 * i + 4] = o + 1;
483          ip[6 * i + 5] = o + 0;
484     }
485 }
486
487 QSGMaterialType *QSGTextMaskMaterial::type() const
488 {
489     static QSGMaterialType rgb, gray;
490     return glyphCache()->glyphFormat() == QFontEngine::Format_A32 ? &rgb : &gray;
491 }
492
493 QOpenGLTextureGlyphCache *QSGTextMaskMaterial::glyphCache() const
494 {
495     return static_cast<QOpenGLTextureGlyphCache*>(m_glyphCache.data());
496 }
497
498 QSGMaterialShader *QSGTextMaskMaterial::createShader() const
499 {
500     QFontEngine::GlyphFormat glyphFormat = glyphCache()->glyphFormat();
501     return glyphFormat == QFontEngine::Format_A32
502            ? (QSGMaterialShader *) new QSG24BitTextMaskShader(glyphFormat)
503            : (QSGMaterialShader *) new QSG8BitTextMaskShader(glyphFormat);
504 }
505
506 static inline int qsg_colorDiff(const QVector4D &a, const QVector4D &b)
507 {
508     if (a.x() != b.x())
509         return a.x() > b.x() ? 1 : -1;
510     if (a.y() != b.y())
511         return a.y() > b.y() ? 1 : -1;
512     if (a.z() != b.z())
513         return a.z() > b.z() ? 1 : -1;
514     if (a.w() != b.w())
515         return a.w() > b.w() ? 1 : -1;
516     return 0;
517 }
518
519 int QSGTextMaskMaterial::compare(const QSGMaterial *o) const
520 {
521     Q_ASSERT(o && type() == o->type());
522     const QSGTextMaskMaterial *other = static_cast<const QSGTextMaskMaterial *>(o);
523     if (m_glyphCache != other->m_glyphCache)
524         return m_glyphCache.data() < other->m_glyphCache.data() ? -1 : 1;
525     return qsg_colorDiff(m_color, other->m_color);
526 }
527
528 bool QSGTextMaskMaterial::ensureUpToDate()
529 {
530     QSize glyphCacheSize(glyphCache()->width(), glyphCache()->height());
531     if (glyphCacheSize != m_size) {
532         if (m_texture)
533             delete m_texture;
534         m_texture = new QSGPlainTexture();
535         m_texture->setTextureId(glyphCache()->texture());
536         m_texture->setTextureSize(QSize(glyphCache()->width(), glyphCache()->height()));
537         m_texture->setOwnsTexture(false);
538
539         m_size = glyphCacheSize;
540
541         return true;
542     } else {
543         return false;
544     }
545 }
546
547 int QSGTextMaskMaterial::cacheTextureWidth() const
548 {
549     return glyphCache()->width();
550 }
551
552 int QSGTextMaskMaterial::cacheTextureHeight() const
553 {
554     return glyphCache()->height();
555 }
556
557
558 QSGStyledTextMaterial::QSGStyledTextMaterial(const QRawFont &font)
559     : QSGTextMaskMaterial(font, QFontEngine::Format_A8)
560 {
561 }
562
563 QSGMaterialType *QSGStyledTextMaterial::type() const
564 {
565     static QSGMaterialType type;
566     return &type;
567 }
568
569 QSGMaterialShader *QSGStyledTextMaterial::createShader() const
570 {
571     return new QSGStyledTextShader(glyphCache()->glyphFormat());
572 }
573
574 int QSGStyledTextMaterial::compare(const QSGMaterial *o) const
575 {
576     const QSGStyledTextMaterial *other = static_cast<const QSGStyledTextMaterial *>(o);
577
578     if (m_styleShift != other->m_styleShift)
579         return m_styleShift.y() - other->m_styleShift.y();
580
581     int diff = qsg_colorDiff(m_styleColor, other->m_styleColor);
582     if (diff == 0)
583         return QSGTextMaskMaterial::compare(o);
584     return diff;
585 }
586
587
588 QSGOutlinedTextMaterial::QSGOutlinedTextMaterial(const QRawFont &font)
589     : QSGStyledTextMaterial(font)
590 {
591 }
592
593 QSGMaterialType *QSGOutlinedTextMaterial::type() const
594 {
595     static QSGMaterialType type;
596     return &type;
597 }
598
599 QSGMaterialShader *QSGOutlinedTextMaterial::createShader() const
600 {
601     return new QSGOutlinedTextShader(glyphCache()->glyphFormat());
602 }
603
604 QT_END_NAMESPACE