summaryrefslogtreecommitdiffstats
path: root/src/extras/text
diff options
context:
space:
mode:
Diffstat (limited to 'src/extras/text')
-rw-r--r--src/extras/text/areaallocator.cpp296
-rw-r--r--src/extras/text/areaallocator_p.h92
-rw-r--r--src/extras/text/distancefieldtextrenderer.cpp156
-rw-r--r--src/extras/text/distancefieldtextrenderer_p.h90
-rw-r--r--src/extras/text/distancefieldtextrenderer_p_p.h95
-rw-r--r--src/extras/text/qdistancefieldglyphcache.cpp370
-rw-r--r--src/extras/text/qdistancefieldglyphcache_p.h111
-rw-r--r--src/extras/text/qtext2dentity.cpp381
-rw-r--r--src/extras/text/qtext2dentity.h98
-rw-r--r--src/extras/text/qtext2dentity_p.h122
-rw-r--r--src/extras/text/qtext2dmaterial.cpp179
-rw-r--r--src/extras/text/qtext2dmaterial_p.h83
-rw-r--r--src/extras/text/qtext2dmaterial_p_p.h106
-rw-r--r--src/extras/text/qtextureatlas.cpp303
-rw-r--r--src/extras/text/qtextureatlas_p.h95
-rw-r--r--src/extras/text/qtextureatlas_p_p.h139
-rw-r--r--src/extras/text/text.pri21
17 files changed, 2737 insertions, 0 deletions
diff --git a/src/extras/text/areaallocator.cpp b/src/extras/text/areaallocator.cpp
new file mode 100644
index 000000000..61f1d5bc6
--- /dev/null
+++ b/src/extras/text/areaallocator.cpp
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "areaallocator_p.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qpoint.h>
+
+//
+// This file is copied from qtdeclarative/src/quick/scenegraph/util/qsgareaallocator.cpp
+//
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DExtras {
+
+namespace
+{
+ enum SplitType
+ {
+ VerticalSplit,
+ HorizontalSplit
+ };
+
+ static const int maxMargin = 2;
+}
+
+struct AreaAllocatorNode
+{
+ AreaAllocatorNode(AreaAllocatorNode *parent);
+ ~AreaAllocatorNode();
+ inline bool isLeaf();
+
+ AreaAllocatorNode *parent;
+ AreaAllocatorNode *left;
+ AreaAllocatorNode *right;
+ int split; // only valid for inner nodes.
+ SplitType splitType;
+ bool isOccupied; // only valid for leaf nodes.
+};
+
+AreaAllocatorNode::AreaAllocatorNode(AreaAllocatorNode *parent)
+ : parent(parent)
+ , left(0)
+ , right(0)
+ , isOccupied(false)
+{
+}
+
+AreaAllocatorNode::~AreaAllocatorNode()
+{
+ delete left;
+ delete right;
+}
+
+bool AreaAllocatorNode::isLeaf()
+{
+ Q_ASSERT((left != 0) == (right != 0));
+ return !left;
+}
+
+
+AreaAllocator::AreaAllocator(const QSize &size) : m_size(size)
+{
+ m_root = new AreaAllocatorNode(0);
+}
+
+AreaAllocator::~AreaAllocator()
+{
+ delete m_root;
+}
+
+QRect AreaAllocator::allocate(const QSize &size)
+{
+ QPoint point;
+ bool result = allocateInNode(size, point, QRect(QPoint(0, 0), m_size), m_root);
+ return result ? QRect(point, size) : QRect();
+}
+
+bool AreaAllocator::deallocate(const QRect &rect)
+{
+ return deallocateInNode(rect.topLeft(), m_root);
+}
+
+bool AreaAllocator::allocateInNode(const QSize &size, QPoint &result, const QRect &currentRect, AreaAllocatorNode *node)
+{
+ if (size.width() > currentRect.width() || size.height() > currentRect.height())
+ return false;
+
+ if (node->isLeaf()) {
+ if (node->isOccupied)
+ return false;
+ if (size.width() + maxMargin >= currentRect.width() && size.height() + maxMargin >= currentRect.height()) {
+ //Snug fit, occupy entire rectangle.
+ node->isOccupied = true;
+ result = currentRect.topLeft();
+ return true;
+ }
+ // TODO: Reuse nodes.
+ // Split node.
+ node->left = new AreaAllocatorNode(node);
+ node->right = new AreaAllocatorNode(node);
+ QRect splitRect = currentRect;
+ if ((currentRect.width() - size.width()) * currentRect.height() < (currentRect.height() - size.height()) * currentRect.width()) {
+ node->splitType = HorizontalSplit;
+ node->split = currentRect.top() + size.height();
+ splitRect.setHeight(size.height());
+ } else {
+ node->splitType = VerticalSplit;
+ node->split = currentRect.left() + size.width();
+ splitRect.setWidth(size.width());
+ }
+ return allocateInNode(size, result, splitRect, node->left);
+ } else {
+ // TODO: avoid unnecessary recursion.
+ // has been split.
+ QRect leftRect = currentRect;
+ QRect rightRect = currentRect;
+ if (node->splitType == HorizontalSplit) {
+ leftRect.setHeight(node->split - leftRect.top());
+ rightRect.setTop(node->split);
+ } else {
+ leftRect.setWidth(node->split - leftRect.left());
+ rightRect.setLeft(node->split);
+ }
+ if (allocateInNode(size, result, leftRect, node->left))
+ return true;
+ if (allocateInNode(size, result, rightRect, node->right))
+ return true;
+ return false;
+ }
+}
+
+bool AreaAllocator::deallocateInNode(const QPoint &pos, AreaAllocatorNode *node)
+{
+ while (!node->isLeaf()) {
+ // has been split.
+ int cmp = node->splitType == HorizontalSplit ? pos.y() : pos.x();
+ node = cmp < node->split ? node->left : node->right;
+ }
+ if (!node->isOccupied)
+ return false;
+ node->isOccupied = false;
+ mergeNodeWithNeighbors(node);
+ return true;
+}
+
+void AreaAllocator::mergeNodeWithNeighbors(AreaAllocatorNode *node)
+{
+ bool done = false;
+ AreaAllocatorNode *parent = 0;
+ AreaAllocatorNode *current = 0;
+ AreaAllocatorNode *sibling;
+ while (!done) {
+ Q_ASSERT(node->isLeaf());
+ Q_ASSERT(!node->isOccupied);
+ if (node->parent == 0)
+ return; // No neighbors.
+
+ SplitType splitType = SplitType(node->parent->splitType);
+ done = true;
+
+ /* Special case. Might be faster than going through the general code path.
+ // Merge with sibling.
+ parent = node->parent;
+ sibling = (node == parent->left ? parent->right : parent->left);
+ if (sibling->isLeaf() && !sibling->isOccupied) {
+ Q_ASSERT(!sibling->right);
+ node = parent;
+ parent->isOccupied = false;
+ delete parent->left;
+ delete parent->right;
+ parent->left = parent->right = 0;
+ done = false;
+ continue;
+ }
+ */
+
+ // Merge with left neighbor.
+ current = node;
+ parent = current->parent;
+ while (parent && current == parent->left && parent->splitType == splitType) {
+ current = parent;
+ parent = parent->parent;
+ }
+
+ if (parent && parent->splitType == splitType) {
+ Q_ASSERT(current == parent->right);
+ Q_ASSERT(parent->left);
+
+ AreaAllocatorNode *neighbor = parent->left;
+ while (neighbor->right && neighbor->splitType == splitType)
+ neighbor = neighbor->right;
+
+ if (neighbor->isLeaf() && neighbor->parent->splitType == splitType && !neighbor->isOccupied) {
+ // Left neighbor can be merged.
+ parent->split = neighbor->parent->split;
+
+ parent = neighbor->parent;
+ sibling = neighbor == parent->left ? parent->right : parent->left;
+ AreaAllocatorNode **nodeRef = &m_root;
+ if (parent->parent) {
+ if (parent == parent->parent->left)
+ nodeRef = &parent->parent->left;
+ else
+ nodeRef = &parent->parent->right;
+ }
+ sibling->parent = parent->parent;
+ *nodeRef = sibling;
+ parent->left = parent->right = 0;
+ delete parent;
+ delete neighbor;
+ done = false;
+ }
+ }
+
+ // Merge with right neighbor.
+ current = node;
+ parent = current->parent;
+ while (parent && current == parent->right && parent->splitType == splitType) {
+ current = parent;
+ parent = parent->parent;
+ }
+
+ if (parent && parent->splitType == splitType) {
+ Q_ASSERT(current == parent->left);
+ Q_ASSERT(parent->right);
+
+ AreaAllocatorNode *neighbor = parent->right;
+ while (neighbor->left && neighbor->splitType == splitType)
+ neighbor = neighbor->left;
+
+ if (neighbor->isLeaf() && neighbor->parent->splitType == splitType && !neighbor->isOccupied) {
+ // Right neighbor can be merged.
+ parent->split = neighbor->parent->split;
+
+ parent = neighbor->parent;
+ sibling = neighbor == parent->left ? parent->right : parent->left;
+ AreaAllocatorNode **nodeRef = &m_root;
+ if (parent->parent) {
+ if (parent == parent->parent->left)
+ nodeRef = &parent->parent->left;
+ else
+ nodeRef = &parent->parent->right;
+ }
+ sibling->parent = parent->parent;
+ *nodeRef = sibling;
+ parent->left = parent->right = 0;
+ delete parent;
+ delete neighbor;
+ done = false;
+ }
+ }
+ } // end while (!done)
+}
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
diff --git a/src/extras/text/areaallocator_p.h b/src/extras/text/areaallocator_p.h
new file mode 100644
index 000000000..809c5c698
--- /dev/null
+++ b/src/extras/text/areaallocator_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DEXTRAS_AREAALLOCATOR_P_H
+#define QT3DEXTRAS_AREAALLOCATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+//
+// This file is copied from qtdeclarative/src/quick/scenegraph/util/qsgareaallocator_p.h
+//
+
+#include <QtCore/qsize.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRect;
+class QPoint;
+
+namespace Qt3DExtras {
+
+struct AreaAllocatorNode;
+
+class AreaAllocator
+{
+public:
+ AreaAllocator(const QSize &size);
+ ~AreaAllocator();
+
+ QRect allocate(const QSize &size);
+ bool deallocate(const QRect &rect);
+ bool isEmpty() const { return m_root == 0; }
+ QSize size() const { return m_size; }
+private:
+ bool allocateInNode(const QSize &size, QPoint &result, const QRect &currentRect, AreaAllocatorNode *node);
+ bool deallocateInNode(const QPoint &pos, AreaAllocatorNode *node);
+ void mergeNodeWithNeighbors(AreaAllocatorNode *node);
+
+ AreaAllocatorNode *m_root;
+ QSize m_size;
+};
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
+
+#endif // QT3DEXTRAS_AREAALLOCATOR_P_H
diff --git a/src/extras/text/distancefieldtextrenderer.cpp b/src/extras/text/distancefieldtextrenderer.cpp
new file mode 100644
index 000000000..9f390e8da
--- /dev/null
+++ b/src/extras/text/distancefieldtextrenderer.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <Qt3DRender/qmaterial.h>
+#include <Qt3DRender/qbuffer.h>
+#include <Qt3DRender/qattribute.h>
+#include <Qt3DRender/qgeometry.h>
+#include <Qt3DRender/qgeometryrenderer.h>
+
+#include <Qt3DExtras/private/qtext2dmaterial_p.h>
+
+#include "distancefieldtextrenderer_p.h"
+#include "distancefieldtextrenderer_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DExtras {
+
+using namespace Qt3DCore;
+
+DistanceFieldTextRendererPrivate::DistanceFieldTextRendererPrivate()
+ : m_renderer(nullptr)
+ , m_geometry(nullptr)
+ , m_positionAttr(nullptr)
+ , m_texCoordAttr(nullptr)
+ , m_indexAttr(nullptr)
+ , m_vertexBuffer(nullptr)
+ , m_indexBuffer(nullptr)
+ , m_material(nullptr)
+{
+}
+
+DistanceFieldTextRendererPrivate::~DistanceFieldTextRendererPrivate()
+{
+}
+
+void DistanceFieldTextRendererPrivate::init()
+{
+ Q_Q(DistanceFieldTextRenderer);
+
+ m_renderer = new Qt3DRender::QGeometryRenderer(q);
+ m_renderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);
+
+ m_geometry = new Qt3DRender::QGeometry(m_renderer);
+ m_renderer->setGeometry(m_geometry);
+
+ m_vertexBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, m_geometry);
+ m_indexBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::IndexBuffer, m_geometry);
+
+ m_positionAttr = new Qt3DRender::QAttribute(m_geometry);
+ m_positionAttr->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());
+ m_positionAttr->setVertexBaseType(Qt3DRender::QAttribute::Float);
+ m_positionAttr->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
+ m_positionAttr->setVertexSize(3);
+ m_positionAttr->setByteStride(5 * sizeof(float));
+ m_positionAttr->setByteOffset(0);
+ m_positionAttr->setBuffer(m_vertexBuffer);
+
+ m_texCoordAttr = new Qt3DRender::QAttribute(m_geometry);
+ m_texCoordAttr->setName(Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName());
+ m_texCoordAttr->setVertexBaseType(Qt3DRender::QAttribute::Float);
+ m_texCoordAttr->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
+ m_texCoordAttr->setVertexSize(2);
+ m_texCoordAttr->setByteStride(5 * sizeof(float));
+ m_texCoordAttr->setByteOffset(3 * sizeof(float));
+ m_texCoordAttr->setBuffer(m_vertexBuffer);
+
+ m_indexAttr = new Qt3DRender::QAttribute(m_geometry);
+ m_indexAttr->setAttributeType(Qt3DRender::QAttribute::IndexAttribute);
+ m_indexAttr->setVertexBaseType(Qt3DRender::QAttribute::UnsignedShort);
+ m_indexAttr->setBuffer(m_indexBuffer);
+
+ m_geometry->addAttribute(m_positionAttr);
+ m_geometry->setBoundingVolumePositionAttribute(m_positionAttr);
+ m_geometry->addAttribute(m_texCoordAttr);
+ m_geometry->addAttribute(m_indexAttr);
+
+ m_material = new QText2DMaterial(q);
+
+ q->addComponent(m_renderer);
+ q->addComponent(m_material);
+}
+
+DistanceFieldTextRenderer::DistanceFieldTextRenderer(QNode *parent)
+ : QEntity(*new DistanceFieldTextRendererPrivate(), parent)
+{
+ Q_D(DistanceFieldTextRenderer);
+ d->init();
+}
+
+DistanceFieldTextRenderer::~DistanceFieldTextRenderer()
+{
+}
+
+void DistanceFieldTextRenderer::setGlyphData(Qt3DRender::QAbstractTexture *glyphTexture,
+ const QVector<float> &vertexData,
+ const QVector<quint16> &indexData)
+{
+ Q_D(DistanceFieldTextRenderer);
+
+ const int vertexCount = vertexData.size() / 5;
+
+ d->m_vertexBuffer->setData(QByteArray((char*) vertexData.data(), vertexData.size() * sizeof(float)));
+ d->m_indexBuffer->setData(QByteArray((char*) indexData.data(), indexData.size() * sizeof(quint16)));
+ d->m_positionAttr->setCount(vertexCount);
+ d->m_texCoordAttr->setCount(vertexCount);
+ d->m_indexAttr->setCount(indexData.size());
+
+ d->m_material->setDistanceFieldTexture(glyphTexture);
+}
+
+void DistanceFieldTextRenderer::setColor(const QColor &color)
+{
+ Q_D(DistanceFieldTextRenderer);
+ d->m_material->setColor(color);
+}
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
diff --git a/src/extras/text/distancefieldtextrenderer_p.h b/src/extras/text/distancefieldtextrenderer_p.h
new file mode 100644
index 000000000..fdb9a836f
--- /dev/null
+++ b/src/extras/text/distancefieldtextrenderer_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DEXTRAS_DISTANCEFIELDTEXTRENDERER_P_H
+#define QT3DEXTRAS_DISTANCEFIELDTEXTRENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QRectF>
+#include <Qt3DCore/qnode.h>
+#include <Qt3DCore/qentity.h>
+#include <Qt3DExtras/qt3dextras_global.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+class QAbstractTexture;
+}
+
+namespace Qt3DExtras {
+
+class DistanceFieldTextRendererPrivate;
+
+class DistanceFieldTextRenderer : public Qt3DCore::QEntity
+{
+ Q_OBJECT
+
+public:
+ DistanceFieldTextRenderer(Qt3DCore::QNode *parent = nullptr);
+ ~DistanceFieldTextRenderer();
+
+ void setGlyphData(Qt3DRender::QAbstractTexture *glyphTexture,
+ const QVector<float> &vertexData,
+ const QVector<quint16> &indexData);
+
+ void setColor(const QColor &color);
+
+ Q_DECLARE_PRIVATE(DistanceFieldTextRenderer)
+};
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
+
+#endif // QT3DEXTRAS_DISTANCEFIELDTEXTRENDERER_P_H
diff --git a/src/extras/text/distancefieldtextrenderer_p_p.h b/src/extras/text/distancefieldtextrenderer_p_p.h
new file mode 100644
index 000000000..af4bc4723
--- /dev/null
+++ b/src/extras/text/distancefieldtextrenderer_p_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DEXTRAS_DISTANCEFIELDTEXTRENDERER_P_P_H
+#define QT3DEXTRAS_DISTANCEFIELDTEXTRENDERER_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/private/qentity_p.h>
+#include <Qt3DExtras/private/distancefieldtextrenderer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+class QGeometryRenderer;
+class QGeometry;
+class QMaterial;
+class QAttribute;
+class QBuffer;
+}
+
+namespace Qt3DExtras {
+
+class QText2DMaterial;
+
+class DistanceFieldTextRendererPrivate : public Qt3DCore::QEntityPrivate
+{
+public:
+ DistanceFieldTextRendererPrivate();
+ ~DistanceFieldTextRendererPrivate();
+
+ Q_DECLARE_PUBLIC(DistanceFieldTextRenderer)
+
+ void init();
+
+ Qt3DRender::QGeometryRenderer *m_renderer;
+ Qt3DRender::QGeometry *m_geometry;
+ Qt3DRender::QAttribute *m_positionAttr;
+ Qt3DRender::QAttribute *m_texCoordAttr;
+ Qt3DRender::QAttribute *m_indexAttr;
+ Qt3DRender::QBuffer *m_vertexBuffer;
+ Qt3DRender::QBuffer *m_indexBuffer;
+ QText2DMaterial *m_material;
+};
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
+
+#endif // QT3DEXTRAS_DISTANCEFIELDTEXTRENDERER_P_P_H
diff --git a/src/extras/text/qdistancefieldglyphcache.cpp b/src/extras/text/qdistancefieldglyphcache.cpp
new file mode 100644
index 000000000..99085f378
--- /dev/null
+++ b/src/extras/text/qdistancefieldglyphcache.cpp
@@ -0,0 +1,370 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/qrawfont.h>
+#include <QtGui/qglyphrun.h>
+#include <QtGui/private/qrawfont_p.h>
+
+#include "qdistancefieldglyphcache_p.h"
+#include "qtextureatlas_p.h"
+
+#include <QtGui/qfont.h>
+#include <QtGui/private/qdistancefield_p.h>
+#include <Qt3DCore/private/qnode_p.h>
+#include <Qt3DExtras/private/qtextureatlas_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define DEFAULT_IMAGE_PADDING 1
+
+using namespace Qt3DCore;
+
+namespace Qt3DExtras {
+
+// ref-count glyphs and keep track of where they are stored
+class StoredGlyph {
+public:
+ StoredGlyph() = default;
+ StoredGlyph(const StoredGlyph &) = default;
+ StoredGlyph(const QRawFont &font, quint32 glyph, bool doubleResolution);
+
+ int refCount() const { return m_ref; }
+ void ref() { ++m_ref; }
+ int deref() { return m_ref = std::max(m_ref - 1, (quint32) 0); }
+
+ bool addToTextureAtlas(QTextureAtlas *atlas);
+ void removeFromTextureAtlas();
+
+ QTextureAtlas *atlas() const { return m_atlas; }
+ QRectF glyphPathBoundingRect() const { return m_glyphPathBoundingRect; }
+ QRectF texCoords() const;
+
+private:
+ quint32 m_glyph = (quint32) -1;
+ quint32 m_ref = 0;
+ QTextureAtlas *m_atlas = nullptr;
+ QTextureAtlas::TextureId m_atlasEntry = QTextureAtlas::InvalidTexture;
+ QRectF m_glyphPathBoundingRect;
+ QImage m_distanceFieldImage; // only used until added to texture atlas
+};
+
+// A DistanceFieldFont stores all glyphs for a given QRawFont.
+// it will use multiple QTextureAtlasess to store the distance
+// fields and uses ref-counting for each glyph to ensure that
+// unused glyphs are removed from the texture atlasses.
+class DistanceFieldFont
+{
+public:
+ DistanceFieldFont(const QRawFont &font, bool doubleRes, Qt3DCore::QNode *parent);
+ ~DistanceFieldFont();
+
+ StoredGlyph findGlyph(quint32 glyph) const;
+ StoredGlyph refGlyph(quint32 glyph);
+ void derefGlyph(quint32 glyph);
+
+ bool doubleGlyphResolution() const { return m_doubleGlyphResolution; }
+
+private:
+ QRawFont m_font;
+ bool m_doubleGlyphResolution;
+ Qt3DCore::QNode *m_parentNode; // parent node for the QTextureAtlasses
+
+ QHash<quint32, StoredGlyph> m_glyphs;
+
+ QVector<QTextureAtlas*> m_atlasses;
+};
+
+StoredGlyph::StoredGlyph(const QRawFont &font, quint32 glyph, bool doubleResolution)
+ : m_glyph(glyph)
+ , m_ref(1)
+ , m_atlas(nullptr)
+ , m_atlasEntry(QTextureAtlas::InvalidTexture)
+{
+ // create new single-channel distance field image for given glyph
+ const QPainterPath path = font.pathForGlyph(glyph);
+ const QDistanceField dfield(font, glyph, doubleResolution);
+ m_distanceFieldImage = dfield.toImage(QImage::Format_Alpha8);
+
+ // scale bounding rect down (as in QSGDistanceFieldGlyphCache::glyphData())
+ const QRectF pathBound = path.boundingRect();
+ float f = 1.0f / QT_DISTANCEFIELD_SCALE(doubleResolution);
+ m_glyphPathBoundingRect = QRectF(pathBound.left() * f, -pathBound.top() * f, pathBound.width() * f, pathBound.height() * f);
+}
+
+bool StoredGlyph::addToTextureAtlas(QTextureAtlas *atlas)
+{
+ if (m_atlas || m_distanceFieldImage.isNull())
+ return false;
+
+ const auto texId = atlas->addImage(m_distanceFieldImage, DEFAULT_IMAGE_PADDING);
+ if (texId != QTextureAtlas::InvalidTexture) {
+ m_atlas = atlas;
+ m_atlasEntry = texId;
+ m_distanceFieldImage = QImage(); // free glyph image data
+ return true;
+ }
+
+ return false;
+}
+
+void StoredGlyph::removeFromTextureAtlas()
+{
+ if (m_atlas) {
+ m_atlas->removeImage(m_atlasEntry);
+ m_atlas = nullptr;
+ m_atlasEntry = QTextureAtlas::InvalidTexture;
+ }
+}
+
+QRectF StoredGlyph::texCoords() const
+{
+ return m_atlas ? m_atlas->imageTexCoords(m_atlasEntry) : QRectF();
+}
+
+DistanceFieldFont::DistanceFieldFont(const QRawFont &font, bool doubleRes, Qt3DCore::QNode *parent)
+ : m_font(font)
+ , m_doubleGlyphResolution(doubleRes)
+ , m_parentNode(parent)
+{
+}
+
+DistanceFieldFont::~DistanceFieldFont()
+{
+ qDeleteAll(m_atlasses);
+}
+
+StoredGlyph DistanceFieldFont::findGlyph(quint32 glyph) const
+{
+ const auto it = m_glyphs.find(glyph);
+ return (it != m_glyphs.cend()) ? it.value() : StoredGlyph();
+}
+
+StoredGlyph DistanceFieldFont::refGlyph(quint32 glyph)
+{
+ // if glyph already exists, just increase ref-count
+ auto it = m_glyphs.find(glyph);
+ if (it != m_glyphs.end()) {
+ it.value().ref();
+ return it.value();
+ }
+
+ // need to create new glyph
+ StoredGlyph storedGlyph(m_font, glyph, m_doubleGlyphResolution);
+
+ // see if one of the existing atlasses can hold the distance field image
+ for (int i = 0; i < m_atlasses.size(); i++)
+ if (storedGlyph.addToTextureAtlas(m_atlasses[i]))
+ break;
+
+ // if no texture atlas is big enough (or no exists yet), allocate a new one
+ if (!storedGlyph.atlas()) {
+ // this should be enough to store 40-60 glyphs, which should be sufficient for most
+ // scenarios
+ const int size = m_doubleGlyphResolution ? 512 : 256;
+
+ QTextureAtlas *atlas = new QTextureAtlas(m_parentNode);
+ atlas->setWidth(size);
+ atlas->setHeight(size);
+ atlas->setFormat(Qt3DRender::QAbstractTexture::R8_UNorm);
+ atlas->setPixelFormat(QOpenGLTexture::Red);
+ atlas->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear);
+ atlas->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear);
+ m_atlasses << atlas;
+
+ if (!storedGlyph.addToTextureAtlas(atlas))
+ qWarning() << Q_FUNC_INFO << "Couldn't add glyph to newly allocated atlas. Glyph could be huge?";
+ }
+
+ m_glyphs.insert(glyph, storedGlyph);
+ return storedGlyph;
+}
+
+void DistanceFieldFont::derefGlyph(quint32 glyph)
+{
+ auto it = m_glyphs.find(glyph);
+ if (it == m_glyphs.end())
+ return;
+
+ // TODO
+ // possible optimization: keep unreferenced glyphs as the texture atlas
+ // still has space. only if a new glyph needs to be allocated, and there
+ // is no more space within the atlas, then we can actually remove the glyphs
+ // from the atlasses.
+
+ // remove glyph if no refs anymore
+ if (it.value().deref() <= 0) {
+ QTextureAtlas *atlas = it.value().atlas();
+ it.value().removeFromTextureAtlas();
+
+ // remove atlas, if it contains no glyphs anymore
+ if (atlas && atlas->imageCount() == 0) {
+ Q_ASSERT(m_atlasses.contains(atlas));
+
+ m_atlasses.removeAll(atlas);
+ delete atlas;
+ }
+
+ m_glyphs.erase(it);
+ }
+}
+
+// copied from QSGDistanceFieldGlyphCacheManager::fontKey
+// we use this function to compare QRawFonts, as QRawFont doesn't
+// implement a stable comparison function
+QString QDistanceFieldGlyphCache::fontKey(const QRawFont &font)
+{
+ QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine;
+ if (!fe->faceId().filename.isEmpty()) {
+ QByteArray keyName = fe->faceId().filename;
+ if (font.style() != QFont::StyleNormal)
+ keyName += QByteArray(" I");
+ if (font.weight() != QFont::Normal)
+ keyName += ' ' + QByteArray::number(font.weight());
+ keyName += QByteArray(" DF");
+ return QString::fromUtf8(keyName);
+ } else {
+ return QString::fromLatin1("%1_%2_%3_%4")
+ .arg(font.familyName())
+ .arg(font.styleName())
+ .arg(font.weight())
+ .arg(font.style());
+ }
+}
+
+DistanceFieldFont* QDistanceFieldGlyphCache::getOrCreateDistanceFieldFont(const QRawFont &font)
+{
+ // return, if font already exists (make sure to only create one DistanceFieldFont for
+ // each unique QRawFont, by building a hash on the QRawFont that ignores the font size)
+ const QString key = fontKey(font);
+ const auto it = m_fonts.constFind(key);
+ if (it != m_fonts.cend())
+ return it.value();
+
+ // logic taken from QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache
+ QRawFontPrivate *fontD = QRawFontPrivate::get(font);
+ const int glyphCount = fontD->fontEngine->glyphCount();
+ const bool useDoubleRes = qt_fontHasNarrowOutlines(font) && glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
+
+ // only keep one FontCache with a fixed pixel size for each distinct font type
+ QRawFont actualFont = font;
+ actualFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(useDoubleRes) * QT_DISTANCEFIELD_SCALE(useDoubleRes));
+
+ // create new font cache
+ DistanceFieldFont *dff = new DistanceFieldFont(actualFont, useDoubleRes, m_rootNode);
+ m_fonts.insert(key, dff);
+ return dff;
+}
+
+QDistanceFieldGlyphCache::QDistanceFieldGlyphCache()
+ : m_rootNode(nullptr)
+{
+}
+
+QDistanceFieldGlyphCache::~QDistanceFieldGlyphCache()
+{
+}
+
+void QDistanceFieldGlyphCache::setRootNode(QNode *rootNode)
+{
+ m_rootNode = rootNode;
+}
+
+QNode *QDistanceFieldGlyphCache::rootNode() const
+{
+ return m_rootNode;
+}
+
+bool QDistanceFieldGlyphCache::doubleGlyphResolution(const QRawFont &font)
+{
+ return getOrCreateDistanceFieldFont(font)->doubleGlyphResolution();
+}
+
+namespace {
+QDistanceFieldGlyphCache::Glyph refAndGetGlyph(DistanceFieldFont *dff, quint32 glyph)
+{
+ QDistanceFieldGlyphCache::Glyph ret;
+
+ if (dff) {
+ const auto entry = dff->refGlyph(glyph);
+
+ if (entry.atlas()) {
+ ret.glyphPathBoundingRect = entry.glyphPathBoundingRect();
+ ret.texCoords = entry.texCoords();
+ ret.texture = entry.atlas();
+ }
+ }
+
+ return ret;
+}
+} // anonymous
+
+QVector<QDistanceFieldGlyphCache::Glyph> QDistanceFieldGlyphCache::refGlyphs(const QGlyphRun &run)
+{
+ DistanceFieldFont *dff = getOrCreateDistanceFieldFont(run.rawFont());
+ QVector<QDistanceFieldGlyphCache::Glyph> ret;
+
+ const QVector<quint32> glyphs = run.glyphIndexes();
+ for (quint32 glyph : glyphs)
+ ret << refAndGetGlyph(dff, glyph);
+
+ return ret;
+}
+
+QDistanceFieldGlyphCache::Glyph QDistanceFieldGlyphCache::refGlyph(const QRawFont &font, quint32 glyph)
+{
+ return refAndGetGlyph(getOrCreateDistanceFieldFont(font), glyph);
+}
+
+void QDistanceFieldGlyphCache::derefGlyphs(const QGlyphRun &run)
+{
+ DistanceFieldFont *dff = getOrCreateDistanceFieldFont(run.rawFont());
+
+ const QVector<quint32> glyphs = run.glyphIndexes();
+ for (quint32 glyph : glyphs)
+ dff->derefGlyph(glyph);
+}
+
+void QDistanceFieldGlyphCache::derefGlyph(const QRawFont &font, quint32 glyph)
+{
+ getOrCreateDistanceFieldFont(font)->derefGlyph(glyph);
+}
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
diff --git a/src/extras/text/qdistancefieldglyphcache_p.h b/src/extras/text/qdistancefieldglyphcache_p.h
new file mode 100644
index 000000000..6ca011c76
--- /dev/null
+++ b/src/extras/text/qdistancefieldglyphcache_p.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DEXTRAS_QDISTANCEFIELDGLYPHCACHE_P_H
+#define QT3DEXTRAS_QDISTANCEFIELDGLYPHCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QRectF>
+#include <Qt3DCore/qnode.h>
+#include <Qt3DExtras/qt3dextras_global.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRawFont;
+class QGlyphRun;
+
+namespace Qt3DCore {
+class QNode;
+}
+
+namespace Qt3DRender {
+class QAbstractTexture;
+}
+
+namespace Qt3DExtras {
+
+class DistanceFieldFont;
+class QDistanceFieldGlyphCachePrivate;
+
+class QDistanceFieldGlyphCache
+{
+public:
+ QDistanceFieldGlyphCache();
+ ~QDistanceFieldGlyphCache();
+
+ void setRootNode(Qt3DCore::QNode *rootNode);
+ Qt3DCore::QNode *rootNode() const;
+
+ struct Glyph {
+ Qt3DRender::QAbstractTexture *texture = nullptr;
+ QRectF glyphPathBoundingRect; // bounding rect of the QPainterPath used to draw the glyph
+ QRectF texCoords; // texture coordinates within texture
+ };
+
+ bool doubleGlyphResolution(const QRawFont &font);
+
+ QVector<Glyph> refGlyphs(const QGlyphRun &run);
+ Glyph refGlyph(const QRawFont &font, quint32 glyph);
+
+ void derefGlyphs(const QGlyphRun &run);
+ void derefGlyph(const QRawFont &font, quint32 glyph);
+
+private:
+ DistanceFieldFont* getOrCreateDistanceFieldFont(const QRawFont &font);
+ static QString fontKey(const QRawFont &font);
+
+ QHash<QString, DistanceFieldFont*> m_fonts;
+ Qt3DCore::QNode *m_rootNode;
+};
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
+
+#endif // QT3DEXTRAS_QDISTANCEFIELDGLYPHCACHE_P_H
diff --git a/src/extras/text/qtext2dentity.cpp b/src/extras/text/qtext2dentity.cpp
new file mode 100644
index 000000000..dbd4368bc
--- /dev/null
+++ b/src/extras/text/qtext2dentity.cpp
@@ -0,0 +1,381 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtext2dentity.h"
+#include "qtext2dentity_p.h"
+#include "qtext2dmaterial_p.h"
+
+#include <QtGui/qtextlayout.h>
+#include <QtGui/qglyphrun.h>
+#include <QtGui/private/qdistancefield_p.h>
+#include <QtGui/private/qtextureglyphcache_p.h>
+#include <QtGui/private/qfont_p.h>
+#include <QtGui/private/qdistancefield_p.h>
+
+#include <Qt3DRender/qmaterial.h>
+#include <Qt3DRender/qbuffer.h>
+#include <Qt3DRender/qattribute.h>
+#include <Qt3DRender/qgeometry.h>
+#include <Qt3DRender/qgeometryrenderer.h>
+
+#include <Qt3DCore/private/qscene_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+inline Q_DECL_CONSTEXPR QRectF scaleRectF(const QRectF &rect, float scale)
+{
+ return QRectF(rect.left() * scale, rect.top() * scale, rect.width() * scale, rect.height() * scale);
+}
+
+} // anonymous
+
+namespace Qt3DExtras {
+
+QHash<Qt3DCore::QScene *, QText2DEntityPrivate::CacheEntry> QText2DEntityPrivate::m_glyphCacheInstances;
+
+QText2DEntityPrivate::QText2DEntityPrivate()
+ : m_glyphCache(nullptr)
+ , m_font(QLatin1String("Times"), 10)
+ , m_scaledFont(QLatin1String("Times"), 10)
+ , m_color(QColor(255, 255, 255, 255))
+ , m_width(0.0f)
+ , m_height(0.0f)
+{
+}
+
+QText2DEntityPrivate::~QText2DEntityPrivate()
+{
+}
+
+void QText2DEntityPrivate::setScene(Qt3DCore::QScene *scene)
+{
+ if (scene == m_scene)
+ return;
+
+ // Unref old glyph cache if it exists
+ if (m_scene != nullptr) {
+ m_glyphCache = nullptr;
+ QText2DEntityPrivate::CacheEntry &entry = QText2DEntityPrivate::m_glyphCacheInstances[m_scene];
+ --entry.count;
+ if (entry.count == 0 && entry.glyphCache != nullptr) {
+ delete entry.glyphCache;
+ entry.glyphCache = nullptr;
+ }
+ }
+
+ QEntityPrivate::setScene(scene);
+
+ // Ref new glyph cache is scene is valid
+ if (scene != nullptr) {
+ QText2DEntityPrivate::CacheEntry &entry = QText2DEntityPrivate::m_glyphCacheInstances[scene];
+ if (entry.glyphCache == nullptr) {
+ entry.glyphCache = new QDistanceFieldGlyphCache();
+ entry.glyphCache->setRootNode(scene->rootNode());
+ }
+ m_glyphCache = entry.glyphCache;
+ ++entry.count;
+ // Update to populate glyphCache if needed
+ update();
+ }
+}
+
+QText2DEntity::QText2DEntity(QNode *parent)
+ : Qt3DCore::QEntity(*new QText2DEntityPrivate(), parent)
+{
+}
+
+QText2DEntity::~QText2DEntity()
+{
+}
+
+float QText2DEntityPrivate::computeActualScale() const
+{
+ // scale font based on fontScale property and given QFont
+ float scale = 1.0f;
+ if (m_font.pointSizeF() > 0)
+ scale *= m_font.pointSizeF() / m_scaledFont.pointSizeF();
+ return scale;
+}
+
+struct RenderData {
+ int vertexCount = 0;
+ QVector<float> vertex;
+ QVector<quint16> index;
+};
+
+void QText2DEntityPrivate::setCurrentGlyphRuns(const QVector<QGlyphRun> &runs)
+{
+ if (runs.isEmpty())
+ return;
+
+ // For each distinct texture, we need a separate DistanceFieldTextRenderer,
+ // for which we need vertex and index data
+ QHash<Qt3DRender::QAbstractTexture*, RenderData> renderData;
+
+ const float scale = computeActualScale();
+
+ // process glyph runs
+ for (const QGlyphRun &run : runs) {
+ const QVector<quint32> glyphs = run.glyphIndexes();
+ const QVector<QPointF> pos = run.positions();
+
+ Q_ASSERT(glyphs.size() == pos.size());
+
+ const bool doubleGlyphResolution = m_glyphCache->doubleGlyphResolution(run.rawFont());
+
+ // faithfully copied from QSGDistanceFieldGlyphNode::updateGeometry()
+ const float pixelSize = run.rawFont().pixelSize();
+ const float fontScale = pixelSize / QT_DISTANCEFIELD_BASEFONTSIZE(doubleGlyphResolution);
+ const float margin = QT_DISTANCEFIELD_RADIUS(doubleGlyphResolution) / QT_DISTANCEFIELD_SCALE(doubleGlyphResolution) * fontScale;
+
+ for (int i = 0; i < glyphs.size(); i++) {
+ const QDistanceFieldGlyphCache::Glyph &dfield = m_glyphCache->refGlyph(run.rawFont(), glyphs[i]);
+
+ if (!dfield.texture)
+ continue;
+
+ RenderData &data = renderData[dfield.texture];
+
+ // faithfully copied from QSGDistanceFieldGlyphNode::updateGeometry()
+ QRectF metrics = scaleRectF(dfield.glyphPathBoundingRect, fontScale);
+ metrics.adjust(-margin, margin, margin, 3*margin);
+
+ const float top = 0.0f;
+ const float left = 0.0f;
+ const float right = m_width;
+ const float bottom = m_height;
+
+ float x1 = left + scale * (pos[i].x() + metrics.left());
+ float y2 = bottom - scale * (pos[i].y() - metrics.top());
+ float x2 = x1 + scale * metrics.width();
+ float y1 = y2 - scale * metrics.height();
+
+ // only draw glyphs that are at least partly visible
+ if (y2 < top || x1 > right)
+ continue;
+
+ QRectF texCoords = dfield.texCoords;
+
+ // if a glyph is only partly visible within the given rectangle,
+ // cut it in half and adjust tex coords
+ if (y1 < top) {
+ const float insideRatio = (top - y2) / (y1 - y2);
+ y1 = top;
+ texCoords.setHeight(texCoords.height() * insideRatio);
+ }
+
+ // do the same thing horizontally
+ if (x2 > right) {
+ const float insideRatio = (right - x1) / (x2 - x1);
+ x2 = right;
+ texCoords.setWidth(texCoords.width() * insideRatio);
+ }
+
+ data.vertex << x1 << y1 << 0.f << texCoords.left() << texCoords.bottom();
+ data.vertex << x1 << y2 << 0.f << texCoords.left() << texCoords.top();
+ data.vertex << x2 << y1 << 0.f << texCoords.right() << texCoords.bottom();
+ data.vertex << x2 << y2 << 0.f << texCoords.right() << texCoords.top();
+
+ data.index << data.vertexCount << data.vertexCount+3 << data.vertexCount+1;
+ data.index << data.vertexCount << data.vertexCount+2 << data.vertexCount+3;
+
+ data.vertexCount += 4;
+ }
+ }
+
+ // make sure we have the correct number of DistanceFieldTextRenderers
+ // TODO: we might keep one renderer at all times, so we won't delete and
+ // re-allocate one every time the text changes from an empty to a non-empty string
+ // and vice-versa
+ while (m_renderers.size() > renderData.size())
+ delete m_renderers.takeLast();
+
+ while (m_renderers.size() < renderData.size()) {
+ DistanceFieldTextRenderer *renderer = new DistanceFieldTextRenderer(q_func());
+ renderer->setColor(m_color);
+ m_renderers << renderer;
+ }
+
+ Q_ASSERT(m_renderers.size() == renderData.size());
+
+ // assign vertex data for all textures to the renderers
+ int rendererIdx = 0;
+ for (auto it = renderData.begin(); it != renderData.end(); ++it) {
+ m_renderers[rendererIdx++]->setGlyphData(it.key(), it.value().vertex, it.value().index);
+ }
+
+ // de-ref all glyphs for previous QGlyphRuns
+ for (int i = 0; i < m_currentGlyphRuns.size(); i++)
+ m_glyphCache->derefGlyphs(m_currentGlyphRuns[i]);
+ m_currentGlyphRuns = runs;
+}
+
+void QText2DEntityPrivate::update()
+{
+ if (m_glyphCache == nullptr)
+ return;
+
+ QVector<QGlyphRun> glyphRuns;
+
+ // collect all GlyphRuns generated by the QTextLayout
+ if ((m_width > 0.0f || m_height > 0.0f) && !m_text.isEmpty()) {
+ QTextLayout layout(m_text, m_scaledFont);
+ const float lineWidth = m_width / computeActualScale();
+ float height = 0;
+ layout.beginLayout();
+
+ while (true) {
+ QTextLine line = layout.createLine();
+ if (!line.isValid())
+ break;
+
+ // position current line
+ line.setLineWidth(lineWidth);
+ line.setPosition(QPointF(0, height));
+ height += line.height();
+
+ // add glyph runs created by line
+ const QList<QGlyphRun> runs = line.glyphRuns();
+ for (const QGlyphRun &run : runs)
+ glyphRuns << run;
+ }
+
+ layout.endLayout();
+ }
+
+ setCurrentGlyphRuns(glyphRuns);
+}
+
+QFont QText2DEntity::font() const
+{
+ Q_D(const QText2DEntity);
+ return d->m_font;
+}
+
+void QText2DEntity::setFont(const QFont &font)
+{
+ Q_D(QText2DEntity);
+ if (d->m_font != font) {
+ // ignore the point size of the font, just make it a default value.
+ // still we want to make sure that font() returns the same value
+ // that was passed to setFont(), so we store it nevertheless
+ d->m_font = font;
+ d->m_scaledFont = font;
+ d->m_scaledFont.setPointSize(10);
+
+ emit fontChanged(font);
+
+ if (!d->m_text.isEmpty())
+ d->update();
+ }
+}
+
+QColor QText2DEntity::color() const
+{
+ Q_D(const QText2DEntity);
+ return d->m_color;
+}
+
+void QText2DEntity::setColor(const QColor &color)
+{
+ Q_D(QText2DEntity);
+ if (d->m_color != color) {
+ d->m_color = color;
+
+ emit colorChanged(color);
+
+ for (DistanceFieldTextRenderer *renderer : qAsConst(d->m_renderers))
+ renderer->setColor(color);
+ }
+}
+
+QString QText2DEntity::text() const
+{
+ Q_D(const QText2DEntity);
+ return d->m_text;
+}
+
+void QText2DEntity::setText(const QString &text)
+{
+ Q_D(QText2DEntity);
+ if (d->m_text != text) {
+ d->m_text = text;
+ emit textChanged(text);
+
+ d->update();
+ }
+}
+
+float QText2DEntity::width() const
+{
+ Q_D(const QText2DEntity);
+ return d->m_width;
+}
+
+float QText2DEntity::height() const
+{
+ Q_D(const QText2DEntity);
+ return d->m_height;
+}
+
+void QText2DEntity::setWidth(float width)
+{
+ Q_D(QText2DEntity);
+ if (width != d->m_width) {
+ d->m_width = width;
+ emit widthChanged(width);
+ d->update();
+ }
+}
+
+void QText2DEntity::setHeight(float height)
+{
+ Q_D(QText2DEntity);
+ if (height != d->m_height) {
+ d->m_height = height;
+ emit heightChanged(height);
+ d->update();
+ }
+}
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
diff --git a/src/extras/text/qtext2dentity.h b/src/extras/text/qtext2dentity.h
new file mode 100644
index 000000000..39be91a0f
--- /dev/null
+++ b/src/extras/text/qtext2dentity.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DEXTRAS_QTEXT2DENTITY_H
+#define QT3DEXTRAS_QTEXT2DENTITY_H
+
+#include <QtCore/qrect.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qfont.h>
+#include <Qt3DCore/qentity.h>
+#include <Qt3DExtras/qt3dextras_global.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DExtras {
+
+class QText2DEntityPrivate;
+
+class QT3DEXTRASSHARED_EXPORT QText2DEntity : public Qt3DCore::QEntity
+{
+ Q_OBJECT
+ Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
+ Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+ Q_PROPERTY(float width READ width WRITE setWidth NOTIFY widthChanged)
+ Q_PROPERTY(float height READ height WRITE setHeight NOTIFY heightChanged)
+
+public:
+ explicit QText2DEntity(Qt3DCore::QNode *parent = nullptr);
+ ~QText2DEntity();
+
+ QFont font() const;
+ void setFont(const QFont &font);
+
+ QColor color() const;
+ void setColor(const QColor &color);
+
+ QString text() const;
+ void setText(const QString &text);
+
+ float width() const;
+ float height() const;
+
+ void setWidth(float width);
+ void setHeight(float height);
+
+Q_SIGNALS:
+ void fontChanged(const QFont &font);
+ void colorChanged(const QColor &color);
+ void textChanged(const QString &text);
+ void widthChanged(float width);
+ void heightChanged(float height);
+
+private:
+ Q_DECLARE_PRIVATE(QText2DEntity)
+};
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
+
+#endif // QT3DEXTRAS_QTEXT2DENTITY_H
diff --git a/src/extras/text/qtext2dentity_p.h b/src/extras/text/qtext2dentity_p.h
new file mode 100644
index 000000000..863431091
--- /dev/null
+++ b/src/extras/text/qtext2dentity_p.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DEXTRAS_QTEXT2DENTITY_P_H
+#define QT3DEXTRAS_QTEXT2DENTITY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/private/qentity_p.h>
+#include <Qt3DExtras/private/distancefieldtextrenderer_p.h>
+#include <Qt3DExtras/private/qdistancefieldglyphcache_p.h>
+#include <QFont>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+class QScene;
+}
+
+namespace Qt3DRender {
+class QGeometryRenderer;
+class QGeometry;
+class QMaterial;
+class QAttribute;
+class QBuffer;
+}
+
+namespace Qt3DExtras {
+
+class QText2DMaterial;
+class QText2DEntity;
+
+class QText2DEntityPrivate : public Qt3DCore::QEntityPrivate
+{
+public:
+ QText2DEntityPrivate();
+ ~QText2DEntityPrivate();
+
+ Q_DECLARE_PUBLIC(QText2DEntity)
+
+ // keep track of the glyphs currently being displayed,
+ // to guarantee proper glyph ref-counting in the
+ // QDistanceFieldGlyphCache
+ QVector<QGlyphRun> m_currentGlyphRuns;
+ QDistanceFieldGlyphCache *m_glyphCache;
+
+ void setScene(Qt3DCore::QScene *scene) Q_DECL_OVERRIDE;
+
+ QFont m_font;
+ QFont m_scaledFont; // ignore point or pixel size, set to default value
+
+ QColor m_color;
+ QString m_text;
+ float m_width;
+ float m_height;
+
+ QVector<DistanceFieldTextRenderer*> m_renderers;
+
+ float computeActualScale() const;
+
+ void setCurrentGlyphRuns(const QVector<QGlyphRun> &runs);
+ void update();
+
+ struct CacheEntry
+ {
+ QDistanceFieldGlyphCache *glyphCache = nullptr;
+ int count = 0;
+ };
+
+ static QHash<Qt3DCore::QScene *, CacheEntry> m_glyphCacheInstances;
+};
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
+
+#endif // QT3DEXTRAS_QTEXT2DENTITY_P_H
diff --git a/src/extras/text/qtext2dmaterial.cpp b/src/extras/text/qtext2dmaterial.cpp
new file mode 100644
index 000000000..d8bf312c4
--- /dev/null
+++ b/src/extras/text/qtext2dmaterial.cpp
@@ -0,0 +1,179 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtext2dmaterial_p.h"
+#include "qtext2dmaterial_p_p.h"
+#include <Qt3DRender/qfilterkey.h>
+#include <Qt3DRender/qmaterial.h>
+#include <Qt3DRender/qeffect.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DRender/qshaderprogram.h>
+#include <Qt3DRender/qparameter.h>
+#include <Qt3DRender/qrenderpass.h>
+#include <Qt3DRender/qgraphicsapifilter.h>
+#include <Qt3DRender/qblendequation.h>
+#include <Qt3DRender/qblendequationarguments.h>
+#include <Qt3DRender/qdepthtest.h>
+#include <Qt3DRender/qabstracttexture.h>
+#include <QUrl>
+#include <QVector3D>
+#include <QVector4D>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DExtras {
+
+QText2DMaterialPrivate::QText2DMaterialPrivate()
+ : QMaterialPrivate()
+ , m_effect(new Qt3DRender::QEffect())
+ , m_distanceFieldTexture(nullptr)
+ , m_textureParameter(new Qt3DRender::QParameter(QStringLiteral("distanceFieldTexture"), QVariant(0)))
+ , m_textureSizeParameter(new Qt3DRender::QParameter(QStringLiteral("textureSize"), QVariant(256.f)))
+ , m_colorParameter(new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(255, 255, 255, 255))))
+ , m_gl3Technique(new Qt3DRender::QTechnique())
+ , m_gl2Technique(new Qt3DRender::QTechnique())
+ , m_es2Technique(new Qt3DRender::QTechnique())
+ , m_gl3RenderPass(new Qt3DRender::QRenderPass())
+ , m_gl2RenderPass(new Qt3DRender::QRenderPass())
+ , m_es2RenderPass(new Qt3DRender::QRenderPass())
+ , m_gl3ShaderProgram(new Qt3DRender::QShaderProgram())
+ , m_gl2es2ShaderProgram(new Qt3DRender::QShaderProgram())
+ , m_blend(new Qt3DRender::QBlendEquation())
+ , m_blendArgs(new Qt3DRender::QBlendEquationArguments())
+ , m_depthTest(new Qt3DRender::QDepthTest())
+{
+}
+
+void QText2DMaterialPrivate::init()
+{
+ m_gl3ShaderProgram->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/gl3/distancefieldtext.vert"))));
+ m_gl3ShaderProgram->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/gl3/distancefieldtext.frag"))));
+
+ m_gl2es2ShaderProgram->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/es2/distancefieldtext.vert"))));
+ m_gl2es2ShaderProgram->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/es2/distancefieldtext.frag"))));
+
+ m_blend->setBlendFunction(Qt3DRender::QBlendEquation::Add);
+ m_blendArgs->setSourceRgba(Qt3DRender::QBlendEquationArguments::SourceAlpha);
+ m_blendArgs->setDestinationRgba(Qt3DRender::QBlendEquationArguments::OneMinusSourceAlpha);
+ m_depthTest->setDepthFunction(Qt3DRender::QDepthTest::LessOrEqual);
+
+ m_gl3RenderPass->setShaderProgram(m_gl3ShaderProgram);
+ m_gl3RenderPass->addRenderState(m_blend);
+ m_gl3RenderPass->addRenderState(m_blendArgs);
+ m_gl3RenderPass->addRenderState(m_depthTest);
+
+ m_gl2RenderPass->setShaderProgram(m_gl2es2ShaderProgram);
+ m_gl2RenderPass->addRenderState(m_blend);
+ m_gl2RenderPass->addRenderState(m_blendArgs);
+ m_gl2RenderPass->addRenderState(m_depthTest);
+
+ m_es2RenderPass->setShaderProgram(m_gl2es2ShaderProgram);
+ m_es2RenderPass->addRenderState(m_blend);
+ m_es2RenderPass->addRenderState(m_blendArgs);
+ m_es2RenderPass->addRenderState(m_depthTest);
+
+ m_gl3Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL);
+ m_gl3Technique->graphicsApiFilter()->setMajorVersion(3);
+ m_gl3Technique->graphicsApiFilter()->setMinorVersion(1);
+ m_gl3Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile);
+ m_gl3Technique->addRenderPass(m_gl3RenderPass);
+
+ m_gl2Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL);
+ m_gl2Technique->graphicsApiFilter()->setMajorVersion(2);
+ m_gl2Technique->graphicsApiFilter()->setMinorVersion(0);
+ m_gl2Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile);
+ m_gl2Technique->addRenderPass(m_gl2RenderPass);
+
+ m_es2Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGLES);
+ m_es2Technique->graphicsApiFilter()->setMajorVersion(2);
+ m_es2Technique->graphicsApiFilter()->setMinorVersion(0);
+ m_es2Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile);
+ m_es2Technique->addRenderPass(m_es2RenderPass);
+
+ Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey(q_func());
+ filterKey->setName(QStringLiteral("renderingStyle"));
+ filterKey->setValue(QStringLiteral("forward"));
+ m_gl3Technique->addFilterKey(filterKey);
+ m_gl2Technique->addFilterKey(filterKey);
+ m_es2Technique->addFilterKey(filterKey);
+
+ m_effect->addTechnique(m_gl3Technique);
+ m_effect->addTechnique(m_gl2Technique);
+ m_effect->addTechnique(m_es2Technique);
+ m_effect->addParameter(m_textureParameter);
+ m_effect->addParameter(m_textureSizeParameter);
+ m_effect->addParameter(m_colorParameter);
+
+ q_func()->setEffect(m_effect);
+}
+
+QText2DMaterial::QText2DMaterial(Qt3DCore::QNode *parent)
+ : QMaterial(*new QText2DMaterialPrivate(), parent)
+{
+ Q_D(QText2DMaterial);
+ d->init();
+}
+
+QText2DMaterial::~QText2DMaterial()
+{
+}
+
+void QText2DMaterial::setColor(const QColor &color)
+{
+ Q_D(QText2DMaterial);
+ d->m_colorParameter->setValue(QVariant::fromValue(color));
+}
+
+void QText2DMaterial::setDistanceFieldTexture(Qt3DRender::QAbstractTexture *tex)
+{
+ Q_D(QText2DMaterial);
+ d->m_distanceFieldTexture = tex;
+
+ if (tex) {
+ d->m_textureParameter->setValue(QVariant::fromValue(tex));
+ d->m_textureSizeParameter->setValue(QVariant::fromValue(static_cast<float>(tex->width())));
+ } else {
+ d->m_textureParameter->setValue(0);
+ d->m_textureSizeParameter->setValue(QVariant::fromValue(1.f));
+ }
+}
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
diff --git a/src/extras/text/qtext2dmaterial_p.h b/src/extras/text/qtext2dmaterial_p.h
new file mode 100644
index 000000000..cf967b02b
--- /dev/null
+++ b/src/extras/text/qtext2dmaterial_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DEXTRAS_QTEXT2DMATERIAL_P_H
+#define QT3DEXTRAS_QTEXT2DMATERIAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DExtras/qt3dextras_global.h>
+#include <Qt3DRender/qmaterial.h>
+#include <QColor>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DExtras {
+
+class QText2DMaterialPrivate;
+
+class QText2DMaterial : public Qt3DRender::QMaterial
+{
+ Q_OBJECT
+
+public:
+ explicit QText2DMaterial(Qt3DCore::QNode *parent = nullptr);
+ ~QText2DMaterial();
+
+ void setColor(const QColor &color);
+ void setDistanceFieldTexture(Qt3DRender::QAbstractTexture *tex);
+
+private:
+ Q_DECLARE_PRIVATE(QText2DMaterial)
+};
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
+
+#endif // QT3DEXTRAS_QTEXT2DMATERIAL_P_H
diff --git a/src/extras/text/qtext2dmaterial_p_p.h b/src/extras/text/qtext2dmaterial_p_p.h
new file mode 100644
index 000000000..90f0a71f1
--- /dev/null
+++ b/src/extras/text/qtext2dmaterial_p_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DEXTRAS_QTEXT2DMATERIAL_P_P_H
+#define QT3DEXTRAS_QTEXT2DMATERIAL_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/private/qmaterial_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+class QAbstractTexture;
+class QEffect;
+class QTechnique;
+class QParameter;
+class QRenderPass;
+class QShaderProgram;
+class QBlendEquation;
+class QBlendEquationArguments;
+class QDepthTest;
+
+} // namespace Qt3DRender
+
+namespace Qt3DExtras {
+
+class QText2DMaterial;
+
+class QText2DMaterialPrivate : public Qt3DRender::QMaterialPrivate
+{
+public:
+ QText2DMaterialPrivate();
+
+ Qt3DRender::QEffect *m_effect;
+ Qt3DRender::QAbstractTexture *m_distanceFieldTexture;
+ Qt3DRender::QParameter *m_textureParameter;
+ Qt3DRender::QParameter *m_textureSizeParameter;
+ Qt3DRender::QParameter *m_colorParameter;
+ Qt3DRender::QTechnique *m_gl3Technique;
+ Qt3DRender::QTechnique *m_gl2Technique;
+ Qt3DRender::QTechnique *m_es2Technique;
+ Qt3DRender::QRenderPass *m_gl3RenderPass;
+ Qt3DRender::QRenderPass *m_gl2RenderPass;
+ Qt3DRender::QRenderPass *m_es2RenderPass;
+ Qt3DRender::QShaderProgram *m_gl3ShaderProgram;
+ Qt3DRender::QShaderProgram *m_gl2es2ShaderProgram;
+ Qt3DRender::QBlendEquation *m_blend;
+ Qt3DRender::QBlendEquationArguments *m_blendArgs;
+ Qt3DRender::QDepthTest *m_depthTest;
+
+ void init();
+
+ Q_DECLARE_PUBLIC(QText2DMaterial)
+};
+
+} // Qt3DExtras
+
+QT_END_NAMESPACE
+
+#endif // QT3DEXTRAS_QTEXT2DMATERIAL_P_P_H
diff --git a/src/extras/text/qtextureatlas.cpp b/src/extras/text/qtextureatlas.cpp
new file mode 100644
index 000000000..2b82010a6
--- /dev/null
+++ b/src/extras/text/qtextureatlas.cpp
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextureatlas_p.h"
+#include "qtextureatlas_p_p.h"
+#include <Qt3DRender/qtexturedata.h>
+#include <Qt3DRender/qabstracttextureimage.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+#include <Qt3DCore/qpropertynodeaddedchange.h>
+#include <Qt3DCore/qpropertynoderemovedchange.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt3DCore;
+
+namespace Qt3DExtras {
+
+QTextureAtlasData::QTextureAtlasData(int w, int h, QImage::Format fmt)
+ : m_image(w, h, fmt)
+{
+}
+
+QTextureAtlasData::~QTextureAtlasData()
+{
+}
+
+void QTextureAtlasData::addImage(const AtlasTexture &texture, const QImage &image)
+{
+ QMutexLocker lock(&m_mutex);
+
+ Update update;
+ update.textureInfo = texture;
+ update.image = image;
+ m_updates << update;
+}
+
+QByteArray QTextureAtlasData::createUpdatedImageData()
+{
+ m_mutex.lock();
+ const QVector<Update> updates = std::move(m_updates);
+ m_mutex.unlock();
+
+ // copy sub-images into the actual texture image
+ for (const Update &update : updates) {
+ const QImage &image = update.image;
+
+ const int padding = update.textureInfo.padding;
+ const QRect imgRect = update.textureInfo.position;
+ const QRect alloc = imgRect.adjusted(-padding, -padding, padding, padding);
+
+ // bytes per pixel
+ if (image.depth() != m_image.depth()) {
+ qWarning() << "[QTextureAtlas] Image depth does not match. Original =" << m_image.depth() << ", Sub-Image =" << image.depth();
+ continue;
+ }
+ int bpp = image.depth() / 8;
+
+ // copy image contents into texture image
+ // use image border pixels to fill the padding region
+ for (int y = alloc.top(); y <= alloc.bottom(); y++) {
+ const int ySrc = qBound(0, y - imgRect.top(), image.height()-1);
+
+ const uchar *srcLine = image.scanLine(ySrc);
+ const uchar *srcLastPx = &srcLine[bpp * (image.width()-1)];
+
+ uchar *dstLine = m_image.scanLine(y);
+
+ uchar *dstPadL = &dstLine[bpp * alloc.left()];
+ uchar *dstPadR = &dstLine[bpp * imgRect.right()];
+ uchar *dstImg = &dstLine[bpp * imgRect.left()];
+
+ // copy left and right padding pixels
+ for (int pad = 0; pad < padding; pad++) {
+ for (int px = 0; px < bpp; px++) {
+ dstPadL[bpp * pad + px] = srcLine[px];
+ dstPadR[bpp * pad + px] = srcLastPx[px];
+ }
+ }
+
+ // copy image scanline
+ memcpy(dstImg, srcLine, bpp * imgRect.width());
+ }
+ }
+
+ return QByteArray(reinterpret_cast<const char*>(m_image.constBits()), m_image.byteCount());
+}
+
+QTextureAtlasPrivate::QTextureAtlasPrivate()
+ : Qt3DRender::QAbstractTexturePrivate()
+{
+ m_target = Qt3DRender::QAbstractTexture::TargetAutomatic;
+ m_format = Qt3DRender::QAbstractTexture::RGBA8_UNorm;
+ m_width = 256;
+ m_height = 256;
+ m_depth = 1;
+}
+
+QTextureAtlasPrivate::~QTextureAtlasPrivate()
+{
+}
+
+QTextureAtlasGenerator::QTextureAtlasGenerator(const QTextureAtlasPrivate *texAtlas)
+ : m_data(texAtlas->m_data)
+ , m_format(texAtlas->m_format)
+ , m_pixelFormat(texAtlas->m_pixelFormat)
+ , m_generation(texAtlas->m_currGen)
+ , m_atlasId(texAtlas->m_id)
+{
+}
+
+QTextureAtlasGenerator::~QTextureAtlasGenerator()
+{
+}
+
+Qt3DRender::QTextureDataPtr QTextureAtlasGenerator::operator()()
+{
+ Qt3DRender::QTextureImageDataPtr texImage = Qt3DRender::QTextureImageDataPtr::create();
+ texImage->setTarget(QOpenGLTexture::Target2D);
+ texImage->setWidth(m_data->width());
+ texImage->setHeight(m_data->height());
+ texImage->setDepth(1);
+ texImage->setFaces(1);
+ texImage->setLayers(1);
+ texImage->setMipLevels(1);
+ texImage->setFormat(static_cast<QOpenGLTexture::TextureFormat>(m_format));
+ texImage->setPixelFormat(m_pixelFormat);
+ texImage->setPixelType(QOpenGLTexture::UInt8);
+
+ const QByteArray bytes = m_data->createUpdatedImageData();
+ texImage->setData(bytes, 1);
+
+ Qt3DRender::QTextureDataPtr generatedData = Qt3DRender::QTextureDataPtr::create();
+ generatedData->setTarget(Qt3DRender::QAbstractTexture::Target2D);
+ generatedData->setFormat(m_format);
+ generatedData->setWidth(m_data->width());
+ generatedData->setHeight(m_data->height());
+ generatedData->setDepth(1);
+ generatedData->setLayers(1);
+ generatedData->addImageData(texImage);
+
+ return generatedData;
+}
+
+bool QTextureAtlasGenerator::operator==(const QTextureGenerator &other) const
+{
+ const QTextureAtlasGenerator *otherFunctor = Qt3DRender::functor_cast<QTextureAtlasGenerator>(&other);
+ return (otherFunctor != nullptr
+ && otherFunctor->m_data == m_data
+ && otherFunctor->m_atlasId == m_atlasId
+ && otherFunctor->m_generation == m_generation);
+}
+
+QTextureAtlas::QTextureAtlas(Qt3DCore::QNode *parent)
+ : QAbstractTexture(*new QTextureAtlasPrivate(), parent)
+{
+}
+
+QOpenGLTexture::PixelFormat QTextureAtlas::pixelFormat() const
+{
+ Q_D(const QTextureAtlas);
+ return d->m_pixelFormat;
+}
+
+void QTextureAtlas::setPixelFormat(QOpenGLTexture::PixelFormat fmt)
+{
+ Q_D(QTextureAtlas);
+ d->m_pixelFormat = fmt;
+}
+
+QTextureAtlas::~QTextureAtlas()
+{
+}
+
+QTextureAtlas::TextureId QTextureAtlas::addImage(const QImage &image, int padding)
+{
+ Q_D(QTextureAtlas);
+
+ // lazily create image and allocator to allow setWidth/setHeight after object construction
+ if (!d->m_allocator) {
+ Q_ASSERT(d->m_data.isNull());
+
+ d->m_allocator.reset(new AreaAllocator(QSize(width(), height())));
+ d->m_data = QTextureAtlasDataPtr::create(width(), height(), image.format());
+ }
+
+ const QSize allocSz = image.size() + QSize(2 * padding, 2 * padding);
+
+ // try to allocate space within image space
+ const QRect alloc = d->m_allocator->allocate(allocSz);
+ if (alloc.isEmpty())
+ return InvalidTexture;
+
+ const QRect imgRect = alloc.adjusted(padding, padding, -padding, -padding);
+ AtlasTexture tex;
+ tex.position = imgRect;
+ tex.padding = padding;
+
+ // store texture
+ TextureId id = d->m_currId++;
+ d->m_textures[id] = tex;
+ d->m_data->addImage(tex, image);
+
+ // update data functor
+ d->m_currGen++;
+ d->setDataFunctor(QTextureAtlasGeneratorPtr::create(d));
+
+ return id;
+}
+
+void QTextureAtlas::removeImage(TextureId id)
+{
+ Q_D(QTextureAtlas);
+ auto it = d->m_textures.find(id);
+ if (it != d->m_textures.end()) {
+ QRect imgRect = it->position;
+ imgRect.adjust(-it->padding, -it->padding, 2*it->padding, 2*it->padding);
+
+ if (d->m_allocator)
+ d->m_allocator->deallocate(imgRect);
+ d->m_textures.erase(it);
+ }
+}
+
+bool QTextureAtlas::hasImage(TextureId id) const
+{
+ Q_D(const QTextureAtlas);
+ return d->m_textures.contains(id);
+}
+
+int QTextureAtlas::imageCount() const
+{
+ Q_D(const QTextureAtlas);
+ return d->m_textures.size();
+}
+
+QRect QTextureAtlas::imagePosition(TextureId id) const
+{
+ Q_D(const QTextureAtlas);
+ const auto it = d->m_textures.find(id);
+ return (it != d->m_textures.cend()) ? it->position : QRect();
+}
+
+QRectF QTextureAtlas::imageTexCoords(TextureId id) const
+{
+ Q_D(const QTextureAtlas);
+ const auto it = d->m_textures.find(id);
+ if (it != d->m_textures.cend()) {
+ const float w = d->m_data->width();
+ const float h = d->m_data->height();
+ return QRectF(static_cast<float>(it->position.x()) / w,
+ static_cast<float>(it->position.y()) / h,
+ static_cast<float>(it->position.width()) / w,
+ static_cast<float>(it->position.height()) / h);
+ }
+ return QRectF();
+}
+
+int QTextureAtlas::imagePadding(TextureId id) const
+{
+ Q_D(const QTextureAtlas);
+ const auto it = d->m_textures.find(id);
+ return (it != d->m_textures.cend()) ? it->padding : -1;
+}
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
diff --git a/src/extras/text/qtextureatlas_p.h b/src/extras/text/qtextureatlas_p.h
new file mode 100644
index 000000000..8dc9e19b3
--- /dev/null
+++ b/src/extras/text/qtextureatlas_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DEXTRAS_QTEXTUREATLAS_P_H
+#define QT3DEXTRAS_QTEXTUREATLAS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DExtras/qt3dextras_global.h>
+#include <Qt3DRender/qabstracttexture.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DExtras {
+
+class QTextureAtlasPrivate;
+
+class QTextureAtlas : public Qt3DRender::QAbstractTexture
+{
+ Q_OBJECT
+
+public:
+ typedef int TextureId;
+ static Q_CONSTEXPR TextureId InvalidTexture = -1;
+
+ QTextureAtlas(Qt3DCore::QNode *parent = nullptr);
+ ~QTextureAtlas();
+
+ QOpenGLTexture::PixelFormat pixelFormat() const;
+ void setPixelFormat(QOpenGLTexture::PixelFormat fmt);
+
+ TextureId addImage(const QImage &image, int padding);
+ void removeImage(TextureId id);
+
+ int imageCount() const;
+
+ bool hasImage(TextureId id) const;
+ QRect imagePosition(TextureId id) const;
+ QRectF imageTexCoords(TextureId id) const;
+ int imagePadding(TextureId id) const;
+
+private:
+ Q_DECLARE_PRIVATE(QTextureAtlas)
+};
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
+
+#endif // QT3DEXTRAS_QTEXTUREATLAS_P_H
diff --git a/src/extras/text/qtextureatlas_p_p.h b/src/extras/text/qtextureatlas_p_p.h
new file mode 100644
index 000000000..ca18a263a
--- /dev/null
+++ b/src/extras/text/qtextureatlas_p_p.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DEXTRAS_QTEXTUREATLAS_P_P_H
+#define QT3DEXTRAS_QTEXTUREATLAS_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qscopedpointer.h>
+#include <Qt3DRender/private/qabstracttexture_p.h>
+#include <Qt3DRender/qtexturegenerator.h>
+#include <Qt3DExtras/private/areaallocator_p.h>
+#include <Qt3DExtras/private/qtextureatlas_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DExtras {
+
+// Used to store texture info within atlas
+struct AtlasTexture
+{
+ QRect position;
+ int padding = 0;
+};
+
+// data shared between QTextureAtlasPrivate and the QTextureGenerators
+// we use this extra indirection so we can lazily copy the sub-images
+// into the actual texture image in the backend texture loader thread.
+class QTextureAtlasData
+{
+public:
+ QTextureAtlasData(int w, int h, QImage::Format fmt);
+ ~QTextureAtlasData();
+
+ int width() const { return m_image.width(); }
+ int height() const { return m_image.height(); }
+
+ void addImage(const AtlasTexture &texture, const QImage &image);
+ QByteArray createUpdatedImageData();
+
+private:
+ struct Update {
+ AtlasTexture textureInfo;
+ QImage image;
+ };
+
+ QMutex m_mutex;
+ QImage m_image;
+ QVector<Update> m_updates;
+};
+
+typedef QSharedPointer<QTextureAtlasData> QTextureAtlasDataPtr;
+
+class QTextureAtlasPrivate : public Qt3DRender::QAbstractTexturePrivate
+{
+public:
+ QTextureAtlasPrivate();
+ ~QTextureAtlasPrivate();
+
+ Q_DECLARE_PUBLIC(QTextureAtlas)
+
+ QTextureAtlas::TextureId m_currId = 1; // IDs for new sub-textures
+ int m_currGen = 0;
+
+ QTextureAtlasDataPtr m_data;
+ QScopedPointer<AreaAllocator> m_allocator;
+ QOpenGLTexture::PixelFormat m_pixelFormat;
+ QHash<QTextureAtlas::TextureId, AtlasTexture> m_textures;
+};
+
+class QTextureAtlasGenerator : public Qt3DRender::QTextureGenerator
+{
+public:
+ QTextureAtlasGenerator(const QTextureAtlasPrivate *texAtlas);
+ ~QTextureAtlasGenerator();
+ Qt3DRender::QTextureDataPtr operator()() Q_DECL_OVERRIDE;
+ bool operator==(const QTextureGenerator &other) const Q_DECL_OVERRIDE;
+
+ QT3D_FUNCTOR(QTextureAtlasGenerator)
+
+private:
+ QTextureAtlasDataPtr m_data;
+ Qt3DRender::QAbstractTexture::TextureFormat m_format;
+ QOpenGLTexture::PixelFormat m_pixelFormat;
+ int m_generation;
+ Qt3DCore::QNodeId m_atlasId;
+};
+typedef QSharedPointer<QTextureAtlasGenerator> QTextureAtlasGeneratorPtr;
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
+
+#endif // QT3DEXTRAS_QTEXTUREATLAS_P_P_H
diff --git a/src/extras/text/text.pri b/src/extras/text/text.pri
new file mode 100644
index 000000000..1d2cb793c
--- /dev/null
+++ b/src/extras/text/text.pri
@@ -0,0 +1,21 @@
+HEADERS += \
+ $$PWD/distancefieldtextrenderer_p.h \
+ $$PWD/distancefieldtextrenderer_p_p.h \
+ $$PWD/areaallocator_p.h \
+ $$PWD/qdistancefieldglyphcache_p.h \
+ $$PWD/qtextureatlas_p_p.h \
+ $$PWD/qtextureatlas_p.h \
+ $$PWD/qtext2dentity_p.h \
+ $$PWD/qtext2dentity.h \
+ $$PWD/qtext2dmaterial_p_p.h \
+ $$PWD/qtext2dmaterial_p.h
+
+SOURCES += \
+ $$PWD/qtextureatlas.cpp \
+ $$PWD/qdistancefieldglyphcache.cpp \
+ $$PWD/distancefieldtextrenderer.cpp \
+ $$PWD/areaallocator.cpp \
+ $$PWD/qtext2dentity.cpp \
+ $$PWD/qtext2dmaterial.cpp
+
+INCLUDEPATH += $$PWD