blob: 4ab99222456b9f01353167f6449940ee29675413 [file] [log] [blame]
/****************************************************************************
**
** 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>
QT_BEGIN_NAMESPACE
using namespace Qt3DCore;
namespace Qt3DExtras {
QTextureAtlasData::QTextureAtlasData(int w, int h, QImage::Format fmt)
: m_image(w, h, fmt)
{
m_image.fill(0);
}
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++) {
uchar *dstLine = m_image.scanLine(y);
uchar *dstPadL = &dstLine[bpp * alloc.left()];
uchar *dstPadR = &dstLine[bpp * imgRect.right()];
uchar *dstImg = &dstLine[bpp * imgRect.left()];
// do padding with 0 in the upper/lower padding parts around the actual image
if (y < imgRect.top() || y > imgRect.bottom()) {
memset(dstPadL, 0, bpp * (imgRect.width() + 2 * padding));
continue;
}
// copy left and right padding pixels
memset(dstPadL, 0, bpp * padding);
memset(dstPadR, 0, bpp * padding);
// copy image scanline
const int ySrc = qBound(0, y - imgRect.top(), image.height()-1);
memcpy(dstImg, image.scanLine(ySrc), bpp * imgRect.width());
}
}
return QByteArray(reinterpret_cast<const char*>(m_image.constBits()), m_image.sizeInBytes());
}
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