aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/qsgdistancefieldglyphnode.cpp')
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode.cpp316
1 files changed, 316 insertions, 0 deletions
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
new file mode 100644
index 0000000000..d3b90bed60
--- /dev/null
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
@@ -0,0 +1,316 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgdistancefieldglyphnode_p.h"
+#include "qsgdistancefieldglyphnode_p_p.h"
+#include <QtQuick/private/qsgdistancefieldutil_p.h>
+#include <QtQuick/private/qsgcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGDistanceFieldGlyphCacheManager *cacheManager)
+ : m_material(0)
+ , m_glyph_cacheManager(cacheManager)
+ , m_glyph_cache(0)
+ , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
+ , m_style(QQuickText::Normal)
+ , m_antialiasingMode(GrayAntialiasing)
+ , m_dirtyGeometry(false)
+ , m_dirtyMaterial(false)
+{
+ m_geometry.setDrawingMode(GL_TRIANGLES);
+ setGeometry(&m_geometry);
+ setPreferredAntialiasingMode(cacheManager->defaultAntialiasingMode());
+#ifdef QML_RUNTIME_TESTING
+ description = QLatin1String("glyphs");
+#endif
+}
+
+QSGDistanceFieldGlyphNode::~QSGDistanceFieldGlyphNode()
+{
+ delete m_material;
+ if (m_glyph_cache) {
+ m_glyph_cache->release(m_glyphs.glyphIndexes());
+ m_glyph_cache->unregisterGlyphNode(this);
+ }
+}
+
+void QSGDistanceFieldGlyphNode::setColor(const QColor &color)
+{
+ m_color = color;
+ if (m_material != 0) {
+ m_material->setColor(color);
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGDistanceFieldGlyphNode::setPreferredAntialiasingMode(AntialiasingMode mode)
+{
+ if (mode == m_antialiasingMode)
+ return;
+ m_antialiasingMode = mode;
+ m_dirtyMaterial = true;
+}
+
+void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
+{
+ QRawFont font = glyphs.rawFont();
+ m_position = QPointF(position.x(), position.y() - font.ascent());
+ m_glyphs = glyphs;
+
+ QSGDistanceFieldGlyphCache *oldCache = m_glyph_cache;
+ m_glyph_cache = m_glyph_cacheManager->cache(m_glyphs.rawFont());
+ if (m_glyph_cache != oldCache) {
+ if (oldCache)
+ oldCache->unregisterGlyphNode(this);
+ m_glyph_cache->registerGlyphNode(this);
+ }
+ m_glyph_cache->populate(glyphs.glyphIndexes());
+
+ const QVector<quint32> &glyphIndexes = m_glyphs.glyphIndexes();
+ const QVector<QPointF> &glyphPositions = m_glyphs.positions();
+ for (int i = 0; i < glyphIndexes.size(); ++i) {
+ GlyphInfo g;
+ g.glyphIndex = glyphIndexes.at(i);
+ g.position = glyphPositions.at(i);
+ m_glyphsToAdd.append(g);
+ }
+
+ m_dirtyGeometry = true;
+ m_dirtyMaterial = true;
+}
+
+void QSGDistanceFieldGlyphNode::setStyle(QQuickText::TextStyle style)
+{
+ if (m_style == style)
+ return;
+ m_style = style;
+ m_dirtyMaterial = true;
+}
+
+void QSGDistanceFieldGlyphNode::setStyleColor(const QColor &color)
+{
+ if (m_styleColor == color)
+ return;
+ m_styleColor = color;
+ m_dirtyMaterial = true;
+}
+
+void QSGDistanceFieldGlyphNode::update()
+{
+ if (m_dirtyMaterial)
+ updateMaterial();
+ if (m_dirtyGeometry)
+ updateGeometry();
+}
+
+void QSGDistanceFieldGlyphNode::updateGeometry()
+{
+ Q_ASSERT(m_glyph_cache);
+
+ if (m_glyphsToAdd.isEmpty())
+ return;
+
+ QSGGeometry *g = geometry();
+
+ Q_ASSERT(g->indexType() == GL_UNSIGNED_SHORT);
+
+ int oldVertexCount = g->vertexCount();
+ int oldIndexCount = g->indexCount();
+
+ QVector<QSGGeometry::TexturedPoint2D> vp;
+ vp.reserve(m_glyphsToAdd.size() * 4);
+ QVector<ushort> ip;
+ ip.reserve(m_glyphsToAdd.size() * 6);
+
+ QPointF margins(2, 2);
+ QPointF texMargins = margins / m_glyph_cache->fontScale();
+
+ const QSGDistanceFieldGlyphCache::Texture *textureToUse = 0;
+
+ QLinkedList<GlyphInfo>::iterator it = m_glyphsToAdd.begin();
+ while (it != m_glyphsToAdd.end()) {
+ quint32 glyphIndex = it->glyphIndex;
+ QSGDistanceFieldGlyphCache::TexCoord c = m_glyph_cache->glyphTexCoord(glyphIndex);
+
+ if (c.isNull()) {
+ if (!c.isValid())
+ ++it;
+ else
+ it = m_glyphsToAdd.erase(it);
+ continue;
+ }
+
+ const QSGDistanceFieldGlyphCache::Texture *texture = m_glyph_cache->glyphTexture(glyphIndex);
+ if (!texture->textureId) {
+ ++it;
+ continue;
+ }
+
+ QSGDistanceFieldGlyphCache::Metrics metrics = m_glyph_cache->glyphMetrics(glyphIndex);
+
+ if (!textureToUse)
+ textureToUse = texture;
+
+ metrics.width += margins.x() * 2;
+ metrics.height += margins.y() * 2;
+ metrics.baselineX -= margins.x();
+ metrics.baselineY += margins.y();
+ c.xMargin -= texMargins.x();
+ c.yMargin -= texMargins.y();
+ c.width += texMargins.x() * 2;
+ c.height += texMargins.y() * 2;
+
+ const QPointF &glyphPosition = it->position;
+ qreal x = glyphPosition.x() + metrics.baselineX + m_position.x();
+ qreal y = glyphPosition.y() - metrics.baselineY + m_position.y();
+
+ m_boundingRect |= QRectF(x, y, metrics.width, metrics.height);
+
+ float cx1 = x;
+ float cx2 = x + metrics.width;
+ float cy1 = y;
+ float cy2 = y + metrics.height;
+
+ float tx1 = c.x + c.xMargin;
+ float tx2 = tx1 + c.width;
+ float ty1 = c.y + c.yMargin;
+ float ty2 = ty1 + c.height;
+
+ if (m_baseLine.isNull())
+ m_baseLine = glyphPosition;
+
+ int i = vp.size();
+
+ QSGGeometry::TexturedPoint2D v1;
+ v1.set(cx1, cy1, tx1, ty1);
+ QSGGeometry::TexturedPoint2D v2;
+ v2.set(cx2, cy1, tx2, ty1);
+ QSGGeometry::TexturedPoint2D v3;
+ v3.set(cx1, cy2, tx1, ty2);
+ QSGGeometry::TexturedPoint2D v4;
+ v4.set(cx2, cy2, tx2, ty2);
+ vp.append(v1);
+ vp.append(v2);
+ vp.append(v3);
+ vp.append(v4);
+
+ int o = i + oldVertexCount;
+ ip.append(o + 0);
+ ip.append(o + 2);
+ ip.append(o + 3);
+ ip.append(o + 3);
+ ip.append(o + 1);
+ ip.append(o + 0);
+
+ it = m_glyphsToAdd.erase(it);
+ }
+
+ if (vp.isEmpty())
+ return;
+
+ void *data = 0;
+ if (oldVertexCount && oldIndexCount) {
+ int byteSize = oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D)
+ + oldIndexCount * sizeof(quint16);
+ data = qMalloc(byteSize);
+ memcpy(data, g->vertexData(), byteSize);
+ }
+
+ g->allocate(oldVertexCount + vp.size(), oldIndexCount + ip.size());
+
+ if (data) {
+ memcpy(g->vertexData(), data, oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D));
+ memcpy(g->indexData(), ((char *) data) + oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D),
+ oldIndexCount * sizeof(quint16));
+ qFree(data);
+ }
+
+ memcpy(g->vertexDataAsTexturedPoint2D() + oldVertexCount, vp.constData(), vp.size() * sizeof(QSGGeometry::TexturedPoint2D));
+ memcpy(g->indexDataAsUShort() + oldIndexCount, ip.constData(), ip.size() * sizeof(quint16));
+
+ setBoundingRect(m_boundingRect);
+ markDirty(DirtyGeometry);
+ m_dirtyGeometry = false;
+
+ m_material->setTexture(textureToUse);
+}
+
+void QSGDistanceFieldGlyphNode::updateMaterial()
+{
+ delete m_material;
+
+ if (m_style == QQuickText::Normal) {
+ switch (m_antialiasingMode) {
+ case HighQualitySubPixelAntialiasing:
+ m_material = new QSGHiQSubPixelDistanceFieldTextMaterial;
+ break;
+ case LowQualitySubPixelAntialiasing:
+ m_material = new QSGLoQSubPixelDistanceFieldTextMaterial;
+ break;
+ case GrayAntialiasing:
+ default:
+ m_material = new QSGDistanceFieldTextMaterial;
+ break;
+ }
+ } else {
+ QSGDistanceFieldStyledTextMaterial *material;
+ if (m_style == QQuickText::Outline) {
+ material = new QSGDistanceFieldOutlineTextMaterial;
+ } else {
+ QSGDistanceFieldShiftedStyleTextMaterial *sMaterial = new QSGDistanceFieldShiftedStyleTextMaterial;
+ if (m_style == QQuickText::Raised)
+ sMaterial->setShift(QPointF(0.0, 1.0));
+ else
+ sMaterial->setShift(QPointF(0.0, -1.0));
+ material = sMaterial;
+ }
+ material->setStyleColor(m_styleColor);
+ m_material = material;
+ }
+
+ m_material->setGlyphCache(m_glyph_cache);
+ m_material->setColor(m_color);
+ setMaterial(m_material);
+ m_dirtyMaterial = false;
+}
+
+QT_END_NAMESPACE