#include "qquicktext_p.h"
#include "qquicktext_p_p.h"
-#include <private/qsgdistancefieldglyphcache_p.h>
#include <private/qsgcontext_p.h>
#include <private/qsgadaptationlayer_p.h>
#include "qquicktextnode_p.h"
#include <private/qdeclarativeglobal_p.h>
#include <private/qtextcontrol_p.h>
#include <private/qtextengine_p.h>
-#include <private/qsgdistancefieldglyphcache_p.h>
#include <private/qsgtexture_p.h>
#include <private/qsgadaptationlayer_p.h>
#include "qquickcanvas.h"
#include <private/qdeclarativeglobal_p.h>
-#include <private/qsgdistancefieldglyphcache_p.h>
#include <QtDeclarative/qdeclarativeinfo.h>
#include <QtGui/qevent.h>
#include "qquicktextnode_p.h"
#include "qsgsimplerectnode.h"
#include <private/qsgadaptationlayer_p.h>
-#include <private/qsgdistancefieldglyphcache_p.h>
#include <private/qsgdistancefieldglyphnode_p.h>
#include <private/qsgcontext_p.h>
****************************************************************************/
#include "qsgadaptationlayer_p.h"
+
+#include <qmath.h>
+#include <private/qsgdistancefieldutil_p.h>
+#include <private/qsgdistancefieldglyphnode_p.h>
+#include <private/qrawfont_p.h>
+#include <QtGui/qguiapplication.h>
+#include <qdir.h>
+
+QT_BEGIN_NAMESPACE
+
+
+QHash<QString, QOpenGLMultiGroupSharedResource> QSGDistanceFieldGlyphCache::m_caches_data;
+
+QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
+ : ctx(c)
+ , m_manager(man)
+{
+ Q_ASSERT(font.isValid());
+ m_font = font;
+
+ m_cacheData = cacheData();
+
+ QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
+ m_glyphCount = fontD->fontEngine->glyphCount();
+
+ m_cacheData->doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT;
+
+ m_referenceFont = m_font;
+ m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(m_cacheData->doubleGlyphResolution));
+ Q_ASSERT(m_referenceFont.isValid());
+}
+
+QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
+{
+}
+
+QSGDistanceFieldGlyphCache::GlyphCacheData *QSGDistanceFieldGlyphCache::cacheData()
+{
+ QString key = QString::fromLatin1("%1_%2_%3_%4")
+ .arg(m_font.familyName())
+ .arg(m_font.styleName())
+ .arg(m_font.weight())
+ .arg(m_font.style());
+ return m_caches_data[key].value<QSGDistanceFieldGlyphCache::GlyphCacheData>(ctx);
+}
+
+qreal QSGDistanceFieldGlyphCache::fontScale() const
+{
+ return qreal(m_font.pixelSize()) / QT_DISTANCEFIELD_BASEFONTSIZE(m_cacheData->doubleGlyphResolution);
+}
+
+int QSGDistanceFieldGlyphCache::distanceFieldRadius() const
+{
+ return QT_DISTANCEFIELD_DEFAULT_RADIUS / QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution);
+}
+
+QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph)
+{
+ QHash<glyph_t, Metrics>::iterator metric = m_metrics.find(glyph);
+ if (metric == m_metrics.end()) {
+ QPainterPath path = m_font.pathForGlyph(glyph);
+ QRectF br = path.boundingRect();
+
+ Metrics m;
+ m.width = br.width();
+ m.height = br.height();
+ m.baselineX = br.x();
+ m.baselineY = -br.y();
+
+ metric = m_metrics.insert(glyph, m);
+ }
+
+ return metric.value();
+}
+
+QSGDistanceFieldGlyphCache::TexCoord QSGDistanceFieldGlyphCache::glyphTexCoord(glyph_t glyph) const
+{
+ return m_cacheData->texCoords.value(glyph);
+}
+
+static QSGDistanceFieldGlyphCache::Texture g_emptyTexture;
+
+const QSGDistanceFieldGlyphCache::Texture *QSGDistanceFieldGlyphCache::glyphTexture(glyph_t glyph) const
+{
+ QHash<glyph_t, Texture*>::const_iterator it = m_cacheData->glyphTextures.find(glyph);
+ if (it == m_cacheData->glyphTextures.constEnd())
+ return &g_emptyTexture;
+ return it.value();
+}
+
+void QSGDistanceFieldGlyphCache::populate(const QVector<glyph_t> &glyphs)
+{
+ QSet<glyph_t> newGlyphs;
+ int count = glyphs.count();
+ for (int i = 0; i < count; ++i) {
+ glyph_t glyphIndex = glyphs.at(i);
+ if ((int) glyphIndex >= glyphCount()) {
+ qWarning("Warning: distance-field glyph is not available with index %d", glyphIndex);
+ continue;
+ }
+
+ if (m_cacheData->texCoords.contains(glyphIndex) || newGlyphs.contains(glyphIndex))
+ continue;
+
+ QPainterPath path = m_referenceFont.pathForGlyph(glyphIndex);
+ m_cacheData->glyphPaths.insert(glyphIndex, path);
+ if (path.isEmpty()) {
+ TexCoord c;
+ c.width = 0;
+ c.height = 0;
+ m_cacheData->texCoords.insert(glyphIndex, c);
+ continue;
+ }
+
+ newGlyphs.insert(glyphIndex);
+ }
+
+ if (newGlyphs.isEmpty())
+ return;
+
+ QVector<glyph_t> glyphsVec;
+ QSet<glyph_t>::const_iterator it = newGlyphs.constBegin();
+ while (it != newGlyphs.constEnd()) {
+ glyphsVec.append(*it);
+ ++it;
+ }
+ requestGlyphs(glyphsVec);
+}
+
+void QSGDistanceFieldGlyphCache::release(const QVector<glyph_t> &glyphs)
+{
+ releaseGlyphs(glyphs);
+}
+
+void QSGDistanceFieldGlyphCache::update()
+{
+ if (m_cacheData->pendingGlyphs.isEmpty())
+ return;
+
+ QHash<glyph_t, QImage> distanceFields;
+
+ // ### Remove before final release
+ static bool cacheDistanceFields = QGuiApplication::arguments().contains(QLatin1String("--cache-distance-fields"));
+
+ QString tmpPath = QString::fromLatin1("%1/.qt/").arg(QDir::tempPath());
+ QString keyBase = QString::fromLatin1("%1%2%3_%4_%5_%6.fontblob")
+ .arg(tmpPath)
+ .arg(m_font.familyName())
+ .arg(m_font.styleName())
+ .arg(m_font.weight())
+ .arg(m_font.style());
+
+ if (cacheDistanceFields && !QFile::exists(tmpPath))
+ QDir(tmpPath).mkpath(tmpPath);
+
+ for (int i = 0; i < m_cacheData->pendingGlyphs.size(); ++i) {
+ glyph_t glyphIndex = m_cacheData->pendingGlyphs.at(i);
+
+ if (cacheDistanceFields) {
+ QString key = keyBase.arg(glyphIndex);
+ QFile file(key);
+ if (file.open(QFile::ReadOnly)) {
+ int fileSize = file.size();
+ int dim = sqrt(float(fileSize));
+ QByteArray blob = file.readAll();
+ QImage df(dim, dim, QImage::Format_Indexed8);
+ memcpy(df.bits(), blob.constData(), fileSize);
+ distanceFields.insert(glyphIndex, df);
+ continue;
+ }
+ }
+
+ QImage distanceField = qt_renderDistanceFieldGlyph(m_font, glyphIndex, m_cacheData->doubleGlyphResolution);
+ distanceFields.insert(glyphIndex, distanceField);
+
+ if (cacheDistanceFields) {
+ QString key = keyBase.arg(glyphIndex);
+ QFile file(key);
+ file.open(QFile::WriteOnly);
+ file.write((const char *) distanceField.constBits(), distanceField.width() * distanceField.height());
+ }
+ }
+
+ m_cacheData->pendingGlyphs.reset();
+
+ storeGlyphs(distanceFields);
+}
+
+void QSGDistanceFieldGlyphCache::addGlyphPositions(const QList<GlyphPosition> &glyphs)
+{
+ int count = glyphs.count();
+ for (int i = 0; i < count; ++i) {
+ GlyphPosition glyph = glyphs.at(i);
+
+ QPainterPath path = m_cacheData->glyphPaths.value(glyph.glyph);
+ QRectF br = path.boundingRect();
+ TexCoord c;
+ c.xMargin = QT_DISTANCEFIELD_RADIUS(m_cacheData->doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution));
+ c.yMargin = QT_DISTANCEFIELD_RADIUS(m_cacheData->doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution));
+ c.x = glyph.position.x();
+ c.y = glyph.position.y();
+ c.width = br.width();
+ c.height = br.height();
+
+ m_cacheData->texCoords.insert(glyph.glyph, c);
+ }
+}
+
+void QSGDistanceFieldGlyphCache::addGlyphTextures(const QVector<glyph_t> &glyphs, const Texture &tex)
+{
+ int i = m_cacheData->textures.indexOf(tex);
+ if (i == -1) {
+ m_cacheData->textures.append(tex);
+ i = m_cacheData->textures.size() - 1;
+ } else {
+ m_cacheData->textures[i].size = tex.size;
+ }
+ Texture *texture = &(m_cacheData->textures[i]);
+
+ int count = glyphs.count();
+ for (int j = 0; j < count; ++j)
+ m_cacheData->glyphTextures.insert(glyphs.at(j), texture);
+
+ QLinkedList<QSGDistanceFieldGlyphNode *>::iterator it = m_cacheData->m_registeredNodes.begin();
+ while (it != m_cacheData->m_registeredNodes.end()) {
+ (*it)->updateGeometry();
+ ++it;
+ }
+}
+
+void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QVector<glyph_t> &glyphs)
+{
+ int count = glyphs.count();
+ for (int i = 0; i < count; ++i)
+ m_cacheData->pendingGlyphs.add(glyphs.at(i));
+}
+
+void QSGDistanceFieldGlyphCache::removeGlyph(glyph_t glyph)
+{
+ m_cacheData->texCoords.remove(glyph);
+ m_cacheData->glyphTextures.remove(glyph);
+}
+
+void QSGDistanceFieldGlyphCache::updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize)
+{
+ int count = m_cacheData->textures.count();
+ for (int i = 0; i < count; ++i) {
+ Texture &tex = m_cacheData->textures[i];
+ if (tex.textureId == oldTex) {
+ tex.textureId = newTex;
+ tex.size = newTexSize;
+ return;
+ }
+ }
+}
+
+bool QSGDistanceFieldGlyphCache::containsGlyph(glyph_t glyph) const
+{
+ return m_cacheData->texCoords.contains(glyph);
+}
+
+void QSGDistanceFieldGlyphCache::registerGlyphNode(QSGDistanceFieldGlyphNode *node)
+{
+ m_cacheData->m_registeredNodes.append(node);
+}
+
+void QSGDistanceFieldGlyphCache::unregisterGlyphNode(QSGDistanceFieldGlyphNode *node)
+{
+ m_cacheData->m_registeredNodes.removeOne(node);
+}
+
+
+QT_END_NAMESPACE
#include <QtCore/qsharedpointer.h>
#include <QtGui/qglyphrun.h>
#include <QtCore/qurl.h>
+#include <private/qfontengine_p.h>
+#include <QtGui/private/qdatabuffer_p.h>
+#include <private/qopenglcontext_p.h>
// ### remove
#include <private/qquicktext_p.h>
class QSGNode;
class QImage;
class TextureReference;
+class QSGDistanceFieldGlyphCacheManager;
+class QSGDistanceFieldGlyphNode;
// TODO: Rename from XInterface to AbstractX.
class Q_DECLARATIVE_EXPORT QSGRectangleNode : public QSGGeometryNode
QRectF m_bounding_rect;
};
+class Q_DECLARATIVE_EXPORT QSGDistanceFieldGlyphCache
+{
+public:
+ QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font);
+ virtual ~QSGDistanceFieldGlyphCache();
+
+ struct Metrics {
+ qreal width;
+ qreal height;
+ qreal baselineX;
+ qreal baselineY;
+
+ bool isNull() const { return width == 0 || height == 0; }
+ };
+
+ struct TexCoord {
+ qreal x;
+ qreal y;
+ qreal width;
+ qreal height;
+ qreal xMargin;
+ qreal yMargin;
+
+ TexCoord() : x(0), y(0), width(-1), height(-1), xMargin(0), yMargin(0) { }
+
+ bool isNull() const { return width <= 0 || height <= 0; }
+ bool isValid() const { return width >= 0 && height >= 0; }
+ };
+
+ struct Texture {
+ GLuint textureId;
+ QSize size;
+
+ Texture() : textureId(0), size(QSize()) { }
+ bool operator == (const Texture &other) const { return textureId == other.textureId; }
+ };
+
+ const QSGDistanceFieldGlyphCacheManager *manager() const { return m_manager; }
+
+ const QRawFont &font() const { return m_font; }
+
+ qreal fontScale() const;
+ int distanceFieldRadius() const;
+ int glyphCount() const { return m_glyphCount; }
+ bool doubleGlyphResolution() const { return m_cacheData->doubleGlyphResolution; }
+
+ Metrics glyphMetrics(glyph_t glyph);
+ TexCoord glyphTexCoord(glyph_t glyph) const;
+ const Texture *glyphTexture(glyph_t glyph) const;
+
+ void populate(const QVector<glyph_t> &glyphs);
+ void release(const QVector<glyph_t> &glyphs);
+
+ void update();
+
+ void registerGlyphNode(QSGDistanceFieldGlyphNode *node);
+ void unregisterGlyphNode(QSGDistanceFieldGlyphNode *node);
+
+protected:
+ struct GlyphPosition {
+ glyph_t glyph;
+ QPointF position;
+ };
+
+ virtual void requestGlyphs(const QVector<glyph_t> &glyphs) = 0;
+ virtual void storeGlyphs(const QHash<glyph_t, QImage> &glyphs) = 0;
+ virtual void releaseGlyphs(const QVector<glyph_t> &glyphs) = 0;
+
+ void addGlyphPositions(const QList<GlyphPosition> &glyphs);
+ void addGlyphTextures(const QVector<glyph_t> &glyphs, const Texture &tex);
+ void markGlyphsToRender(const QVector<glyph_t> &glyphs);
+ void removeGlyph(glyph_t glyph);
+
+ void updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize);
+
+ bool containsGlyph(glyph_t glyph) const;
+
+ QOpenGLContext *ctx;
+
+private:
+ struct GlyphCacheData : public QOpenGLSharedResource {
+ QList<Texture> textures;
+ QHash<glyph_t, Texture*> glyphTextures;
+ QHash<glyph_t, TexCoord> texCoords;
+ QDataBuffer<glyph_t> pendingGlyphs;
+ QHash<glyph_t, QPainterPath> glyphPaths;
+ bool doubleGlyphResolution;
+ QLinkedList<QSGDistanceFieldGlyphNode*> m_registeredNodes;
+
+ GlyphCacheData(QOpenGLContext *ctx)
+ : QOpenGLSharedResource(ctx->shareGroup())
+ , pendingGlyphs(64)
+ , doubleGlyphResolution(false)
+ {}
+
+ void invalidateResource()
+ {
+ textures.clear();
+ glyphTextures.clear();
+ texCoords.clear();
+ }
+
+ void freeResource(QOpenGLContext *)
+ {
+ }
+ };
+
+ QSGDistanceFieldGlyphCacheManager *m_manager;
+
+ QRawFont m_font;
+ QRawFont m_referenceFont;
+
+ int m_glyphCount;
+ QHash<glyph_t, Metrics> m_metrics;
+
+ GlyphCacheData *cacheData();
+ GlyphCacheData *m_cacheData;
+ static QHash<QString, QOpenGLMultiGroupSharedResource> m_caches_data;
+};
+
QT_END_NAMESPACE
#include <private/qsgdefaultrenderer_p.h>
+#include <private/qsgdistancefieldutil_p.h>
+#include <private/qsgdefaultdistancefieldglyphcache_p.h>
#include <private/qsgdefaultrectanglenode_p.h>
#include <private/qsgdefaultimagenode_p.h>
#include <private/qsgdefaultglyphnode_p.h>
#include <private/qsgdistancefieldglyphnode_p.h>
-#include <private/qsgdistancefieldglyphcache_p.h>
#include <private/qsgtexture_p.h>
#include <qsgengine.h>
}
/*!
+ Factory function for scene graph backends of the distance-field glyph cache.
+ */
+QSGDistanceFieldGlyphCache *QSGContext::createDistanceFieldGlyphCache(const QRawFont &font)
+{
+ Q_D(QSGContext);
+ return new QSGDefaultDistanceFieldGlyphCache(d->distanceFieldCacheManager, glContext(), font);
+}
+
+/*!
Factory function for scene graph backends of the Text elements;
*/
QSGGlyphNode *QSGContext::createGlyphNode()
return new QSGDefaultGlyphNode;
} else {
if (!d->distanceFieldCacheManager) {
- d->distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager(d->gl);
+ d->distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager(this);
if (doSubpixel)
d->distanceFieldCacheManager->setDefaultAntialiasingMode(QSGGlyphNode::HighQualitySubPixelAntialiasing);
else if (doLowQualSubpixel)
#include <QtGui/QImage>
#include <QtGui/QSurfaceFormat>
+#include <private/qrawfont_p.h>
+
#include "qsgnode.h"
QT_BEGIN_HEADER
class QSGImageNode;
class QSGGlyphNode;
class QSGRenderer;
+class QSGDistanceFieldGlyphCache;
class QSGTexture;
class QSGMaterial;
virtual void renderNextFrame(QOpenGLFramebufferObject *fbo = 0);
+ virtual QSGDistanceFieldGlyphCache *createDistanceFieldGlyphCache(const QRawFont &font);
+
virtual QSGRectangleNode *createRectangleNode();
virtual QSGImageNode *createImageNode();
virtual QSGGlyphNode *createGlyphNode();
--- /dev/null
+/****************************************************************************
+**
+** 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 "qsgdefaultdistancefieldglyphcache_p.h"
+
+#include <private/qsgdistancefieldutil_p.h>
+#include <qopenglshaderprogram.h>
+#include <QtGui/private/qopenglengineshadersource_p.h>
+#include <qopenglfunctions.h>
+
+
+class TextureBlitHelper
+{
+public:
+ TextureBlitHelper()
+ {
+ m_vertexCoordinateArray[0] = -1.0f;
+ m_vertexCoordinateArray[1] = -1.0f;
+ m_vertexCoordinateArray[2] = 1.0f;
+ m_vertexCoordinateArray[3] = -1.0f;
+ m_vertexCoordinateArray[4] = 1.0f;
+ m_vertexCoordinateArray[5] = 1.0f;
+ m_vertexCoordinateArray[6] = -1.0f;
+ m_vertexCoordinateArray[7] = 1.0f;
+
+ m_textureCoordinateArray[0] = 0.0f;
+ m_textureCoordinateArray[1] = 0.0f;
+ m_textureCoordinateArray[2] = 1.0f;
+ m_textureCoordinateArray[3] = 0.0f;
+ m_textureCoordinateArray[4] = 1.0f;
+ m_textureCoordinateArray[5] = 1.0f;
+ m_textureCoordinateArray[6] = 0.0f;
+ m_textureCoordinateArray[7] = 1.0f;
+
+ m_blitProgram = new QOpenGLShaderProgram;
+ {
+ QString source;
+ source.append(QLatin1String(qopenglslMainWithTexCoordsVertexShader));
+ source.append(QLatin1String(qopenglslUntransformedPositionVertexShader));
+
+ QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, m_blitProgram);
+ vertexShader->compileSourceCode(source);
+
+ m_blitProgram->addShader(vertexShader);
+ }
+ {
+ QString source;
+ source.append(QLatin1String(qopenglslMainFragmentShader));
+ source.append(QLatin1String(qopenglslImageSrcFragmentShader));
+
+ QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, m_blitProgram);
+ fragmentShader->compileSourceCode(source);
+
+ m_blitProgram->addShader(fragmentShader);
+ }
+ m_blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
+ m_blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
+ m_blitProgram->link();
+ }
+
+ ~TextureBlitHelper()
+ {
+ delete m_blitProgram;
+ }
+
+ QOpenGLShaderProgram *blitProgram() { return m_blitProgram; }
+ const GLfloat *blitVertexArray() const { return &m_vertexCoordinateArray[0]; }
+ const GLfloat *blitTextureArray() const { return &m_textureCoordinateArray[0]; }
+
+private:
+ QOpenGLShaderProgram *m_blitProgram;
+ GLfloat m_vertexCoordinateArray[8];
+ GLfloat m_textureCoordinateArray[8];
+};
+
+static TextureBlitHelper *g_textureBlitHelper = 0;
+
+QHash<QString, QOpenGLMultiGroupSharedResource> QSGDefaultDistanceFieldGlyphCache::m_textures_data;
+
+QSGDefaultDistanceFieldGlyphCache::DistanceFieldTextureData *QSGDefaultDistanceFieldGlyphCache::textureData()
+{
+ QString key = QString::fromLatin1("%1_%2_%3_%4")
+ .arg(font().familyName())
+ .arg(font().styleName())
+ .arg(font().weight())
+ .arg(font().style());
+ return m_textures_data[key].value<QSGDefaultDistanceFieldGlyphCache::DistanceFieldTextureData>(QOpenGLContext::currentContext());
+}
+
+QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
+ : QSGDistanceFieldGlyphCache(man, c, font)
+ , m_maxTextureSize(0)
+{
+ m_textureData = textureData();
+}
+
+void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QVector<glyph_t> &glyphs)
+{
+ int count = glyphs.count();
+
+ // Avoid useless and costly glyph re-generation
+ if (cacheIsFull() && !m_textureData->unusedGlyphs.isEmpty()) {
+ for (int i = 0; i < count; ++i) {
+ glyph_t glyphIndex = glyphs.at(i);
+ if (containsGlyph(glyphIndex) && m_textureData->unusedGlyphs.contains(glyphIndex))
+ m_textureData->unusedGlyphs.remove(glyphIndex);
+ }
+ }
+
+ QList<GlyphPosition> glyphPositions;
+ QVector<glyph_t> glyphsToRender;
+
+ for (int i = 0; i < count; ++i) {
+ glyph_t glyphIndex = glyphs.at(i);
+
+ if (++m_textureData->glyphRefCount[glyphIndex] == 1)
+ m_textureData->unusedGlyphs.remove(glyphIndex);
+
+ if (cacheIsFull() && m_textureData->unusedGlyphs.isEmpty())
+ continue;
+
+ GlyphPosition p;
+ p.glyph = glyphIndex;
+ p.position = QPointF(m_textureData->currX, m_textureData->currY);
+
+ if (!cacheIsFull()) {
+ m_textureData->currX += QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution());
+ if (m_textureData->currX >= maxTextureSize()) {
+ m_textureData->currX = 0;
+ m_textureData->currY += QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution());
+ }
+ } else {
+ // Recycle glyphs
+ if (!m_textureData->unusedGlyphs.isEmpty()) {
+ glyph_t unusedGlyph = *m_textureData->unusedGlyphs.constBegin();
+ TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
+ p.position = QPointF(unusedCoord.x, unusedCoord.y);
+ m_textureData->unusedGlyphs.remove(unusedGlyph);
+ removeGlyph(unusedGlyph);
+ }
+ }
+
+ if (p.position.y() < maxTextureSize()) {
+ glyphPositions.append(p);
+ glyphsToRender.append(glyphIndex);
+ }
+ }
+
+ addGlyphPositions(glyphPositions);
+ markGlyphsToRender(glyphsToRender);
+}
+
+void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
+{
+ int requiredWidth = maxTextureSize();
+ int rows = 128 / (requiredWidth / QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution())); // Enough rows to fill the latin1 set by default..
+ int requiredHeight = qMin(maxTextureSize(),
+ qMax(m_textureData->currY + QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()),
+ QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()) * rows));
+
+ resizeTexture((requiredWidth), (requiredHeight));
+ glBindTexture(GL_TEXTURE_2D, m_textureData->texture);
+
+ QVector<glyph_t> glyphTextures;
+
+ QHash<glyph_t, QImage>::const_iterator it;
+ for (it = glyphs.constBegin(); it != glyphs.constEnd(); ++it) {
+ glyph_t glyphIndex = it.key();
+ TexCoord c = glyphTexCoord(glyphIndex);
+
+ glyphTextures.append(glyphIndex);
+
+ QImage glyph = it.value();
+
+ if (useWorkaroundBrokenFBOReadback()) {
+ uchar *inBits = glyph.scanLine(0);
+ uchar *outBits = m_textureData->image.scanLine(int(c.y)) + int(c.x);
+ for (int y = 0; y < glyph.height(); ++y) {
+ qMemCopy(outBits, inBits, glyph.width());
+ inBits += glyph.bytesPerLine();
+ outBits += m_textureData->image.bytesPerLine();
+ }
+ }
+
+ glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, glyph.width(), glyph.height(), GL_ALPHA, GL_UNSIGNED_BYTE, glyph.constBits());
+ }
+
+ Texture t;
+ t.textureId = m_textureData->texture;
+ t.size = m_textureData->size;
+ addGlyphTextures(glyphTextures, t);
+}
+
+void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QVector<glyph_t> &glyphs)
+{
+ int count = glyphs.count();
+ for (int i = 0; i < count; ++i) {
+ glyph_t glyphIndex = glyphs.at(i);
+ if (--m_textureData->glyphRefCount[glyphIndex] == 0 && !glyphTexCoord(glyphIndex).isNull())
+ m_textureData->unusedGlyphs.insert(glyphIndex);
+ }
+}
+
+void QSGDefaultDistanceFieldGlyphCache::createTexture(int width, int height)
+{
+ if (useWorkaroundBrokenFBOReadback() && m_textureData->image.isNull())
+ m_textureData->image = QImage(width, height, QImage::Format_Indexed8);
+
+ while (glGetError() != GL_NO_ERROR) { }
+
+ glGenTextures(1, &m_textureData->texture);
+ glBindTexture(GL_TEXTURE_2D, m_textureData->texture);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ m_textureData->size = QSize(width, height);
+
+ GLuint error = glGetError();
+ if (error != GL_NO_ERROR) {
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDeleteTextures(1, &m_textureData->texture);
+ m_textureData->texture = 0;
+ }
+
+}
+
+void QSGDefaultDistanceFieldGlyphCache::resizeTexture(int width, int height)
+{
+ int oldWidth = m_textureData->size.width();
+ int oldHeight = m_textureData->size.height();
+ if (width == oldWidth && height == oldHeight)
+ return;
+
+ GLuint oldTexture = m_textureData->texture;
+ createTexture(width, height);
+
+ if (!oldTexture)
+ return;
+
+ updateTexture(oldTexture, m_textureData->texture, m_textureData->size);
+
+ if (useWorkaroundBrokenFBOReadback()) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, oldWidth, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, m_textureData->image.constBits());
+ m_textureData->image = m_textureData->image.copy(0, 0, width, height);
+ glDeleteTextures(1, &oldTexture);
+ return;
+ }
+
+ if (!g_textureBlitHelper)
+ g_textureBlitHelper = new TextureBlitHelper;
+
+ if (!m_textureData->fbo)
+ ctx->functions()->glGenFramebuffers(1, &m_textureData->fbo);
+ ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_textureData->fbo);
+
+ GLuint tmp_texture;
+ glGenTextures(1, &tmp_texture);
+ glBindTexture(GL_TEXTURE_2D, tmp_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ ctx->functions()->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, tmp_texture, 0);
+
+ ctx->functions()->glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, oldTexture);
+
+ // save current render states
+ GLboolean stencilTestEnabled;
+ GLboolean depthTestEnabled;
+ GLboolean scissorTestEnabled;
+ GLboolean blendEnabled;
+ GLint viewport[4];
+ GLint oldProgram;
+ glGetBooleanv(GL_STENCIL_TEST, &stencilTestEnabled);
+ glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled);
+ glGetBooleanv(GL_SCISSOR_TEST, &scissorTestEnabled);
+ glGetBooleanv(GL_BLEND, &blendEnabled);
+ glGetIntegerv(GL_VIEWPORT, &viewport[0]);
+ glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgram);
+
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_BLEND);
+
+ glViewport(0, 0, oldWidth, oldHeight);
+
+ ctx->functions()->glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, g_textureBlitHelper->blitVertexArray());
+ ctx->functions()->glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, g_textureBlitHelper->blitTextureArray());
+
+ g_textureBlitHelper->blitProgram()->bind();
+ g_textureBlitHelper->blitProgram()->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
+ g_textureBlitHelper->blitProgram()->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
+ g_textureBlitHelper->blitProgram()->disableAttributeArray(int(QT_OPACITY_ATTR));
+ g_textureBlitHelper->blitProgram()->setUniformValue("imageTexture", GLuint(0));
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glBindTexture(GL_TEXTURE_2D, m_textureData->texture);
+
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
+
+ ctx->functions()->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, 0);
+ glDeleteTextures(1, &tmp_texture);
+ glDeleteTextures(1, &oldTexture);
+
+ ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ // restore render states
+ if (stencilTestEnabled)
+ glEnable(GL_STENCIL_TEST);
+ if (depthTestEnabled)
+ glEnable(GL_DEPTH_TEST);
+ if (scissorTestEnabled)
+ glEnable(GL_SCISSOR_TEST);
+ if (blendEnabled)
+ glEnable(GL_BLEND);
+ glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
+ ctx->functions()->glUseProgram(oldProgram);
+}
+
+bool QSGDefaultDistanceFieldGlyphCache::useWorkaroundBrokenFBOReadback() const
+{
+ static bool set = false;
+ static bool useWorkaround = false;
+ if (!set) {
+ QOpenGLContextPrivate *ctx_p = static_cast<QOpenGLContextPrivate *>(QOpenGLContextPrivate::get(ctx));
+ useWorkaround = ctx_p->workaround_brokenFBOReadBack;
+ set = true;
+ }
+ return useWorkaround;
+}
+
+int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const
+{
+ if (!m_maxTextureSize)
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
+ return m_maxTextureSize;
+}
+
+QT_END_NAMESPACE
--- /dev/null
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QSGDEFAULTDISTANCEFIELDGLYPHCACHE_H
+#define QSGDEFAULTDISTANCEFIELDGLYPHCACHE_H
+
+#include <QtGui/qopenglfunctions.h>
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_DECLARATIVE_EXPORT QSGDefaultDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
+{
+public:
+ QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font);
+
+ void requestGlyphs(const QVector<glyph_t> &glyphs);
+ void storeGlyphs(const QHash<glyph_t, QImage> &glyphs);
+ void releaseGlyphs(const QVector<glyph_t> &glyphs);
+
+ bool cacheIsFull() const { return m_textureData->currY >= maxTextureSize(); }
+ bool useWorkaroundBrokenFBOReadback() const;
+ int maxTextureSize() const;
+
+private:
+ void createTexture(int width, int height);
+ void resizeTexture(int width, int height);
+
+ mutable int m_maxTextureSize;
+
+ struct DistanceFieldTextureData : public QOpenGLSharedResource {
+ GLuint texture;
+ GLuint fbo;
+ QSize size;
+ QHash<glyph_t, quint32> glyphRefCount;
+ QSet<glyph_t> unusedGlyphs;
+ int currX;
+ int currY;
+ QImage image;
+
+ DistanceFieldTextureData(QOpenGLContext *ctx)
+ : QOpenGLSharedResource(ctx->shareGroup())
+ , texture(0)
+ , fbo(0)
+ , currX(0)
+ , currY(0)
+ {}
+
+ void invalidateResource()
+ {
+ texture = 0;
+ fbo = 0;
+ size = QSize();
+ }
+
+ void freeResource(QOpenGLContext *ctx)
+ {
+ glDeleteTextures(1, &texture);
+ ctx->functions()->glDeleteFramebuffers(1, &fbo);
+ }
+ };
+
+ DistanceFieldTextureData *textureData();
+ DistanceFieldTextureData *m_textureData;
+ static QHash<QString, QOpenGLMultiGroupSharedResource> m_textures_data;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGDEFAULTDISTANCEFIELDGLYPHCACHE_H
+++ /dev/null
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef DISTANCEFIELDGLYPHCACHE_H
-#define DISTANCEFIELDGLYPHCACHE_H
-
-#include <qopengl.h>
-#include <qrawfont.h>
-#include <private/qopenglcontext_p.h>
-#include <QtGui/qopenglfunctions.h>
-#include <private/qfont_p.h>
-#include <private/qfontengine_p.h>
-#include <QtGui/private/qdatabuffer_p.h>
-#include <private/qsgadaptationlayer_p.h>
-
-QT_BEGIN_NAMESPACE
-
-typedef float (*ThresholdFunc)(float glyphScale);
-typedef float (*AntialiasingSpreadFunc)(float glyphScale);
-
-class QOpenGLShaderProgram;
-class QSGDistanceFieldGlyphCache;
-
-class Q_DECLARATIVE_EXPORT QSGDistanceFieldGlyphCacheManager
-{
-public:
- QSGDistanceFieldGlyphCacheManager(QOpenGLContext *c);
- ~QSGDistanceFieldGlyphCacheManager();
-
- QSGDistanceFieldGlyphCache *cache(const QRawFont &font);
-
- QSGGlyphNode::AntialiasingMode defaultAntialiasingMode() const { return m_defaultAntialiasingMode; }
- void setDefaultAntialiasingMode(QSGGlyphNode::AntialiasingMode mode) { m_defaultAntialiasingMode = mode; }
-
- ThresholdFunc thresholdFunc() const { return m_threshold_func; }
- void setThresholdFunc(ThresholdFunc func) { m_threshold_func = func; }
-
- AntialiasingSpreadFunc antialiasingSpreadFunc() const { return m_antialiasingSpread_func; }
- void setAntialiasingSpreadFunc(AntialiasingSpreadFunc func) { m_antialiasingSpread_func = func; }
-
- QOpenGLShaderProgram *blitProgram() { return m_blitProgram; }
- const GLfloat *blitVertexArray() const { return &m_vertexCoordinateArray[0]; }
- const GLfloat *blitTextureArray() const { return &m_textureCoordinateArray[0]; }
-
- int maxTextureSize() const;
-
-private:
- QHash<QFontEngine *, QSGDistanceFieldGlyphCache *> m_caches;
-
- QOpenGLContext *ctx;
-
- QSGGlyphNode::AntialiasingMode m_defaultAntialiasingMode;
- ThresholdFunc m_threshold_func;
- AntialiasingSpreadFunc m_antialiasingSpread_func;
-
- mutable int m_maxTextureSize;
-
- QOpenGLShaderProgram *m_blitProgram;
- GLfloat m_vertexCoordinateArray[8];
- GLfloat m_textureCoordinateArray[8];
-};
-
-class Q_DECLARATIVE_EXPORT QSGDistanceFieldGlyphCache
-{
-public:
- ~QSGDistanceFieldGlyphCache();
-
- struct Metrics {
- qreal width;
- qreal height;
- qreal baselineX;
- qreal baselineY;
-
- bool isNull() const { return width == 0 || height == 0; }
- };
- Metrics glyphMetrics(glyph_t glyph);
-
- struct TexCoord {
- qreal x;
- qreal y;
- qreal width;
- qreal height;
- qreal xMargin;
- qreal yMargin;
-
- TexCoord() : x(0), y(0), width(0), height(0), xMargin(0), yMargin(0) { }
-
- bool isNull() const { return width == 0 || height == 0; }
- };
- TexCoord glyphTexCoord(glyph_t glyph);
-
- const QSGDistanceFieldGlyphCacheManager *manager() const { return m_manager; }
-
- GLuint texture();
- QSize textureSize() const;
- qreal fontScale() const;
- int distanceFieldRadius() const;
- QImage renderDistanceFieldGlyph(glyph_t glyph) const;
-
- int glyphCount() const;
-
- void populate(int count, const glyph_t *glyphs);
- void derefGlyphs(int count, const glyph_t *glyphs);
- void updateCache();
-
- bool cacheIsFull() const { return m_textureData->currY >= m_manager->maxTextureSize(); }
-
- bool useWorkaroundBrokenFBOReadback() const;
-
-private:
- QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font);
-
- void createTexture(int width, int height);
- void resizeTexture(int width, int height);
-
- QSGDistanceFieldGlyphCacheManager *m_manager;
-
- QOpenGLContext *ctx;
-
- QRawFont m_font;
- QRawFont m_referenceFont;
-
- int m_glyphCount;
- QHash<glyph_t, Metrics> m_metrics;
-
- struct DistanceFieldTextureData : public QOpenGLSharedResource {
- GLuint texture;
- GLuint fbo;
- QSize size;
- QHash<glyph_t, TexCoord> texCoords;
- QDataBuffer<glyph_t> pendingGlyphs;
- QHash<glyph_t, quint32> glyphRefCount;
- QSet<glyph_t> unusedGlyphs;
- int currX;
- int currY;
- QImage image;
- bool doubleGlyphResolution;
-
- DistanceFieldTextureData(QOpenGLContext *ctx)
- : QOpenGLSharedResource(ctx->shareGroup())
- , texture(0)
- , fbo(0)
- , pendingGlyphs(64)
- , currX(0)
- , currY(0)
- , doubleGlyphResolution(false)
- {}
-
- void invalidateResource()
- {
- texture = 0;
- fbo = 0;
- size = QSize();
- }
-
- void freeResource(QOpenGLContext *ctx)
- {
- glDeleteTextures(1, &texture);
- ctx->functions()->glDeleteFramebuffers(1, &fbo);
- }
- };
-
- DistanceFieldTextureData *textureData();
- DistanceFieldTextureData *m_textureData;
- static QHash<QString, QOpenGLMultiGroupSharedResource> m_textures_data;
-
- friend class QSGDistanceFieldGlyphCacheManager;
-};
-
-QT_END_NAMESPACE
-
-#endif // DISTANCEFIELDGLYPHCACHE_H
#include "qsgdistancefieldglyphnode_p.h"
#include "qsgdistancefieldglyphnode_p_p.h"
-#include "qsgdistancefieldglyphcache_p.h"
+#include <private/qsgdistancefieldutil_p.h>
#include <private/qsgcontext_p.h>
QT_BEGIN_NAMESPACE
, m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
, m_style(QQuickText::Normal)
, m_antialiasingMode(GrayAntialiasing)
- , m_dirtyFont(false)
, m_dirtyGeometry(false)
, m_dirtyMaterial(false)
{
{
delete m_material;
if (m_glyph_cache) {
- const QVector<quint32> &glyphIndexes = m_glyphs.glyphIndexes();
- m_glyph_cache->derefGlyphs(glyphIndexes.count(), glyphIndexes.constData());
+ m_glyph_cache->release(m_glyphs.glyphIndexes());
+ m_glyph_cache->unregisterGlyphNode(this);
}
}
m_position = QPointF(position.x(), position.y() - font.ascent());
m_glyphs = glyphs;
- m_dirtyFont = true;
+ 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::update()
{
- if (m_dirtyFont)
- updateFont();
- if (m_dirtyGeometry)
- updateGeometry();
if (m_dirtyMaterial)
updateMaterial();
+ if (m_dirtyGeometry)
+ updateGeometry();
}
void QSGDistanceFieldGlyphNode::updateGeometry()
{
Q_ASSERT(m_glyph_cache);
- QSGGeometry *g = geometry();
- QRectF boundingRect;
-
- const QVector<quint32> &glyphIndexes = m_glyphs.glyphIndexes();
+ if (m_glyphsToAdd.isEmpty())
+ return;
- m_glyph_cache->populate(glyphIndexes.count(), glyphIndexes.constData());
+ QSGGeometry *g = geometry();
Q_ASSERT(g->indexType() == GL_UNSIGNED_SHORT);
int oldVertexCount = g->vertexCount();
int oldIndexCount = g->indexCount();
- // We could potentially move the realloc part into the QSGGeometry object as a
- // grow() function...
+ QVector<QSGGeometry::TexturedPoint2D> vp;
+ vp.reserve(m_glyphsToAdd.size() * 4);
+ QVector<ushort> ip;
+ ip.reserve(m_glyphsToAdd.size() * 6);
- void *data = 0;
- if (oldVertexCount && oldIndexCount) {
- int byteSize = oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D)
- + oldIndexCount * sizeof(quint16);
- data = qMalloc(byteSize);
- memcpy(data, g->vertexData(), byteSize);
- }
+ QPointF margins(2, 2);
+ QPointF texMargins = margins / m_glyph_cache->fontScale();
- g->allocate(oldVertexCount + glyphIndexes.size() * 4, oldIndexCount + glyphIndexes.size() * 6);
+ const QSGDistanceFieldGlyphCache::Texture *textureToUse = 0;
- if (data) {
- memcpy(g->vertexData(), data, oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D));
- memcpy(g->indexData(), ((char *) data) + oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D),
- oldIndexCount * sizeof(quint16));
- qFree(data);
- }
+ QLinkedList<GlyphInfo>::iterator it = m_glyphsToAdd.begin();
+ while (it != m_glyphsToAdd.end()) {
+ quint32 glyphIndex = it->glyphIndex;
+ QSGDistanceFieldGlyphCache::TexCoord c = m_glyph_cache->glyphTexCoord(glyphIndex);
- QSGGeometry::TexturedPoint2D *vp = g->vertexDataAsTexturedPoint2D() + oldVertexCount;
- ushort *ip = g->indexDataAsUShort() + oldIndexCount;
+ if (c.isNull()) {
+ if (!c.isValid())
+ ++it;
+ else
+ it = m_glyphsToAdd.erase(it);
+ continue;
+ }
- QPointF margins(2, 2);
- QPointF texMargins = margins / m_glyph_cache->fontScale();
+ const QSGDistanceFieldGlyphCache::Texture *texture = m_glyph_cache->glyphTexture(glyphIndex);
+ if (!texture->textureId) {
+ ++it;
+ continue;
+ }
- QVector<QPointF> glyphPositions = m_glyphs.positions();
- for (int i = 0; i < glyphIndexes.size(); ++i) {
- quint32 glyphIndex = glyphIndexes.at(i);
QSGDistanceFieldGlyphCache::Metrics metrics = m_glyph_cache->glyphMetrics(glyphIndex);
- QSGDistanceFieldGlyphCache::TexCoord c = m_glyph_cache->glyphTexCoord(glyphIndex);
- if (!metrics.isNull() && !c.isNull()) {
- 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;
- }
+ if (!textureToUse)
+ textureToUse = texture;
- const QPointF &glyphPosition = glyphPositions.at(i);
+ 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();
- boundingRect |= QRectF(x, y, metrics.width, metrics.height);
+ m_boundingRect |= QRectF(x, y, metrics.width, metrics.height);
float cx1 = x;
float cx2 = x + metrics.width;
if (m_baseLine.isNull())
m_baseLine = glyphPosition;
- int vi = i & 1 ? (glyphIndexes.size() + 1) / 2 + i / 2 : i / 2;
- vp[4 * vi + 0].set(cx1, cy1, tx1, ty1);
- vp[4 * vi + 1].set(cx2, cy1, tx2, ty1);
- vp[4 * vi + 2].set(cx1, cy2, tx1, ty2);
- vp[4 * vi + 3].set(cx2, cy2, tx2, ty2);
-
- int o = i * 4 + oldVertexCount;
- ip[6 * i + 0] = o + 0;
- ip[6 * i + 1] = o + 2;
- ip[6 * i + 2] = o + 3;
- ip[6 * i + 3] = o + 3;
- ip[6 * i + 4] = o + 1;
- ip[6 * i + 5] = o + 0;
+ 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);
}
-// printf("Vertices:\n");
-// for (int v=0; v<g->vertexCount(); ++v) {
-// QSGGeometry::TexturedPoint2D *t = g->vertexDataAsTexturedPoint2D() + v;
-// printf(" - %d -- %f %f -- %.3f %.3f\n", v, t->x, t->y, t->tx, t->ty);
-// }
-
-// printf("Indices:\n");
-// for (int i=0; i<g->indexCount();) {
-
-// printf(" - %[ ", i);
-// printf("%d, ", g->indexDataAsUShort()[i++]);
-// printf("%d, ", g->indexDataAsUShort()[i++]);
-// printf("%d, ", g->indexDataAsUShort()[i++]);
-// printf("%d, ", g->indexDataAsUShort()[i++]);
-// printf("%d, ", g->indexDataAsUShort()[i++]);
-// printf("%d", g->indexDataAsUShort()[i++]);
-// printf(" ]\n");
-// }
-
- setBoundingRect(boundingRect);
+ 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;
-}
-void QSGDistanceFieldGlyphNode::updateFont()
-{
- m_glyph_cache = m_glyph_cacheManager->cache(m_glyphs.rawFont());
- m_dirtyFont = false;
+ m_material->setTexture(textureToUse);
}
void QSGDistanceFieldGlyphNode::updateMaterial()
****************************************************************************/
#include "qsgdistancefieldglyphnode_p_p.h"
-#include "qsgdistancefieldglyphcache_p.h"
+#include <private/qsgdistancefieldutil_p.h>
#include <private/qsgtexture_p.h>
#include <QtGui/qopenglfunctions.h>
#include <qmath.h>
QSGDistanceFieldTextMaterial *material = static_cast<QSGDistanceFieldTextMaterial *>(newEffect);
QSGDistanceFieldTextMaterial *oldMaterial = static_cast<QSGDistanceFieldTextMaterial *>(oldEffect);
- bool updated = material->updateTexture();
- if (updated && !material->glyphCache()->useWorkaroundBrokenFBOReadback())
- activate();
+ bool updated = material->updateCache();
if (oldMaterial == 0
|| material->color() != oldMaterial->color()
if (updated
|| oldMaterial == 0
- || oldMaterial->glyphCache()->texture() != material->glyphCache()->texture()) {
- program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->glyphCache()->textureSize().width(),
- 1.0 / material->glyphCache()->textureSize().height()));
- glBindTexture(GL_TEXTURE_2D, material->glyphCache()->texture());
+ || oldMaterial->texture()->textureId != material->texture()->textureId) {
+ program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->textureSize().width(),
+ 1.0 / material->textureSize().height()));
+ glBindTexture(GL_TEXTURE_2D, material->texture()->textureId);
if (updated) {
// Set the mag/min filters to be linear. We only need to do this when the texture
QSGDistanceFieldTextMaterial::QSGDistanceFieldTextMaterial()
: m_glyph_cache(0)
+ , m_texture(0)
{
setFlag(Blending, true);
}
return new QSGDistanceFieldTextMaterialShader;
}
-bool QSGDistanceFieldTextMaterial::updateTexture()
+bool QSGDistanceFieldTextMaterial::updateCache()
{
- m_glyph_cache->updateCache();
- QSize glyphCacheSize = m_glyph_cache->textureSize();
+ m_glyph_cache->update();
+ if (!m_texture)
+ m_texture = m_glyph_cache->glyphTexture(-1); // invalid texture
+ QSize glyphCacheSize = m_texture->size;
if (glyphCacheSize != m_size) {
m_size = glyphCacheSize;
if (oldMaterial == 0
|| oldMaterial->glyphCache()->fontScale() != material->glyphCache()->fontScale()
|| oldMaterial->shift() != material->shift()
- || oldMaterial->glyphCache()->textureSize() != material->glyphCache()->textureSize()) {
+ || oldMaterial->textureSize() != material->textureSize()) {
updateShift(material->glyphCache(), material->shift());
}
}
QT_MODULE(Declarative)
-class QSGDistanceFieldGlyphCache;
class QSGDistanceFieldGlyphCacheManager;
class QSGDistanceFieldTextMaterial;
class QSGDistanceFieldGlyphNode: public QSGGlyphNode
virtual void update();
-private:
void updateGeometry();
- void updateFont();
+
+private:
void updateMaterial();
QColor m_color;
QQuickText::TextStyle m_style;
QColor m_styleColor;
AntialiasingMode m_antialiasingMode;
+ QRectF m_boundingRect;
+
+ struct GlyphInfo {
+ quint32 glyphIndex;
+ QPointF position;
+ };
+ QLinkedList<GlyphInfo> m_glyphsToAdd;
- uint m_dirtyFont: 1;
uint m_dirtyGeometry: 1;
uint m_dirtyMaterial: 1;
};
#include <qsgmaterial.h>
#include "qsgdistancefieldglyphnode_p.h"
+#include "qsgadaptationlayer_p.h"
QT_BEGIN_NAMESPACE
-class QSGDistanceFieldGlyphCache;
-
class QSGDistanceFieldTextMaterial: public QSGMaterial
{
public:
void setGlyphCache(QSGDistanceFieldGlyphCache *a) { m_glyph_cache = a; }
QSGDistanceFieldGlyphCache *glyphCache() const { return m_glyph_cache; }
- bool updateTexture();
+ void setTexture(const QSGDistanceFieldGlyphCache::Texture * tex) { m_texture = tex; }
+ const QSGDistanceFieldGlyphCache::Texture * texture() const { return m_texture; }
+
+ QSize textureSize() const { return m_size; }
+
+ bool updateCache();
protected:
QSize m_size;
QColor m_color;
QSGDistanceFieldGlyphCache *m_glyph_cache;
+ const QSGDistanceFieldGlyphCache::Texture *m_texture;
};
class QSGDistanceFieldStyledTextMaterial : public QSGDistanceFieldTextMaterial
$$PWD/util/qsgtexture.h \
$$PWD/util/qsgtexture_p.h \
$$PWD/util/qsgtextureprovider_p.h \
- $$PWD/util/qsgpainternode_p.h
+ $$PWD/util/qsgpainternode_p.h \
+ $$PWD/util/qsgdistancefieldutil_p.h
SOURCES += \
$$PWD/util/qsgareaallocator.cpp \
$$PWD/util/qsgvertexcolormaterial.cpp \
$$PWD/util/qsgtexture.cpp \
$$PWD/util/qsgtextureprovider.cpp \
- $$PWD/util/qsgpainternode.cpp
+ $$PWD/util/qsgpainternode.cpp \
+ $$PWD/util/qsgdistancefieldutil.cpp
# QML / Adaptations API
$$PWD/qsgcontext_p.h \
$$PWD/qsgcontextplugin_p.h \
$$PWD/qsgdefaultglyphnode_p.h \
- $$PWD/qsgdistancefieldglyphcache_p.h \
+ $$PWD/qsgdefaultdistancefieldglyphcache_p.h \
$$PWD/qsgdistancefieldglyphnode_p.h \
$$PWD/qsgdistancefieldglyphnode_p_p.h \
$$PWD/qsgdefaultglyphnode_p_p.h \
$$PWD/qsgcontextplugin.cpp \
$$PWD/qsgdefaultglyphnode.cpp \
$$PWD/qsgdefaultglyphnode_p.cpp \
- $$PWD/qsgdistancefieldglyphcache.cpp \
+ $$PWD/qsgdefaultdistancefieldglyphcache.cpp \
$$PWD/qsgdistancefieldglyphnode.cpp \
$$PWD/qsgdistancefieldglyphnode_p.cpp \
$$PWD/qsgdefaultimagenode.cpp \
$$PWD/qsgdefaultrectanglenode.cpp \
$$PWD/qsgflashnode.cpp \
$$PWD/qsgpathsimplifier.cpp
+
+
+
+
+
+
**
****************************************************************************/
-#include "qsgdistancefieldglyphcache_p.h"
+#include "qsgdistancefieldutil_p.h"
#include <qmath.h>
#include <private/qsgpathsimplifier_p.h>
-#include <private/qdeclarativeglobal_p.h>
-#include <qopenglshaderprogram.h>
+#include <private/qsgadaptationlayer_p.h>
#include <QtGui/private/qopenglengineshadersource_p.h>
#include <private/qsgcontext_p.h>
-#include <private/qrawfont_p.h>
-#include <qopenglfunctions.h>
-#include <qglyphrun.h>
-#include <qrawfont.h>
-#include <qdir.h>
-#include <QtGui/qguiapplication.h>
-
-QT_BEGIN_NAMESPACE
-
-#define QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE 54
-#define QT_DISTANCEFIELD_DEFAULT_TILESIZE 64
-#define QT_DISTANCEFIELD_DEFAULT_SCALE 16
-#define QT_DISTANCEFIELD_DEFAULT_RADIUS 80
-#define QT_DISTANCEFIELD_HIGHGLYPHCOUNT 2000
-
-#define QT_DISTANCEFIELD_BASEFONTSIZE \
- (m_textureData->doubleGlyphResolution ? QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE * 2 : \
- QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE)
-#define QT_DISTANCEFIELD_TILESIZE \
- (m_textureData->doubleGlyphResolution ? QT_DISTANCEFIELD_DEFAULT_TILESIZE * 2 : \
- QT_DISTANCEFIELD_DEFAULT_TILESIZE)
-#define QT_DISTANCEFIELD_SCALE \
- (m_textureData->doubleGlyphResolution ? QT_DISTANCEFIELD_DEFAULT_SCALE / 2 : \
- QT_DISTANCEFIELD_DEFAULT_SCALE)
-#define QT_DISTANCEFIELD_RADIUS \
- (m_textureData->doubleGlyphResolution ? QT_DISTANCEFIELD_DEFAULT_RADIUS / 2 : \
- QT_DISTANCEFIELD_DEFAULT_RADIUS)
-
-static inline int qt_next_power_of_two(int v)
-{
- v--;
- v |= v >> 1;
- v |= v >> 2;
- v |= v >> 4;
- v |= v >> 8;
- v |= v >> 16;
- ++v;
- return v;
-}
+
static float defaultThresholdFunc(float glyphScale)
{
return image;
}
-static bool fontHasNarrowOutlines(const QRawFont &f)
+bool qt_fontHasNarrowOutlines(const QRawFont &f)
{
QRawFont font = f;
font.setPixelSize(QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE);
return minHThick == 1 || minVThick == 1;
}
-QSGDistanceFieldGlyphCacheManager::QSGDistanceFieldGlyphCacheManager(QOpenGLContext *c)
- : ctx(c)
+QImage qt_renderDistanceFieldGlyph(const QRawFont &font, glyph_t glyph, bool doubleResolution)
+{
+ QRawFont renderFont = font;
+ renderFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(doubleResolution) * QT_DISTANCEFIELD_SCALE(doubleResolution));
+
+ QPainterPath path = renderFont.pathForGlyph(glyph);
+ path.translate(-path.boundingRect().topLeft());
+ path.setFillRule(Qt::WindingFill);
+
+ QImage im = makeDistanceField(QT_DISTANCEFIELD_TILESIZE(doubleResolution),
+ path,
+ QT_DISTANCEFIELD_SCALE(doubleResolution),
+ QT_DISTANCEFIELD_RADIUS(doubleResolution) / QT_DISTANCEFIELD_SCALE(doubleResolution));
+ return im;
+}
+
+QSGDistanceFieldGlyphCacheManager::QSGDistanceFieldGlyphCacheManager(QSGContext *c)
+ : sgCtx(c)
, m_threshold_func(defaultThresholdFunc)
, m_antialiasingSpread_func(defaultAntialiasingSpreadFunc)
- , m_maxTextureSize(0)
{
#ifndef QT_OPENGL_ES
m_defaultAntialiasingMode = QSGGlyphNode::HighQualitySubPixelAntialiasing;
#else
m_defaultAntialiasingMode = QSGGlyphNode::GrayAntialiasing;
#endif
-
- m_vertexCoordinateArray[0] = -1.0f;
- m_vertexCoordinateArray[1] = -1.0f;
- m_vertexCoordinateArray[2] = 1.0f;
- m_vertexCoordinateArray[3] = -1.0f;
- m_vertexCoordinateArray[4] = 1.0f;
- m_vertexCoordinateArray[5] = 1.0f;
- m_vertexCoordinateArray[6] = -1.0f;
- m_vertexCoordinateArray[7] = 1.0f;
-
- m_textureCoordinateArray[0] = 0.0f;
- m_textureCoordinateArray[1] = 0.0f;
- m_textureCoordinateArray[2] = 1.0f;
- m_textureCoordinateArray[3] = 0.0f;
- m_textureCoordinateArray[4] = 1.0f;
- m_textureCoordinateArray[5] = 1.0f;
- m_textureCoordinateArray[6] = 0.0f;
- m_textureCoordinateArray[7] = 1.0f;
-
- m_blitProgram = new QOpenGLShaderProgram;
- {
- QString source;
- source.append(QLatin1String(qopenglslMainWithTexCoordsVertexShader));
- source.append(QLatin1String(qopenglslUntransformedPositionVertexShader));
-
- QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, m_blitProgram);
- vertexShader->compileSourceCode(source);
-
- m_blitProgram->addShader(vertexShader);
- }
- {
- QString source;
- source.append(QLatin1String(qopenglslMainFragmentShader));
- source.append(QLatin1String(qopenglslImageSrcFragmentShader));
-
- QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, m_blitProgram);
- fragmentShader->compileSourceCode(source);
-
- m_blitProgram->addShader(fragmentShader);
- }
- m_blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
- m_blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
- m_blitProgram->link();
}
QSGDistanceFieldGlyphCacheManager::~QSGDistanceFieldGlyphCacheManager()
{
- delete m_blitProgram;
qDeleteAll(m_caches.values());
}
QRawFontPrivate *fontD = QRawFontPrivate::get(font);
QHash<QFontEngine *, QSGDistanceFieldGlyphCache *>::iterator cache = m_caches.find(fontD->fontEngine);
if (cache == m_caches.end())
- cache = m_caches.insert(fontD->fontEngine, new QSGDistanceFieldGlyphCache(this, ctx, font));
+ cache = m_caches.insert(fontD->fontEngine, sgCtx->createDistanceFieldGlyphCache(font));
return cache.value();
}
-
-int QSGDistanceFieldGlyphCacheManager::maxTextureSize() const
-{
- if (!m_maxTextureSize)
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
- return m_maxTextureSize;
-}
-
-
-QHash<QString, QOpenGLMultiGroupSharedResource> QSGDistanceFieldGlyphCache::m_textures_data;
-
-QSGDistanceFieldGlyphCache::DistanceFieldTextureData *QSGDistanceFieldGlyphCache::textureData()
-{
- QString key = QString::fromLatin1("%1_%2_%3_%4")
- .arg(m_font.familyName())
- .arg(m_font.styleName())
- .arg(m_font.weight())
- .arg(m_font.style());
- return m_textures_data[key].value<QSGDistanceFieldGlyphCache::DistanceFieldTextureData>(QOpenGLContext::currentContext());
-}
-
-QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
- : m_manager(man)
- , ctx(c)
-{
- Q_ASSERT(font.isValid());
- m_font = font;
-
- m_textureData = textureData();
-
- QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
- m_glyphCount = fontD->fontEngine->glyphCount();
-
- m_textureData->doubleGlyphResolution = fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT;
-
- m_referenceFont = m_font;
- m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE);
- Q_ASSERT(m_referenceFont.isValid());
-}
-
-QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
-{
-}
-
-GLuint QSGDistanceFieldGlyphCache::texture()
-{
- return m_textureData->texture;
-}
-
-QSize QSGDistanceFieldGlyphCache::textureSize() const
-{
- return m_textureData->size;
-}
-
-QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph)
-{
- QHash<glyph_t, Metrics>::iterator metric = m_metrics.find(glyph);
- if (metric == m_metrics.end()) {
- QPainterPath path = m_font.pathForGlyph(glyph);
- QRectF br = path.boundingRect();
-
- Metrics m;
- m.width = br.width();
- m.height = br.height();
- m.baselineX = br.x();
- m.baselineY = -br.y();
-
- metric = m_metrics.insert(glyph, m);
- }
-
- return metric.value();
-}
-
-QSGDistanceFieldGlyphCache::TexCoord QSGDistanceFieldGlyphCache::glyphTexCoord(glyph_t glyph)
-{
- return m_textureData->texCoords.value(glyph);
-}
-
-QImage QSGDistanceFieldGlyphCache::renderDistanceFieldGlyph(glyph_t glyph) const
-{
- QRawFont renderFont = m_font;
- renderFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE * QT_DISTANCEFIELD_SCALE);
-
- QPainterPath path = renderFont.pathForGlyph(glyph);
- path.translate(-path.boundingRect().topLeft());
- path.setFillRule(Qt::WindingFill);
-
- QImage im = makeDistanceField(QT_DISTANCEFIELD_TILESIZE,
- path,
- QT_DISTANCEFIELD_SCALE,
- QT_DISTANCEFIELD_RADIUS / QT_DISTANCEFIELD_SCALE);
- return im;
-}
-
-qreal QSGDistanceFieldGlyphCache::fontScale() const
-{
- return qreal(m_font.pixelSize()) / QT_DISTANCEFIELD_BASEFONTSIZE;
-}
-
-int QSGDistanceFieldGlyphCache::distanceFieldRadius() const
-{
- return QT_DISTANCEFIELD_DEFAULT_RADIUS / QT_DISTANCEFIELD_SCALE;
-}
-
-void QSGDistanceFieldGlyphCache::populate(int count, const glyph_t *glyphs)
-{
- // Avoid useless and costly glyph re-generation
- if (cacheIsFull() && !m_textureData->unusedGlyphs.isEmpty()) {
- for (int i = 0; i < count; ++i) {
- glyph_t glyphIndex = glyphs[i];
- if (m_textureData->texCoords.contains(glyphIndex) && m_textureData->unusedGlyphs.contains(glyphIndex))
- m_textureData->unusedGlyphs.remove(glyphIndex);
- }
- }
-
- for (int i = 0; i < count; ++i) {
- glyph_t glyphIndex = glyphs[i];
- if ((int) glyphIndex >= glyphCount()) {
- qWarning("Warning: distance-field glyph is not available with index %d", glyphIndex);
- continue;
- }
-
- if (++m_textureData->glyphRefCount[glyphIndex] == 1)
- m_textureData->unusedGlyphs.remove(glyphIndex);
-
- if (m_textureData->texCoords.contains(glyphIndex)
- || (cacheIsFull() && m_textureData->unusedGlyphs.isEmpty()))
- continue;
-
- QPainterPath path = m_referenceFont.pathForGlyph(glyphIndex);
- if (path.isEmpty()) {
- m_textureData->texCoords.insert(glyphIndex, TexCoord());
- continue;
- }
- QRectF br = path.boundingRect();
-
- TexCoord c;
- c.xMargin = QT_DISTANCEFIELD_RADIUS / qreal(QT_DISTANCEFIELD_SCALE);
- c.yMargin = QT_DISTANCEFIELD_RADIUS / qreal(QT_DISTANCEFIELD_SCALE);
- c.x = m_textureData->currX;
- c.y = m_textureData->currY;
- c.width = br.width();
- c.height = br.height();
-
- if (!cacheIsFull()) {
- m_textureData->currX += QT_DISTANCEFIELD_TILESIZE;
- if (m_textureData->currX >= m_manager->maxTextureSize()) {
- m_textureData->currX = 0;
- m_textureData->currY += QT_DISTANCEFIELD_TILESIZE;
- }
- } else {
- // Recycle glyphs
- if (!m_textureData->unusedGlyphs.isEmpty()) {
- glyph_t unusedGlyph = *m_textureData->unusedGlyphs.constBegin();
- TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
- c.x = unusedCoord.x;
- c.y = unusedCoord.y;
- m_textureData->unusedGlyphs.remove(unusedGlyph);
- m_textureData->texCoords.remove(unusedGlyph);
- }
- }
-
- if (c.y < m_manager->maxTextureSize()) {
- m_textureData->texCoords.insert(glyphIndex, c);
- m_textureData->pendingGlyphs.add(glyphIndex);
- }
- }
-}
-
-void QSGDistanceFieldGlyphCache::derefGlyphs(int count, const glyph_t *glyphs)
-{
- for (int i = 0; i < count; ++i)
- if (--m_textureData->glyphRefCount[glyphs[i]] == 0 && !glyphTexCoord(glyphs[i]).isNull())
- m_textureData->unusedGlyphs.insert(glyphs[i]);
-}
-
-void QSGDistanceFieldGlyphCache::createTexture(int width, int height)
-{
- if (ctx->d_func()->workaround_brokenFBOReadBack && m_textureData->image.isNull())
- m_textureData->image = QImage(width, height, QImage::Format_Indexed8);
-
- while (glGetError() != GL_NO_ERROR) { }
-
- glGenTextures(1, &m_textureData->texture);
- glBindTexture(GL_TEXTURE_2D, m_textureData->texture);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- m_textureData->size = QSize(width, height);
-
- GLuint error = glGetError();
- if (error != GL_NO_ERROR) {
- glBindTexture(GL_TEXTURE_2D, 0);
- glDeleteTextures(1, &m_textureData->texture);
- m_textureData->texture = 0;
- }
-
-}
-
-void QSGDistanceFieldGlyphCache::resizeTexture(int width, int height)
-{
- int oldWidth = m_textureData->size.width();
- int oldHeight = m_textureData->size.height();
- if (width == oldWidth && height == oldHeight)
- return;
-
- GLuint oldTexture = m_textureData->texture;
- createTexture(width, height);
-
- if (!oldTexture)
- return;
-
- if (ctx->d_func()->workaround_brokenFBOReadBack) {
- m_textureData->image = m_textureData->image.copy(0, 0, width, height);
- QImage copy = m_textureData->image.copy(0, 0, oldWidth, oldHeight);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, oldWidth, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, copy.constBits());
- glDeleteTextures(1, &oldTexture);
- return;
- }
-
- if (!m_textureData->fbo)
- ctx->functions()->glGenFramebuffers(1, &m_textureData->fbo);
- ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_textureData->fbo);
-
- GLuint tmp_texture;
- glGenTextures(1, &tmp_texture);
- glBindTexture(GL_TEXTURE_2D, tmp_texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glBindTexture(GL_TEXTURE_2D, 0);
- ctx->functions()->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, tmp_texture, 0);
-
- ctx->functions()->glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, oldTexture);
-
- // save current render states
- GLboolean stencilTestEnabled;
- GLboolean depthTestEnabled;
- GLboolean scissorTestEnabled;
- GLboolean blendEnabled;
- GLint viewport[4];
- glGetBooleanv(GL_STENCIL_TEST, &stencilTestEnabled);
- glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled);
- glGetBooleanv(GL_SCISSOR_TEST, &scissorTestEnabled);
- glGetBooleanv(GL_BLEND, &blendEnabled);
- glGetIntegerv(GL_VIEWPORT, &viewport[0]);
-
- glDisable(GL_STENCIL_TEST);
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_SCISSOR_TEST);
- glDisable(GL_BLEND);
-
- glViewport(0, 0, oldWidth, oldHeight);
-
- ctx->functions()->glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_manager->blitVertexArray());
- ctx->functions()->glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_manager->blitTextureArray());
-
- m_manager->blitProgram()->bind();
- m_manager->blitProgram()->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
- m_manager->blitProgram()->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
- m_manager->blitProgram()->disableAttributeArray(int(QT_OPACITY_ATTR));
- m_manager->blitProgram()->setUniformValue("imageTexture", GLuint(0));
-
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-
- glBindTexture(GL_TEXTURE_2D, m_textureData->texture);
-
- glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
-
- ctx->functions()->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_RENDERBUFFER, 0);
- glDeleteTextures(1, &tmp_texture);
- glDeleteTextures(1, &oldTexture);
-
- ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
- // restore render states
- if (stencilTestEnabled)
- glEnable(GL_STENCIL_TEST);
- if (depthTestEnabled)
- glEnable(GL_DEPTH_TEST);
- if (scissorTestEnabled)
- glEnable(GL_SCISSOR_TEST);
- if (blendEnabled)
- glEnable(GL_BLEND);
- glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
-}
-
-void QSGDistanceFieldGlyphCache::updateCache()
-{
- if (m_textureData->pendingGlyphs.isEmpty())
- return;
-
- int requiredWidth = m_manager->maxTextureSize();
- int rows = 128 / (requiredWidth / QT_DISTANCEFIELD_TILESIZE); // Enough rows to fill the latin1 set by default..
- int requiredHeight = qMin(m_manager->maxTextureSize(), qMax(m_textureData->currY + QT_DISTANCEFIELD_TILESIZE, QT_DISTANCEFIELD_TILESIZE * rows));
-
- resizeTexture((requiredWidth), (requiredHeight));
- glBindTexture(GL_TEXTURE_2D, m_textureData->texture);
-
- // ### Remove before final release
- static bool cacheDistanceFields = QGuiApplication::arguments().contains(QLatin1String("--cache-distance-fields"));
-
-// #define QSGDISTANCEFIELDS_TIME_CREATION
-#ifdef QSGDISTANCEFIELDS_TIME_CREATION
- QTime time;
- time.start();
-#endif
-
- QString tmpPath = QString::fromLatin1("%1/.qt/").arg(QDir::tempPath());
- QString keyBase = QString::fromLatin1("%1%2%3_%4_%5_%6.fontblob")
- .arg(tmpPath)
- .arg(m_font.familyName())
- .arg(m_font.styleName())
- .arg(m_font.weight())
- .arg(m_font.style());
-
- if (cacheDistanceFields && !QFile::exists(tmpPath))
- QDir(tmpPath).mkpath(tmpPath);
-
- for (int i = 0; i < m_textureData->pendingGlyphs.size(); ++i) {
- glyph_t glyphIndex = m_textureData->pendingGlyphs.at(i);
- TexCoord c = m_textureData->texCoords.value(glyphIndex);
-
- if (cacheDistanceFields) {
- QString key = keyBase.arg(glyphIndex);
- QFile file(key);
- if (file.open(QFile::ReadOnly)) {
- int fileSize = file.size();
- int dim = sqrt(float(fileSize));
- QByteArray blob = file.readAll();
- glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, dim, dim, GL_ALPHA, GL_UNSIGNED_BYTE, blob.constData());
- continue;
- }
- }
-
- QImage glyph = renderDistanceFieldGlyph(glyphIndex);
-
- if (ctx->d_func()->workaround_brokenFBOReadBack) {
- uchar *inBits = glyph.scanLine(0);
- uchar *outBits = m_textureData->image.scanLine(int(c.y)) + int(c.x);
- for (int y = 0; y < glyph.height(); ++y) {
- qMemCopy(outBits, inBits, glyph.width());
- inBits += glyph.bytesPerLine();
- outBits += m_textureData->image.bytesPerLine();
- }
- }
-
- glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, glyph.width(), glyph.height(), GL_ALPHA, GL_UNSIGNED_BYTE, glyph.constBits());
-
- if (cacheDistanceFields) {
- QString key = keyBase.arg(glyphIndex);
- QFile file(key);
- file.open(QFile::WriteOnly);
- file.write((const char *) glyph.constBits(), glyph.width() * glyph.height());
- }
- }
-
-#ifdef QSGDISTANCEFIELDS_TIME_CREATION
- static int totalTime;
- totalTime += time.elapsed();
- printf("time: %d\n", totalTime);
-#endif
-
- m_textureData->pendingGlyphs.reset();
-}
-
-bool QSGDistanceFieldGlyphCache::useWorkaroundBrokenFBOReadback() const
-{
- return ctx->d_func()->workaround_brokenFBOReadBack;
-}
-
-int QSGDistanceFieldGlyphCache::glyphCount() const
-{
- return m_glyphCount;
-}
-
-QT_END_NAMESPACE
--- /dev/null
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QSGDISTANCEFIELDUTIL_H
+#define QSGDISTANCEFIELDUTIL_H
+
+#include <qrawfont.h>
+#include <private/qfontengine_p.h>
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE 54
+#define QT_DISTANCEFIELD_DEFAULT_TILESIZE 64
+#define QT_DISTANCEFIELD_DEFAULT_SCALE 16
+#define QT_DISTANCEFIELD_DEFAULT_RADIUS 80
+#define QT_DISTANCEFIELD_HIGHGLYPHCOUNT 2000
+
+#define QT_DISTANCEFIELD_BASEFONTSIZE(NarrowOutlineFont) \
+ (NarrowOutlineFont ? QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE * 2 : \
+ QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE)
+#define QT_DISTANCEFIELD_TILESIZE(NarrowOutlineFont) \
+ (NarrowOutlineFont ? QT_DISTANCEFIELD_DEFAULT_TILESIZE * 2 : \
+ QT_DISTANCEFIELD_DEFAULT_TILESIZE)
+#define QT_DISTANCEFIELD_SCALE(NarrowOutlineFont) \
+ (NarrowOutlineFont ? QT_DISTANCEFIELD_DEFAULT_SCALE / 2 : \
+ QT_DISTANCEFIELD_DEFAULT_SCALE)
+#define QT_DISTANCEFIELD_RADIUS(NarrowOutlineFont) \
+ (NarrowOutlineFont ? QT_DISTANCEFIELD_DEFAULT_RADIUS / 2 : \
+ QT_DISTANCEFIELD_DEFAULT_RADIUS)
+
+
+typedef float (*ThresholdFunc)(float glyphScale);
+typedef float (*AntialiasingSpreadFunc)(float glyphScale);
+
+bool qt_fontHasNarrowOutlines(const QRawFont &f);
+QImage qt_renderDistanceFieldGlyph(const QRawFont &font, glyph_t glyph, bool doubleResolution);
+
+
+class QOpenGLShaderProgram;
+class QSGDistanceFieldGlyphCache;
+class QSGContext;
+
+class Q_DECLARATIVE_EXPORT QSGDistanceFieldGlyphCacheManager
+{
+public:
+ QSGDistanceFieldGlyphCacheManager(QSGContext *c);
+ ~QSGDistanceFieldGlyphCacheManager();
+
+ QSGDistanceFieldGlyphCache *cache(const QRawFont &font);
+
+ QSGGlyphNode::AntialiasingMode defaultAntialiasingMode() const { return m_defaultAntialiasingMode; }
+ void setDefaultAntialiasingMode(QSGGlyphNode::AntialiasingMode mode) { m_defaultAntialiasingMode = mode; }
+
+ ThresholdFunc thresholdFunc() const { return m_threshold_func; }
+ void setThresholdFunc(ThresholdFunc func) { m_threshold_func = func; }
+
+ AntialiasingSpreadFunc antialiasingSpreadFunc() const { return m_antialiasingSpread_func; }
+ void setAntialiasingSpreadFunc(AntialiasingSpreadFunc func) { m_antialiasingSpread_func = func; }
+
+private:
+ QHash<QFontEngine *, QSGDistanceFieldGlyphCache *> m_caches;
+
+ QSGContext *sgCtx;
+
+ QSGGlyphNode::AntialiasingMode m_defaultAntialiasingMode;
+ ThresholdFunc m_threshold_func;
+ AntialiasingSpreadFunc m_antialiasingSpread_func;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGDISTANCEFIELDUTIL_H
#include <private/qquicktext_p.h>
#include <private/qquicktext_p_p.h>
#include <private/qdeclarativevaluetype_p.h>
-#include <private/qsgdistancefieldglyphcache_p.h>
#include <QFontMetrics>
#include <QGraphicsSceneMouseEvent>
#include <qmath.h>
#include <QtGui/qguiapplication.h>
#include <private/qquicktextedit_p.h>
#include <private/qquicktextedit_p_p.h>
-#include <private/qsgdistancefieldglyphcache_p.h>
#include <QFontMetrics>
#include <QQuickView>
#include <QDir>
#include <QDir>
#include <QStyle>
#include <QInputContext>
-#include <private/qsgdistancefieldglyphcache_p.h>
#include <QtOpenGL/QGLShaderProgram>
#include <math.h>