summaryrefslogtreecommitdiffstats
path: root/src/extras/text
diff options
context:
space:
mode:
authorWieland Hagen <wieland.hagen@kdab.com>2016-12-12 12:53:53 +0700
committerSean Harmer <sean.harmer@kdab.com>2017-01-30 18:56:10 +0000
commit0010aa249244ff801af62e73a4b121fbd1491d73 (patch)
treee91f7f9358d1a5fbbb0751685ea9d9d24eec7b6e /src/extras/text
parentfb4c4abbc4beb9673654c318135f096013272d87 (diff)
Add Qt3DExtras::QTextureAtlas class
areallocator is copied from qtdeclarative/src/quick/scenegraph/util. We could link to QtQuick to use this allocator from the private headers, but this would introduce a hard dependency on QtQuick that Qt3DExtras doesn't yet have. Image data for texture atlasses are stored inside QTextureAtlasData structures, which are shared between 1. the frontend QTextureAtlas node, and 2. the texture generators that are updated each time something is added to the atlas If we didn't share this data, we would have to copy the current QImage that stores the current state of the texture atlas, each time that a new sub-image is added. This would result in considerable memory copying overhead. By sharing the data, we can just add new sub-images to the shared data structure, and update the texture generator. This may happen dozens of times within one frame. When the backend texture loading job is executed, it will copy all the new sub-images into the overall texture image, and create the texture data from that image. This way, exactly zero image copying overhead happens in the frontend thread. Change-Id: I8c418bf335afd1363ad7cefdf81778e4075038e8 Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
Diffstat (limited to 'src/extras/text')
-rw-r--r--src/extras/text/areaallocator.cpp296
-rw-r--r--src/extras/text/areaallocator_p.h92
-rw-r--r--src/extras/text/qtextureatlas.cpp303
-rw-r--r--src/extras/text/qtextureatlas.h84
-rw-r--r--src/extras/text/qtextureatlas_p.h139
-rw-r--r--src/extras/text/text.pri10
6 files changed, 924 insertions, 0 deletions
diff --git a/src/extras/text/areaallocator.cpp b/src/extras/text/areaallocator.cpp
new file mode 100644
index 000000000..61f1d5bc6
--- /dev/null
+++ b/src/extras/text/areaallocator.cpp
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "areaallocator_p.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qpoint.h>
+
+//
+// This file is copied from qtdeclarative/src/quick/scenegraph/util/qsgareaallocator.cpp
+//
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DExtras {
+
+namespace
+{
+ enum SplitType
+ {
+ VerticalSplit,
+ HorizontalSplit
+ };
+
+ static const int maxMargin = 2;
+}
+
+struct AreaAllocatorNode
+{
+ AreaAllocatorNode(AreaAllocatorNode *parent);
+ ~AreaAllocatorNode();
+ inline bool isLeaf();
+
+ AreaAllocatorNode *parent;
+ AreaAllocatorNode *left;
+ AreaAllocatorNode *right;
+ int split; // only valid for inner nodes.
+ SplitType splitType;
+ bool isOccupied; // only valid for leaf nodes.
+};
+
+AreaAllocatorNode::AreaAllocatorNode(AreaAllocatorNode *parent)
+ : parent(parent)
+ , left(0)
+ , right(0)
+ , isOccupied(false)
+{
+}
+
+AreaAllocatorNode::~AreaAllocatorNode()
+{
+ delete left;
+ delete right;
+}
+
+bool AreaAllocatorNode::isLeaf()
+{
+ Q_ASSERT((left != 0) == (right != 0));
+ return !left;
+}
+
+
+AreaAllocator::AreaAllocator(const QSize &size) : m_size(size)
+{
+ m_root = new AreaAllocatorNode(0);
+}
+
+AreaAllocator::~AreaAllocator()
+{
+ delete m_root;
+}
+
+QRect AreaAllocator::allocate(const QSize &size)
+{
+ QPoint point;
+ bool result = allocateInNode(size, point, QRect(QPoint(0, 0), m_size), m_root);
+ return result ? QRect(point, size) : QRect();
+}
+
+bool AreaAllocator::deallocate(const QRect &rect)
+{
+ return deallocateInNode(rect.topLeft(), m_root);
+}
+
+bool AreaAllocator::allocateInNode(const QSize &size, QPoint &result, const QRect &currentRect, AreaAllocatorNode *node)
+{
+ if (size.width() > currentRect.width() || size.height() > currentRect.height())
+ return false;
+
+ if (node->isLeaf()) {
+ if (node->isOccupied)
+ return false;
+ if (size.width() + maxMargin >= currentRect.width() && size.height() + maxMargin >= currentRect.height()) {
+ //Snug fit, occupy entire rectangle.
+ node->isOccupied = true;
+ result = currentRect.topLeft();
+ return true;
+ }
+ // TODO: Reuse nodes.
+ // Split node.
+ node->left = new AreaAllocatorNode(node);
+ node->right = new AreaAllocatorNode(node);
+ QRect splitRect = currentRect;
+ if ((currentRect.width() - size.width()) * currentRect.height() < (currentRect.height() - size.height()) * currentRect.width()) {
+ node->splitType = HorizontalSplit;
+ node->split = currentRect.top() + size.height();
+ splitRect.setHeight(size.height());
+ } else {
+ node->splitType = VerticalSplit;
+ node->split = currentRect.left() + size.width();
+ splitRect.setWidth(size.width());
+ }
+ return allocateInNode(size, result, splitRect, node->left);
+ } else {
+ // TODO: avoid unnecessary recursion.
+ // has been split.
+ QRect leftRect = currentRect;
+ QRect rightRect = currentRect;
+ if (node->splitType == HorizontalSplit) {
+ leftRect.setHeight(node->split - leftRect.top());
+ rightRect.setTop(node->split);
+ } else {
+ leftRect.setWidth(node->split - leftRect.left());
+ rightRect.setLeft(node->split);
+ }
+ if (allocateInNode(size, result, leftRect, node->left))
+ return true;
+ if (allocateInNode(size, result, rightRect, node->right))
+ return true;
+ return false;
+ }
+}
+
+bool AreaAllocator::deallocateInNode(const QPoint &pos, AreaAllocatorNode *node)
+{
+ while (!node->isLeaf()) {
+ // has been split.
+ int cmp = node->splitType == HorizontalSplit ? pos.y() : pos.x();
+ node = cmp < node->split ? node->left : node->right;
+ }
+ if (!node->isOccupied)
+ return false;
+ node->isOccupied = false;
+ mergeNodeWithNeighbors(node);
+ return true;
+}
+
+void AreaAllocator::mergeNodeWithNeighbors(AreaAllocatorNode *node)
+{
+ bool done = false;
+ AreaAllocatorNode *parent = 0;
+ AreaAllocatorNode *current = 0;
+ AreaAllocatorNode *sibling;
+ while (!done) {
+ Q_ASSERT(node->isLeaf());
+ Q_ASSERT(!node->isOccupied);
+ if (node->parent == 0)
+ return; // No neighbors.
+
+ SplitType splitType = SplitType(node->parent->splitType);
+ done = true;
+
+ /* Special case. Might be faster than going through the general code path.
+ // Merge with sibling.
+ parent = node->parent;
+ sibling = (node == parent->left ? parent->right : parent->left);
+ if (sibling->isLeaf() && !sibling->isOccupied) {
+ Q_ASSERT(!sibling->right);
+ node = parent;
+ parent->isOccupied = false;
+ delete parent->left;
+ delete parent->right;
+ parent->left = parent->right = 0;
+ done = false;
+ continue;
+ }
+ */
+
+ // Merge with left neighbor.
+ current = node;
+ parent = current->parent;
+ while (parent && current == parent->left && parent->splitType == splitType) {
+ current = parent;
+ parent = parent->parent;
+ }
+
+ if (parent && parent->splitType == splitType) {
+ Q_ASSERT(current == parent->right);
+ Q_ASSERT(parent->left);
+
+ AreaAllocatorNode *neighbor = parent->left;
+ while (neighbor->right && neighbor->splitType == splitType)
+ neighbor = neighbor->right;
+
+ if (neighbor->isLeaf() && neighbor->parent->splitType == splitType && !neighbor->isOccupied) {
+ // Left neighbor can be merged.
+ parent->split = neighbor->parent->split;
+
+ parent = neighbor->parent;
+ sibling = neighbor == parent->left ? parent->right : parent->left;
+ AreaAllocatorNode **nodeRef = &m_root;
+ if (parent->parent) {
+ if (parent == parent->parent->left)
+ nodeRef = &parent->parent->left;
+ else
+ nodeRef = &parent->parent->right;
+ }
+ sibling->parent = parent->parent;
+ *nodeRef = sibling;
+ parent->left = parent->right = 0;
+ delete parent;
+ delete neighbor;
+ done = false;
+ }
+ }
+
+ // Merge with right neighbor.
+ current = node;
+ parent = current->parent;
+ while (parent && current == parent->right && parent->splitType == splitType) {
+ current = parent;
+ parent = parent->parent;
+ }
+
+ if (parent && parent->splitType == splitType) {
+ Q_ASSERT(current == parent->left);
+ Q_ASSERT(parent->right);
+
+ AreaAllocatorNode *neighbor = parent->right;
+ while (neighbor->left && neighbor->splitType == splitType)
+ neighbor = neighbor->left;
+
+ if (neighbor->isLeaf() && neighbor->parent->splitType == splitType && !neighbor->isOccupied) {
+ // Right neighbor can be merged.
+ parent->split = neighbor->parent->split;
+
+ parent = neighbor->parent;
+ sibling = neighbor == parent->left ? parent->right : parent->left;
+ AreaAllocatorNode **nodeRef = &m_root;
+ if (parent->parent) {
+ if (parent == parent->parent->left)
+ nodeRef = &parent->parent->left;
+ else
+ nodeRef = &parent->parent->right;
+ }
+ sibling->parent = parent->parent;
+ *nodeRef = sibling;
+ parent->left = parent->right = 0;
+ delete parent;
+ delete neighbor;
+ done = false;
+ }
+ }
+ } // end while (!done)
+}
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
diff --git a/src/extras/text/areaallocator_p.h b/src/extras/text/areaallocator_p.h
new file mode 100644
index 000000000..809c5c698
--- /dev/null
+++ b/src/extras/text/areaallocator_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DEXTRAS_AREAALLOCATOR_P_H
+#define QT3DEXTRAS_AREAALLOCATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+//
+// This file is copied from qtdeclarative/src/quick/scenegraph/util/qsgareaallocator_p.h
+//
+
+#include <QtCore/qsize.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRect;
+class QPoint;
+
+namespace Qt3DExtras {
+
+struct AreaAllocatorNode;
+
+class AreaAllocator
+{
+public:
+ AreaAllocator(const QSize &size);
+ ~AreaAllocator();
+
+ QRect allocate(const QSize &size);
+ bool deallocate(const QRect &rect);
+ bool isEmpty() const { return m_root == 0; }
+ QSize size() const { return m_size; }
+private:
+ bool allocateInNode(const QSize &size, QPoint &result, const QRect &currentRect, AreaAllocatorNode *node);
+ bool deallocateInNode(const QPoint &pos, AreaAllocatorNode *node);
+ void mergeNodeWithNeighbors(AreaAllocatorNode *node);
+
+ AreaAllocatorNode *m_root;
+ QSize m_size;
+};
+
+} // namespace Qt3DExtras
+
+QT_END_NAMESPACE
+
+#endif // QT3DEXTRAS_AREAALLOCATOR_P_H
diff --git a/src/extras/text/qtextureatlas.cpp b/src/extras/text/qtextureatlas.cpp
new file mode 100644
index 000000000..1a076a3ab
--- /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.h"
+#include "qtextureatlas_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 = 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.h b/src/extras/text/qtextureatlas.h
new file mode 100644
index 000000000..c70f9dfd3
--- /dev/null
+++ b/src/extras/text/qtextureatlas.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** 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_H
+#define QT3DEXTRAS_QTEXTUREATLAS_H
+
+#include <Qt3DExtras/qt3dextras_global.h>
+#include <Qt3DRender/qabstracttexture.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DExtras {
+
+class QTextureAtlasPrivate;
+
+class QT3DEXTRASSHARED_EXPORT 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_H
diff --git a/src/extras/text/qtextureatlas_p.h b/src/extras/text/qtextureatlas_p.h
new file mode 100644
index 000000000..34386a87a
--- /dev/null
+++ b/src/extras/text/qtextureatlas_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_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 <QtCore/qscopedpointer.h>
+#include <Qt3DRender/private/qabstracttexture_p.h>
+#include <Qt3DRender/qtexturegenerator.h>
+#include <Qt3DExtras/private/areaallocator_p.h>
+#include <Qt3DExtras/qtextureatlas.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_H
diff --git a/src/extras/text/text.pri b/src/extras/text/text.pri
new file mode 100644
index 000000000..9ba6830e9
--- /dev/null
+++ b/src/extras/text/text.pri
@@ -0,0 +1,10 @@
+HEADERS += \
+ $$PWD/qtextureatlas.h \
+ $$PWD/qtextureatlas_p.h \
+ $$PWD/areaallocator_p.h
+
+SOURCES += \
+ $$PWD/qtextureatlas.cpp \
+ $$PWD/areaallocator.cpp
+
+INCLUDEPATH += $$PWD