diff options
Diffstat (limited to 'src/extras/text')
-rw-r--r-- | src/extras/text/areaallocator.cpp | 296 | ||||
-rw-r--r-- | src/extras/text/areaallocator_p.h | 92 | ||||
-rw-r--r-- | src/extras/text/distancefieldtextrenderer.cpp | 156 | ||||
-rw-r--r-- | src/extras/text/distancefieldtextrenderer_p.h | 90 | ||||
-rw-r--r-- | src/extras/text/distancefieldtextrenderer_p_p.h | 95 | ||||
-rw-r--r-- | src/extras/text/qdistancefieldglyphcache.cpp | 370 | ||||
-rw-r--r-- | src/extras/text/qdistancefieldglyphcache_p.h | 111 | ||||
-rw-r--r-- | src/extras/text/qtext2dentity.cpp | 381 | ||||
-rw-r--r-- | src/extras/text/qtext2dentity.h | 98 | ||||
-rw-r--r-- | src/extras/text/qtext2dentity_p.h | 122 | ||||
-rw-r--r-- | src/extras/text/qtext2dmaterial.cpp | 179 | ||||
-rw-r--r-- | src/extras/text/qtext2dmaterial_p.h | 83 | ||||
-rw-r--r-- | src/extras/text/qtext2dmaterial_p_p.h | 106 | ||||
-rw-r--r-- | src/extras/text/qtextureatlas.cpp | 303 | ||||
-rw-r--r-- | src/extras/text/qtextureatlas_p.h | 95 | ||||
-rw-r--r-- | src/extras/text/qtextureatlas_p_p.h | 139 | ||||
-rw-r--r-- | src/extras/text/text.pri | 21 |
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 ¤tRect, 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 ¤tRect, 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 |