diff options
| author | Laszlo Agocs <laszlo.agocs@theqtcompany.com> | 2016-03-15 11:36:52 +0100 |
|---|---|---|
| committer | Laszlo Agocs <laszlo.agocs@theqtcompany.com> | 2016-03-17 13:37:24 +0000 |
| commit | 2401162f340bb50b58de701eede0b55c43753185 (patch) | |
| tree | 5e83bd383db26403231875bf1225ff850c1fbc54 /src/quick/scenegraph/qsgbasicimagenode.cpp | |
| parent | 843f72bd317f195ee31921acf8132074a7c50d4a (diff) | |
D3D12: Text enablers
Enablers for native text rendering. Distance field support is postponed
for the time being.
The main feature here is the revised texture handling where it is now
possible to schedule the update of multiple sub-regions from a list of
QImages. BGRA is now used where appropriate, meaning for example that
RGB32 needs no QImage conversion anymore.
The skeletons of the glyph nodes, the basic text material and the glyph
cache are added as well.
Also fixes cleanup when closing the window by properly reimplementing
invalidate() in the rendercontext.
The default rectangle, image and glyph nodes are refactored a bit once
again: QSGBasicSomethingNode is now the common (albeit optional), abstract
class, living in its own file, whereas QSGDefaultSomethingNode is the
GL-specific one, like before. This prepares the future renaming for
QSGDefault -> QSGOpenGL.
Change-Id: I6dbb4ece10be39fd214bb64082d79607389e3e6e
Reviewed-by: Andy Nichols <andy.nichols@theqtcompany.com>
Diffstat (limited to 'src/quick/scenegraph/qsgbasicimagenode.cpp')
| -rw-r--r-- | src/quick/scenegraph/qsgbasicimagenode.cpp | 519 |
1 files changed, 519 insertions, 0 deletions
diff --git a/src/quick/scenegraph/qsgbasicimagenode.cpp b/src/quick/scenegraph/qsgbasicimagenode.cpp new file mode 100644 index 0000000000..a76c788656 --- /dev/null +++ b/src/quick/scenegraph/qsgbasicimagenode.cpp @@ -0,0 +1,519 @@ +/**************************************************************************** +** +** 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 "qsgbasicimagenode_p.h" + +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +namespace +{ + struct SmoothVertex + { + float x, y, u, v; + float dx, dy, du, dv; + }; + + const QSGGeometry::AttributeSet &smoothAttributeSet() + { + static QSGGeometry::Attribute data[] = { + QSGGeometry::Attribute::createWithSemantic(0, 2, GL_FLOAT, QSGGeometry::Attribute::POSITION), + QSGGeometry::Attribute::createWithSemantic(1, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD), + QSGGeometry::Attribute::createWithSemantic(2, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD1), + QSGGeometry::Attribute::createWithSemantic(3, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD2) + }; + static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data }; + return attrs; + } +} + +QSGBasicImageNode::QSGBasicImageNode() + : m_innerSourceRect(0, 0, 1, 1) + , m_subSourceRect(0, 0, 1, 1) + , m_antialiasing(false) + , m_mirror(false) + , m_dirtyGeometry(false) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) +{ + setGeometry(&m_geometry); + +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("image")); +#endif +} + +void QSGBasicImageNode::setTargetRect(const QRectF &rect) +{ + if (rect == m_targetRect) + return; + m_targetRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicImageNode::setInnerTargetRect(const QRectF &rect) +{ + if (rect == m_innerTargetRect) + return; + m_innerTargetRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicImageNode::setInnerSourceRect(const QRectF &rect) +{ + if (rect == m_innerSourceRect) + return; + m_innerSourceRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicImageNode::setSubSourceRect(const QRectF &rect) +{ + if (rect == m_subSourceRect) + return; + m_subSourceRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicImageNode::setTexture(QSGTexture *texture) +{ + Q_ASSERT(texture); + + setMaterialTexture(texture); + updateMaterialBlending(); + + markDirty(DirtyMaterial); + + // Because the texture can be a different part of the atlas, we need to update it... + m_dirtyGeometry = true; +} + +void QSGBasicImageNode::setAntialiasing(bool antialiasing) +{ + if (antialiasing == m_antialiasing) + return; + m_antialiasing = antialiasing; + if (m_antialiasing) { + setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); + setFlag(OwnsGeometry, true); + } else { + setGeometry(&m_geometry); + setFlag(OwnsGeometry, false); + } + updateMaterialAntialiasing(); + m_dirtyGeometry = true; +} + +void QSGBasicImageNode::setMirror(bool mirror) +{ + if (mirror == m_mirror) + return; + m_mirror = mirror; + m_dirtyGeometry = true; +} + + +void QSGBasicImageNode::update() +{ + if (m_dirtyGeometry) + updateGeometry(); +} + +void QSGBasicImageNode::preprocess() +{ + bool doDirty = false; + QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(materialTexture()); + if (t) { + doDirty = t->updateTexture(); + if (doDirty) + updateGeometry(); + } + + if (updateMaterialBlending()) + doDirty = true; + + if (doDirty) + markDirty(DirtyMaterial); +} + +namespace { + struct X { float x, tx; }; + struct Y { float y, ty; }; +} + +static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight, + quint16 bottomLeft, quint16 bottomRight) +{ + *(*indices)++ = topLeft; + *(*indices)++ = bottomLeft; + *(*indices)++ = bottomRight; + *(*indices)++ = bottomRight; + *(*indices)++ = topRight; + *(*indices)++ = topLeft; +} + +void QSGBasicImageNode::updateGeometry() +{ + Q_ASSERT(!m_targetRect.isEmpty()); + const QSGTexture *t = materialTexture(); + if (!t) { + QSGGeometry *g = geometry(); + g->allocate(4); + g->setDrawingMode(GL_TRIANGLE_STRIP); + memset(g->vertexData(), 0, g->sizeOfVertex() * 4); + } else { + QRectF sourceRect = t->normalizedTextureSubRect(); + + QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(), + sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(), + m_innerSourceRect.width() * sourceRect.width(), + m_innerSourceRect.height() * sourceRect.height()); + + bool hasMargins = m_targetRect != m_innerTargetRect; + + int floorLeft = qFloor(m_subSourceRect.left()); + int ceilRight = qCeil(m_subSourceRect.right()); + int floorTop = qFloor(m_subSourceRect.top()); + int ceilBottom = qCeil(m_subSourceRect.bottom()); + int hTiles = ceilRight - floorLeft; + int vTiles = ceilBottom - floorTop; + + bool hasTiles = hTiles != 1 || vTiles != 1; + bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1); + + // An image can be rendered as a single quad if: + // - There are no margins, and either: + // - the image isn't repeated + // - the source rectangle fills the entire texture so that texture wrapping can be used, + // and NPOT is supported + if (!hasMargins && (!hasTiles || (fullTexture && supportsWrap(t->textureSize())))) { + QRectF sr; + if (!fullTexture) { + sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(), + innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(), + m_subSourceRect.width() * innerSourceRect.width(), + m_subSourceRect.height() * innerSourceRect.height()); + } else { + sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop, + m_subSourceRect.width(), m_subSourceRect.height()); + } + if (m_mirror) { + qreal oldLeft = sr.left(); + sr.setLeft(sr.right()); + sr.setRight(oldLeft); + } + + if (m_antialiasing) { + QSGGeometry *g = geometry(); + Q_ASSERT(g != &m_geometry); + g->allocate(8, 14); + g->setDrawingMode(GL_TRIANGLE_STRIP); + SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); + float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) + ? m_targetRect.width() : m_targetRect.height()) * 0.5f; + float sx = float(sr.width() / m_targetRect.width()); + float sy = float(sr.height() / m_targetRect.height()); + for (int d = -1; d <= 1; d += 2) { + for (int j = 0; j < 2; ++j) { + for (int i = 0; i < 2; ++i, ++vertices) { + vertices->x = m_targetRect.x() + i * m_targetRect.width(); + vertices->y = m_targetRect.y() + j * m_targetRect.height(); + vertices->u = sr.x() + i * sr.width(); + vertices->v = sr.y() + j * sr.height(); + vertices->dx = (i == 0 ? delta : -delta) * d; + vertices->dy = (j == 0 ? delta : -delta) * d; + vertices->du = (d < 0 ? 0 : vertices->dx * sx); + vertices->dv = (d < 0 ? 0 : vertices->dy * sy); + } + } + } + Q_ASSERT(vertices - g->vertexCount() == g->vertexData()); + static const quint16 indices[] = { + 0, 4, 1, 5, 3, 7, 2, 6, 0, 4, + 4, 6, 5, 7 + }; + Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices)); + memcpy(g->indexDataAsUShort(), indices, sizeof(indices)); + } else { + m_geometry.allocate(4); + m_geometry.setDrawingMode(GL_TRIANGLE_STRIP); + QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); + } + } else { + int hCells = hTiles; + int vCells = vTiles; + if (m_innerTargetRect.width() == 0) + hCells = 0; + if (m_innerTargetRect.left() != m_targetRect.left()) + ++hCells; + if (m_innerTargetRect.right() != m_targetRect.right()) + ++hCells; + if (m_innerTargetRect.height() == 0) + vCells = 0; + if (m_innerTargetRect.top() != m_targetRect.top()) + ++vCells; + if (m_innerTargetRect.bottom() != m_targetRect.bottom()) + ++vCells; + QVarLengthArray<X, 32> xData(2 * hCells); + QVarLengthArray<Y, 32> yData(2 * vCells); + X *xs = xData.data(); + Y *ys = yData.data(); + + if (m_innerTargetRect.left() != m_targetRect.left()) { + xs[0].x = m_targetRect.left(); + xs[0].tx = sourceRect.left(); + xs[1].x = m_innerTargetRect.left(); + xs[1].tx = innerSourceRect.left(); + xs += 2; + } + if (m_innerTargetRect.width() != 0) { + xs[0].x = m_innerTargetRect.left(); + xs[0].tx = innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(); + ++xs; + float b = m_innerTargetRect.width() / m_subSourceRect.width(); + float a = m_innerTargetRect.x() - m_subSourceRect.x() * b; + for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) { + xs[0].x = xs[1].x = a + b * i; + xs[0].tx = innerSourceRect.right(); + xs[1].tx = innerSourceRect.left(); + xs += 2; + } + xs[0].x = m_innerTargetRect.right(); + xs[0].tx = innerSourceRect.x() + (m_subSourceRect.right() - ceilRight + 1) * innerSourceRect.width(); + ++xs; + } + if (m_innerTargetRect.right() != m_targetRect.right()) { + xs[0].x = m_innerTargetRect.right(); + xs[0].tx = innerSourceRect.right(); + xs[1].x = m_targetRect.right(); + xs[1].tx = sourceRect.right(); + xs += 2; + } + Q_ASSERT(xs == xData.data() + xData.size()); + if (m_mirror) { + float leftPlusRight = m_targetRect.left() + m_targetRect.right(); + int count = xData.size(); + xs = xData.data(); + for (int i = 0; i < count >> 1; ++i) + qSwap(xs[i], xs[count - 1 - i]); + for (int i = 0; i < count; ++i) + xs[i].x = leftPlusRight - xs[i].x; + } + + if (m_innerTargetRect.top() != m_targetRect.top()) { + ys[0].y = m_targetRect.top(); + ys[0].ty = sourceRect.top(); + ys[1].y = m_innerTargetRect.top(); + ys[1].ty = innerSourceRect.top(); + ys += 2; + } + if (m_innerTargetRect.height() != 0) { + ys[0].y = m_innerTargetRect.top(); + ys[0].ty = innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(); + ++ys; + float b = m_innerTargetRect.height() / m_subSourceRect.height(); + float a = m_innerTargetRect.y() - m_subSourceRect.y() * b; + for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) { + ys[0].y = ys[1].y = a + b * i; + ys[0].ty = innerSourceRect.bottom(); + ys[1].ty = innerSourceRect.top(); + ys += 2; + } + ys[0].y = m_innerTargetRect.bottom(); + ys[0].ty = innerSourceRect.y() + (m_subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height(); + ++ys; + } + if (m_innerTargetRect.bottom() != m_targetRect.bottom()) { + ys[0].y = m_innerTargetRect.bottom(); + ys[0].ty = innerSourceRect.bottom(); + ys[1].y = m_targetRect.bottom(); + ys[1].ty = sourceRect.bottom(); + ys += 2; + } + Q_ASSERT(ys == yData.data() + yData.size()); + + if (m_antialiasing) { + QSGGeometry *g = geometry(); + Q_ASSERT(g != &m_geometry); + + g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4, + hCells * vCells * 6 + (hCells + vCells) * 12); + g->setDrawingMode(GL_TRIANGLES); + SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); + memset(vertices, 0, g->vertexCount() * g->sizeOfVertex()); + quint16 *indices = g->indexDataAsUShort(); + + // The deltas are how much the fuzziness can reach into the image. + // Only the border vertices are moved by the vertex shader, so the fuzziness + // can't reach further into the image than the closest interior vertices. + float leftDx = xData.at(1).x - xData.at(0).x; + float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x; + float topDy = yData.at(1).y - yData.at(0).y; + float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y; + + float leftDu = xData.at(1).tx - xData.at(0).tx; + float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx; + float topDv = yData.at(1).ty - yData.at(0).ty; + float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty; + + if (hCells == 1) { + leftDx = rightDx *= 0.5f; + leftDu = rightDu *= 0.5f; + } + if (vCells == 1) { + topDy = bottomDy *= 0.5f; + topDv = bottomDv *= 0.5f; + } + + // This delta is how much the fuzziness can reach out from the image. + float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) + ? m_targetRect.width() : m_targetRect.height()) * 0.5f; + + quint16 index = 0; + ys = yData.data(); + for (int j = 0; j < vCells; ++j, ys += 2) { + xs = xData.data(); + bool isTop = j == 0; + bool isBottom = j == vCells - 1; + for (int i = 0; i < hCells; ++i, xs += 2) { + bool isLeft = i == 0; + bool isRight = i == hCells - 1; + + SmoothVertex *v = vertices + index; + + quint16 topLeft = index; + for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) { + v->x = xs[0].x; + v->u = xs[0].tx; + v->y = ys[0].y; + v->v = ys[0].ty; + } + + quint16 topRight = index; + for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) { + v->x = xs[1].x; + v->u = xs[1].tx; + v->y = ys[0].y; + v->v = ys[0].ty; + } + + quint16 bottomLeft = index; + for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) { + v->x = xs[0].x; + v->u = xs[0].tx; + v->y = ys[1].y; + v->v = ys[1].ty; + } + + quint16 bottomRight = index; + for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) { + v->x = xs[1].x; + v->u = xs[1].tx; + v->y = ys[1].y; + v->v = ys[1].ty; + } + + appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight); + + if (isTop) { + vertices[topLeft].dy = vertices[topRight].dy = topDy; + vertices[topLeft].dv = vertices[topRight].dv = topDv; + vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta; + appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight); + } + + if (isBottom) { + vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy; + vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv; + vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta; + appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1); + } + + if (isLeft) { + vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx; + vertices[topLeft].du = vertices[bottomLeft].du = leftDu; + vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta; + appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft); + } + + if (isRight) { + vertices[topRight].dx = vertices[bottomRight].dx = -rightDx; + vertices[topRight].du = vertices[bottomRight].du = -rightDu; + vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta; + appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1); + } + } + } + + Q_ASSERT(index == g->vertexCount()); + Q_ASSERT(indices - g->indexCount() == g->indexData()); + } else { + m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6); + m_geometry.setDrawingMode(GL_TRIANGLES); + QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D(); + ys = yData.data(); + for (int j = 0; j < vCells; ++j, ys += 2) { + xs = xData.data(); + for (int i = 0; i < hCells; ++i, xs += 2) { + vertices[0].x = vertices[2].x = xs[0].x; + vertices[0].tx = vertices[2].tx = xs[0].tx; + vertices[1].x = vertices[3].x = xs[1].x; + vertices[1].tx = vertices[3].tx = xs[1].tx; + + vertices[0].y = vertices[1].y = ys[0].y; + vertices[0].ty = vertices[1].ty = ys[0].ty; + vertices[2].y = vertices[3].y = ys[1].y; + vertices[2].ty = vertices[3].ty = ys[1].ty; + + vertices += 4; + } + } + + quint16 *indices = m_geometry.indexDataAsUShort(); + for (int i = 0; i < 4 * vCells * hCells; i += 4) + appendQuad(&indices, i, i + 1, i + 2, i + 3); + } + } + } + markDirty(DirtyGeometry); + m_dirtyGeometry = false; +} + +QT_END_NAMESPACE |
