Change copyrights from Nokia to Digia
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / qsgdefaultglyphnode_p.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml 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
44 #include <qopenglshaderprogram.h>
45
46 #include <QtGui/private/qopengltextureglyphcache_p.h>
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 class QSGTextMaskMaterialData : public QSGMaterialShader
60 {
61 public:
62     QSGTextMaskMaterialData();
63
64     virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
65     virtual char const *const *attributeNames() const;
66
67     virtual void activate();
68     virtual void deactivate();
69
70 private:
71     virtual void initialize();
72     virtual const char *vertexShader() const;
73     virtual const char *fragmentShader() const;
74
75     int m_matrix_id;
76     int m_color_id;
77     int m_textureScale_id;
78 };
79
80 const char *QSGTextMaskMaterialData::vertexShader() const {
81     return
82         "uniform highp mat4 matrix;                     \n"
83         "uniform highp vec2 textureScale;               \n"
84         "attribute highp vec4 vCoord;                   \n"
85         "attribute highp vec2 tCoord;                   \n"
86         "varying highp vec2 sampleCoord;                \n"
87         "void main() {                                  \n"
88         "     sampleCoord = tCoord * textureScale;      \n"
89         "     gl_Position = matrix * vCoord;            \n"
90         "}";
91 }
92
93 const char *QSGTextMaskMaterialData::fragmentShader() const {
94     return
95         "varying highp vec2 sampleCoord;                \n"
96         "uniform sampler2D texture;                     \n"
97         "uniform lowp vec4 color;                       \n"
98         "void main() {                                  \n"
99         "    gl_FragColor = vec4(texture2D(texture, sampleCoord).rgb, 1.0); \n"
100         "}";
101 }
102
103 char const *const *QSGTextMaskMaterialData::attributeNames() const
104 {
105     static char const *const attr[] = { "vCoord", "tCoord", 0 };
106     return attr;
107 }
108
109 QSGTextMaskMaterialData::QSGTextMaskMaterialData()
110 {
111 }
112
113 void QSGTextMaskMaterialData::initialize()
114 {
115     m_matrix_id = program()->uniformLocation("matrix");
116     m_color_id = program()->uniformLocation("color");
117     m_textureScale_id = program()->uniformLocation("textureScale");
118 }
119
120 static inline qreal fontSmoothingGamma()
121 {
122     static qreal fontSmoothingGamma = QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FontSmoothingGamma).toReal();
123     return fontSmoothingGamma;
124 }
125
126 void QSGTextMaskMaterialData::activate()
127 {
128     QSGMaterialShader::activate();
129     glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR);
130
131 #if !defined(QT_OPENGL_ES_2) && defined(GL_ARB_framebuffer_sRGB)
132     // 0.25 was found to be acceptable error margin by experimentation. On Mac, the gamma is 2.0,
133     // but using sRGB looks okay.
134     if (qAbs(fontSmoothingGamma() - 2.2) < 0.25)
135         glEnable(GL_FRAMEBUFFER_SRGB);
136 #endif
137 }
138
139 void QSGTextMaskMaterialData::deactivate()
140 {
141     QSGMaterialShader::deactivate();
142     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
143
144 #if !defined(QT_OPENGL_ES_2) && defined(GL_ARB_framebuffer_sRGB)
145     if (qAbs(fontSmoothingGamma() - 2.2) < 0.25)
146         glDisable(GL_FRAMEBUFFER_SRGB);
147 #endif
148 }
149
150 void QSGTextMaskMaterialData::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
151 {
152     Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type());
153     QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect);
154     QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect);
155
156     if (oldMaterial == 0 || material->color() != oldMaterial->color() || state.isOpacityDirty()) {
157         QColor c = material->color();
158         QVector4D color(c.redF(), c.greenF(), c.blueF(), c.alphaF());
159         color *= state.opacity();
160         program()->setUniformValue(m_color_id, color);
161
162         if (oldMaterial == 0 || material->color() != oldMaterial->color()) {
163             state.context()->functions()->glBlendColor(c.redF(),
164                                                        c.greenF(),
165                                                        c.blueF(),
166                                                        1.0f);
167         }
168     }
169
170     bool updated = material->ensureUpToDate();
171     Q_ASSERT(material->texture());
172
173     Q_ASSERT(oldMaterial == 0 || oldMaterial->texture());
174     if (updated
175             || oldMaterial == 0
176             || oldMaterial->texture()->textureId() != material->texture()->textureId()) {
177         program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->cacheTextureWidth(),
178                                                                1.0 / material->cacheTextureHeight()));
179         glBindTexture(GL_TEXTURE_2D, material->texture()->textureId());
180
181         // Set the mag/min filters to be linear. We only need to do this when the texture
182         // has been recreated.
183         if (updated) {
184             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
185             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
186         }
187     }
188
189     if (state.isMatrixDirty())
190         program()->setUniformValue(m_matrix_id, state.combinedMatrix());
191 }
192
193 QSGTextMaskMaterial::QSGTextMaskMaterial(const QRawFont &font)
194     : m_texture(0), m_glyphCache(), m_font(font)
195 {
196     init();
197 }
198
199 QSGTextMaskMaterial::~QSGTextMaskMaterial()
200 {
201 }
202
203 void QSGTextMaskMaterial::init()
204 {
205     Q_ASSERT(m_font.isValid());
206
207     QFontEngineGlyphCache::Type type = QFontEngineGlyphCache::Raster_RGBMask;
208     setFlag(Blending, true);
209
210     QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
211     Q_ASSERT(ctx != 0);
212
213     QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
214     if (fontD->fontEngine != 0) {
215         m_glyphCache = fontD->fontEngine->glyphCache(ctx, type, QTransform());
216         if (!m_glyphCache || m_glyphCache->cacheType() != type) {
217             m_glyphCache = new QOpenGLTextureGlyphCache(type, QTransform());
218             fontD->fontEngine->setGlyphCache(ctx, m_glyphCache.data());
219         }
220     }
221 }
222
223 void QSGTextMaskMaterial::populate(const QPointF &p,
224                                 const QVector<quint32> &glyphIndexes,
225                                 const QVector<QPointF> &glyphPositions,
226                                 QSGGeometry *geometry,
227                                 QRectF *boundingRect,
228                                 QPointF *baseLine)
229 {
230     Q_ASSERT(m_font.isValid());
231     QVector<QFixedPoint> fixedPointPositions;
232     for (int i=0; i<glyphPositions.size(); ++i)
233         fixedPointPositions.append(QFixedPoint::fromPointF(glyphPositions.at(i)));
234
235     QTextureGlyphCache *cache = glyphCache();
236
237     QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
238     cache->populate(fontD->fontEngine, glyphIndexes.size(), glyphIndexes.constData(),
239                     fixedPointPositions.data());
240     cache->fillInPendingGlyphs();
241
242     int margin = fontD->fontEngine->glyphMargin(cache->cacheType());
243
244     Q_ASSERT(geometry->indexType() == GL_UNSIGNED_SHORT);
245     geometry->allocate(glyphIndexes.size() * 4, glyphIndexes.size() * 6);
246     QVector4D *vp = (QVector4D *)geometry->vertexDataAsTexturedPoint2D();
247     Q_ASSERT(geometry->sizeOfVertex() == sizeof(QVector4D));
248     ushort *ip = geometry->indexDataAsUShort();
249
250     QPointF position(p.x(), p.y() - m_font.ascent());
251     bool supportsSubPixelPositions = fontD->fontEngine->supportsSubPixelPositions();
252     for (int i=0; i<glyphIndexes.size(); ++i) {
253          QFixed subPixelPosition;
254          if (supportsSubPixelPositions)
255              subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPositions.at(i).x()));
256
257          QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i), subPixelPosition);
258          const QTextureGlyphCache::Coord &c = cache->coords.value(glyph);
259
260          QPointF glyphPosition = glyphPositions.at(i) + position;
261          int x = qFloor(glyphPosition.x()) + c.baseLineX - margin;
262          int y = qFloor(glyphPosition.y()) - c.baseLineY - margin;
263
264          *boundingRect |= QRectF(x + margin, y + margin, c.w, c.h);
265
266          float cx1 = x;
267          float cx2 = x + c.w;
268          float cy1 = y;
269          float cy2 = y + c.h;
270
271          float tx1 = c.x;
272          float tx2 = (c.x + c.w);
273          float ty1 = c.y;
274          float ty2 = (c.y + c.h);
275
276          if (baseLine->isNull())
277              *baseLine = glyphPosition;
278
279          vp[4 * i + 0] = QVector4D(cx1, cy1, tx1, ty1);
280          vp[4 * i + 1] = QVector4D(cx2, cy1, tx2, ty1);
281          vp[4 * i + 2] = QVector4D(cx1, cy2, tx1, ty2);
282          vp[4 * i + 3] = QVector4D(cx2, cy2, tx2, ty2);
283
284          int o = i * 4;
285          ip[6 * i + 0] = o + 0;
286          ip[6 * i + 1] = o + 2;
287          ip[6 * i + 2] = o + 3;
288          ip[6 * i + 3] = o + 3;
289          ip[6 * i + 4] = o + 1;
290          ip[6 * i + 5] = o + 0;
291     }
292 }
293
294 QSGMaterialType *QSGTextMaskMaterial::type() const
295 {
296     static QSGMaterialType type;
297     return &type;
298 }
299
300 QOpenGLTextureGlyphCache *QSGTextMaskMaterial::glyphCache() const
301 {
302     return static_cast<QOpenGLTextureGlyphCache*>(m_glyphCache.data());
303 }
304
305 QSGMaterialShader *QSGTextMaskMaterial::createShader() const
306 {
307     return new QSGTextMaskMaterialData;
308 }
309
310 int QSGTextMaskMaterial::compare(const QSGMaterial *o) const
311 {
312     Q_ASSERT(o && type() == o->type());
313     const QSGTextMaskMaterial *other = static_cast<const QSGTextMaskMaterial *>(o);
314     if (m_glyphCache != other->m_glyphCache)
315         return m_glyphCache - other->m_glyphCache;
316     QRgb c1 = m_color.rgba();
317     QRgb c2 = other->m_color.rgba();
318     return int(c2 < c1) - int(c1 < c2);
319 }
320
321 bool QSGTextMaskMaterial::ensureUpToDate()
322 {
323     QSize glyphCacheSize(glyphCache()->width(), glyphCache()->height());
324     if (glyphCacheSize != m_size) {
325         if (m_texture)
326             delete m_texture;
327         m_texture = new QSGPlainTexture();
328         m_texture->setTextureId(glyphCache()->texture());
329         m_texture->setTextureSize(QSize(glyphCache()->width(), glyphCache()->height()));
330         m_texture->setOwnsTexture(false);
331
332         m_size = glyphCacheSize;
333
334         return true;
335     } else {
336         return false;
337     }
338 }
339
340 int QSGTextMaskMaterial::cacheTextureWidth() const
341 {
342     return glyphCache()->width();
343 }
344
345 int QSGTextMaskMaterial::cacheTextureHeight() const
346 {
347     return glyphCache()->height();
348 }
349
350 QT_END_NAMESPACE