diff options
Diffstat (limited to 'src/quick/scenegraph')
63 files changed, 16661 insertions, 0 deletions
diff --git a/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp b/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp new file mode 100644 index 0000000000..56a6e0e5d9 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp @@ -0,0 +1,527 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#define GL_GLEXT_PROTOTYPES + +#include "qsgdefaultrenderer_p.h" +#include "qsgmaterial.h" + +#include <QtCore/qvarlengtharray.h> +#include <QtGui/qguiapplication.h> +#include <QtCore/qpair.h> +#include <QtCore/QElapsedTimer> + +//#define FORCE_NO_REORDER + +// #define RENDERER_DEBUG +#ifdef RENDERER_DEBUG +#define DEBUG_THRESHOLD 0 +QElapsedTimer debugTimer; +int materialChanges; +int geometryNodesDrawn; +#endif + +QT_BEGIN_NAMESPACE + +static bool nodeLessThan(QSGGeometryNode *a, QSGGeometryNode *b) +{ + // Sort by clip... + if (a->clipList() != b->clipList()) + return a->clipList() < b->clipList(); + + // Sort by material definition + QSGMaterialType *aDef = a->material()->type(); + QSGMaterialType *bDef = b->material()->type(); + + if (aDef != bDef) + return aDef < bDef; + + // Sort by material state + int cmp = a->material()->compare(b->material()); + if (cmp != 0) + return cmp < 0; + + return a->matrix() < b->matrix(); +} + +static bool nodeLessThanWithRenderOrder(QSGGeometryNode *a, QSGGeometryNode *b) +{ + // Sort by clip... + if (a->clipList() != b->clipList()) + return a->clipList() < b->clipList(); + + // Sort by material definition + QSGMaterialType *aDef = a->material()->type(); + QSGMaterialType *bDef = b->material()->type(); + + if (!(a->material()->flags() & QSGMaterial::Blending)) { + int aOrder = a->renderOrder(); + int bOrder = b->renderOrder(); + if (aOrder != bOrder) + return aOrder > bOrder; + } + + if (aDef != bDef) + return aDef < bDef; + + // Sort by material state + int cmp = a->material()->compare(b->material()); + if (cmp != 0) + return cmp < 0; + + return a->matrix() < b->matrix(); +} + + +QSGDefaultRenderer::IndexGeometryNodePair::IndexGeometryNodePair(int i, QSGGeometryNode *node) + : QPair<int, QSGGeometryNode *>(i, node) +{ +} + +bool QSGDefaultRenderer::IndexGeometryNodePair::operator < (const QSGDefaultRenderer::IndexGeometryNodePair &other) const +{ + return nodeLessThan(second, other.second); +} + + +QSGDefaultRenderer::IndexGeometryNodePairHeap::IndexGeometryNodePairHeap() + : v(64) +{ +} + +void QSGDefaultRenderer::IndexGeometryNodePairHeap::insert(const QSGDefaultRenderer::IndexGeometryNodePair &x) +{ + int i = v.size(); + v.add(x); + while (i != 0 && v.at(i) < v.at(parent(i))) { + qSwap(v.at(parent(i)), v.at(i)); + i = parent(i); + } +} + +QSGDefaultRenderer::IndexGeometryNodePair QSGDefaultRenderer::IndexGeometryNodePairHeap::pop() +{ + IndexGeometryNodePair x = top(); + if (v.size() > 1) + qSwap(v.first(), v.last()); + v.pop_back(); + int i = 0; + while (left(i) < v.size()) { + int low = left(i); + if (right(i) < v.size() && v.at(right(i)) < v.at(low)) + low = right(i); + if (!(v.at(low) < v.at(i))) + break; + qSwap(v.at(i), v.at(low)); + i = low; + } + return x; +} + + +QSGDefaultRenderer::QSGDefaultRenderer(QSGContext *context) + : QSGRenderer(context) + , m_opaqueNodes(64) + , m_transparentNodes(64) + , m_tempNodes(64) + , m_rebuild_lists(false) + , m_needs_sorting(false) + , m_sort_front_to_back(false) + , m_currentRenderOrder(1) +{ + QStringList args = qApp->arguments(); +#if defined(QML_RUNTIME_TESTING) + m_render_opaque_nodes = !args.contains(QLatin1String("--no-opaque-nodes")); + m_render_alpha_nodes = !args.contains(QLatin1String("--no-alpha-nodes")); +#endif +} + +void QSGDefaultRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags) +{ + QSGRenderer::nodeChanged(node, flags); + + quint32 rebuildFlags = QSGNode::DirtyNodeAdded | QSGNode::DirtyNodeRemoved + | QSGNode::DirtyMaterial | QSGNode::DirtyOpacity + | QSGNode::DirtyForceUpdate; + + if (flags & rebuildFlags) + m_rebuild_lists = true; + + if (flags & (rebuildFlags | QSGNode::DirtyClipList)) + m_needs_sorting = true; +} + +void QSGDefaultRenderer::render() +{ +#if defined (QML_RUNTIME_TESTING) + static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree")); + if (dumpTree) { + printf("\n\n"); + QSGNodeDumper::dump(rootNode()); + } +#endif + +#ifdef RENDERER_DEBUG + debugTimer.invalidate(); + debugTimer.start(); + geometryNodesDrawn = 0; + materialChanges = 0; +#endif + + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_BLEND); + + glFrontFace(isMirrored() ? GL_CW : GL_CCW); + glDisable(GL_CULL_FACE); + + glEnable(GL_DEPTH_TEST); + glDepthMask(true); + glDepthFunc(GL_GREATER); +#if defined(QT_OPENGL_ES) + glClearDepthf(0); +#else + glClearDepth(0); +#endif + + glDisable(GL_SCISSOR_TEST); + glClearColor(m_clear_color.redF(), m_clear_color.greenF(), m_clear_color.blueF(), m_clear_color.alphaF()); + +#ifdef RENDERER_DEBUG + int debugtimeSetup = debugTimer.elapsed(); +#endif + + bindable()->clear(clearMode()); + +#ifdef RENDERER_DEBUG + int debugtimeClear = debugTimer.elapsed(); +#endif + + QRect r = viewportRect(); + glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height()); + m_current_projection_matrix = projectionMatrix(); + m_current_model_view_matrix.setToIdentity(); + + m_currentClip = 0; + glDisable(GL_STENCIL_TEST); + + m_currentMaterial = 0; + m_currentProgram = 0; + m_currentMatrix = 0; + + if (m_rebuild_lists) { + m_opaqueNodes.reset(); + m_transparentNodes.reset(); + m_currentRenderOrder = 1; + buildLists(rootNode()); + m_rebuild_lists = false; + } + +#ifdef RENDERER_DEBUG + int debugtimeLists = debugTimer.elapsed(); +#endif + + + if (m_needs_sorting) { + if (!m_opaqueNodes.isEmpty()) { + qSort(&m_opaqueNodes.first(), &m_opaqueNodes.first() + m_opaqueNodes.size(), + m_sort_front_to_back + ? nodeLessThanWithRenderOrder + : nodeLessThan); + } + m_needs_sorting = false; + } + +#ifdef RENDERER_DEBUG + int debugtimeSorting = debugTimer.elapsed(); +#endif + + m_renderOrderMatrix.setToIdentity(); + m_renderOrderMatrix.scale(1, 1, qreal(1) / m_currentRenderOrder); + + glDisable(GL_BLEND); + glDepthMask(true); +#ifdef QML_RUNTIME_TESTING + if (m_render_opaque_nodes) +#endif + { +#if defined (QML_RUNTIME_TESTING) + if (dumpTree) + qDebug() << "Opaque Nodes:"; +#endif + renderNodes(m_opaqueNodes); + } + +#ifdef RENDERER_DEBUG + int debugtimeOpaque = debugTimer.elapsed(); + int opaqueNodes = geometryNodesDrawn; + int opaqueMaterialChanges = materialChanges; +#endif + + glEnable(GL_BLEND); + glDepthMask(false); +#ifdef QML_RUNTIME_TESTING + if (m_render_alpha_nodes) +#endif + { +#if defined (QML_RUNTIME_TESTING) + if (dumpTree) + qDebug() << "Alpha Nodes:"; +#endif + renderNodes(m_transparentNodes); + } + +#ifdef RENDERER_DEBUG + int debugtimeAlpha = debugTimer.elapsed(); +#endif + + + if (m_currentProgram) + m_currentProgram->deactivate(); + +#ifdef RENDERER_DEBUG + if (debugTimer.elapsed() > DEBUG_THRESHOLD) { + printf(" --- Renderer breakdown:\n" + " - setup=%d, clear=%d, building=%d, sorting=%d, opaque=%d, alpha=%d\n" + " - material changes: opaque=%d, alpha=%d, total=%d\n" + " - geometry ndoes: opaque=%d, alpha=%d, total=%d\n", + debugtimeSetup, + debugtimeClear - debugtimeSetup, + debugtimeLists - debugtimeClear, + debugtimeSorting - debugtimeLists, + debugtimeOpaque - debugtimeSorting, + debugtimeAlpha - debugtimeOpaque, + opaqueMaterialChanges, materialChanges - opaqueMaterialChanges, materialChanges, + opaqueNodes, geometryNodesDrawn - opaqueNodes, geometryNodesDrawn); + } +#endif + +} + +void QSGDefaultRenderer::setSortFrontToBackEnabled(bool sort) +{ + printf("setting sorting to... %d\n", sort); + m_sort_front_to_back = sort; +} + +bool QSGDefaultRenderer::isSortFrontToBackEnabled() const +{ + return m_sort_front_to_back; +} + +void QSGDefaultRenderer::buildLists(QSGNode *node) +{ + if (node->isSubtreeBlocked()) + return; + + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *geomNode = static_cast<QSGGeometryNode *>(node); + qreal opacity = geomNode->inheritedOpacity(); + QSGMaterial *m = geomNode->activeMaterial(); + +#ifdef FORCE_NO_REORDER + if (true) { +#else + if ((m->flags() & QSGMaterial::Blending) || opacity < 1) { +#endif + geomNode->setRenderOrder(m_currentRenderOrder - 1); + m_transparentNodes.add(geomNode); + } else { + geomNode->setRenderOrder(m_currentRenderOrder); + m_opaqueNodes.add(geomNode); + m_currentRenderOrder += 2; + } + } + + if (!node->firstChild()) + return; + +#ifdef FORCE_NO_REORDER + static bool reorder = false; +#else + static bool reorder = qApp->arguments().contains(QLatin1String("--reorder")); +#endif + + if (reorder && node->firstChild() != node->lastChild() && (node->flags() & QSGNode::ChildrenDoNotOverlap)) { + QVarLengthArray<int, 16> beginIndices; + QVarLengthArray<int, 16> endIndices; + int baseCount = m_transparentNodes.size(); + int count = 0; + for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) { + beginIndices.append(m_transparentNodes.size()); + buildLists(c); + endIndices.append(m_transparentNodes.size()); + ++count; + } + + int childNodeCount = m_transparentNodes.size() - baseCount; + if (childNodeCount) { + m_tempNodes.reset(); + m_tempNodes.reserve(childNodeCount); + while (childNodeCount) { + for (int i = 0; i < count; ++i) { + if (beginIndices[i] != endIndices[i]) + m_heap.insert(IndexGeometryNodePair(i, m_transparentNodes.at(beginIndices[i]++))); + } + while (!m_heap.isEmpty()) { + IndexGeometryNodePair pair = m_heap.pop(); + m_tempNodes.add(pair.second); + --childNodeCount; + int i = pair.first; + if (beginIndices[i] != endIndices[i] && !nodeLessThan(m_transparentNodes.at(beginIndices[i]), pair.second)) + m_heap.insert(IndexGeometryNodePair(i, m_transparentNodes.at(beginIndices[i]++))); + } + } + Q_ASSERT(m_tempNodes.size() == m_transparentNodes.size() - baseCount); + + qMemCopy(&m_transparentNodes.at(baseCount), &m_tempNodes.at(0), m_tempNodes.size() * sizeof(QSGGeometryNode *)); + } + } else { + for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) + buildLists(c); + } +} + +void QSGDefaultRenderer::renderNodes(const QDataBuffer<QSGGeometryNode *> &list) +{ + const float scale = 1.0f / m_currentRenderOrder; + int count = list.size(); + int currentRenderOrder = 0x80000000; + m_current_projection_matrix.setColumn(2, scale * projectionMatrix().column(2)); + + //int clipChangeCount = 0; + //int programChangeCount = 0; + //int materialChangeCount = 0; + + for (int i = 0; i < count; ++i) { + QSGGeometryNode *geomNode = list.at(i); + + QSGMaterialShader::RenderState::DirtyStates updates; + +#if defined (QML_RUNTIME_TESTING) + static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree")); + if (dumpTree) + qDebug() << geomNode; +#endif + + bool changeMatrix = m_currentMatrix != geomNode->matrix(); + + if (changeMatrix) { + m_currentMatrix = geomNode->matrix(); + if (m_currentMatrix) + m_current_model_view_matrix = *m_currentMatrix; + else + m_current_model_view_matrix.setToIdentity(); + updates |= QSGMaterialShader::RenderState::DirtyMatrix; + } + + bool changeOpacity = m_current_opacity != geomNode->inheritedOpacity(); + if (changeOpacity) { + updates |= QSGMaterialShader::RenderState::DirtyOpacity; + m_current_opacity = geomNode->inheritedOpacity(); + } + + Q_ASSERT(geomNode->activeMaterial()); + + QSGMaterial *material = geomNode->activeMaterial(); + QSGMaterialShader *program = m_context->prepareMaterial(material); + Q_ASSERT(program->program()->isLinked()); + + bool changeClip = geomNode->clipList() != m_currentClip; + QSGRenderer::ClipType clipType = QSGRenderer::NoClip; + if (changeClip) { + // The clip function relies on there not being any depth testing.. + glDisable(GL_DEPTH_TEST); + clipType = updateStencilClip(geomNode->clipList()); + glEnable(GL_DEPTH_TEST); + m_currentClip = geomNode->clipList(); +#ifdef FORCE_NO_REORDER + glDepthMask(false); +#else + glDepthMask((material->flags() & QSGMaterial::Blending) == 0 && m_current_opacity == 1); +#endif + //++clipChangeCount; + } + + bool changeProgram = (changeClip && clipType == QSGRenderer::StencilClip) || m_currentProgram != program; + if (changeProgram) { + if (m_currentProgram) + m_currentProgram->deactivate(); + m_currentProgram = program; + m_currentProgram->activate(); + //++programChangeCount; + updates |= (QSGMaterialShader::RenderState::DirtyMatrix | QSGMaterialShader::RenderState::DirtyOpacity); + +#ifdef RENDERER_DEBUG + materialChanges++; +#endif + } + + bool changeRenderOrder = currentRenderOrder != geomNode->renderOrder(); + if (changeRenderOrder) { + currentRenderOrder = geomNode->renderOrder(); + m_current_projection_matrix.setColumn(3, projectionMatrix().column(3) + + currentRenderOrder + * m_current_projection_matrix.column(2)); + updates |= QSGMaterialShader::RenderState::DirtyMatrix; + } + + if (changeProgram || m_currentMaterial != material) { + program->updateState(state(updates), material, changeProgram ? 0 : m_currentMaterial); + m_currentMaterial = material; + //++materialChangeCount; + } + + //glDepthRange((geomNode->renderOrder() + 0.1) * scale, (geomNode->renderOrder() + 0.9) * scale); + + const QSGGeometry *g = geomNode->geometry(); + draw(program, g); + +#ifdef RENDERER_DEBUG + geometryNodesDrawn++; +#endif + } + //qDebug("Clip: %i, shader program: %i, material: %i times changed while drawing %s items", + // clipChangeCount, programChangeCount, materialChangeCount, + // &list == &m_transparentNodes ? "transparent" : "opaque"); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h b/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h new file mode 100644 index 0000000000..a4c2ffbdbc --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLRENDERER_H +#define QMLRENDERER_H + +#include "qsgrenderer_p.h" + +#include <QtGui/private/qdatabuffer_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGDefaultRenderer : public QSGRenderer +{ + Q_OBJECT +public: + class IndexGeometryNodePair : public QPair<int, QSGGeometryNode *> + { + public: + IndexGeometryNodePair(int i, QSGGeometryNode *n); + bool operator < (const IndexGeometryNodePair &other) const; + }; + + + // Minimum heap. + class IndexGeometryNodePairHeap + { + public: + IndexGeometryNodePairHeap(); + void insert(const IndexGeometryNodePair &x); + const IndexGeometryNodePair &top() const { return v.first(); } + IndexGeometryNodePair pop(); + bool isEmpty() const { return v.isEmpty(); } + private: + static int parent(int i) { return (i - 1) >> 1; } + static int left(int i) { return (i << 1) | 1; } + static int right(int i) { return (i + 1) << 1; } + QDataBuffer<IndexGeometryNodePair> v; + }; + + QSGDefaultRenderer(QSGContext *context); + + void render(); + + void nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags); + + void setSortFrontToBackEnabled(bool sort); + bool isSortFrontToBackEnabled() const; + +private: + void buildLists(QSGNode *node); + void renderNodes(const QDataBuffer<QSGGeometryNode *> &list); + + const QSGClipNode *m_currentClip; + QSGMaterial *m_currentMaterial; + QSGMaterialShader *m_currentProgram; + const QMatrix4x4 *m_currentMatrix; + QMatrix4x4 m_renderOrderMatrix; + QDataBuffer<QSGGeometryNode *> m_opaqueNodes; + QDataBuffer<QSGGeometryNode *> m_transparentNodes; + QDataBuffer<QSGGeometryNode *> m_tempNodes; + IndexGeometryNodePairHeap m_heap; + + bool m_rebuild_lists; + bool m_needs_sorting; + bool m_sort_front_to_back; + int m_currentRenderOrder; + +#ifdef QML_RUNTIME_TESTING + bool m_render_opaque_nodes; + bool m_render_alpha_nodes; +#endif +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLRENDERER_H diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp new file mode 100644 index 0000000000..8661c9af93 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp @@ -0,0 +1,466 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt scene graph research project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsggeometry.h" +#include "qsggeometry_p.h" + +#include <qopenglcontext.h> +#include <qopenglfunctions.h> +#include <private/qopenglextensions_p.h> + +QT_BEGIN_NAMESPACE + + +QSGGeometry::Attribute QSGGeometry::Attribute::create(int attributeIndex, int tupleSize, int primitiveType, bool isPrimitive) +{ + Attribute a = { attributeIndex, tupleSize, primitiveType, isPrimitive, 0 }; + return a; +} + + +/*! + Convenience function which returns attributes to be used for 2D solid + color drawing. + */ + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D() +{ + static Attribute data[] = { + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true) + }; + static AttributeSet attrs = { 1, sizeof(float) * 2, data }; + return attrs; +} + +/*! + Convenience function which returns attributes to be used for textured 2D drawing. + */ + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_TexturedPoint2D() +{ + static Attribute data[] = { + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), + QSGGeometry::Attribute::create(1, 2, GL_FLOAT) + }; + static AttributeSet attrs = { 2, sizeof(float) * 4, data }; + return attrs; +} + +/*! + Convenience function which returns attributes to be used for per vertex colored 2D drawing. + */ + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D() +{ + static Attribute data[] = { + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), + QSGGeometry::Attribute::create(1, 4, GL_UNSIGNED_BYTE) + }; + static AttributeSet attrs = { 2, 2 * sizeof(float) + 4 * sizeof(char), data }; + return attrs; +} + + +/*! + \class QSGGeometry + \brief The QSGGeometry class provides low-level storage for graphics primitives + in the QML Scene Graph. + + The QSGGeometry class provides a few convenience attributes and attribute accessors + by default. The defaultAttributes_Point2D() function returns attributes to be used + in normal solid color rectangles, while the defaultAttributes_TexturedPoint2D function + returns attributes to be used for the common pixmap usecase. + */ + + +/*! + Constructs a geometry object based on \a attributes. + + The object allocate space for \a vertexCount vertices based on the accumulated + size in \a attributes and for \a indexCount. + + Geometry objects are constructed with GL_TRIANGLE_STRIP as default drawing mode. + + The attribute structure is assumed to be POD and the geometry object + assumes this will not go away. There is no memory management involved. + */ + +QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes, + int vertexCount, + int indexCount, + int indexType) + : m_drawing_mode(GL_TRIANGLE_STRIP) + , m_vertex_count(0) + , m_index_count(0) + , m_index_type(indexType) + , m_attributes(attributes) + , m_data(0) + , m_index_data_offset(-1) + , m_server_data(0) + , m_owns_data(false) + , m_index_usage_pattern(AlwaysUploadPattern) + , m_vertex_usage_pattern(AlwaysUploadPattern) +{ + Q_ASSERT(m_attributes.count > 0); + Q_ASSERT(m_attributes.stride > 0); + + Q_ASSERT_X(indexType != GL_UNSIGNED_INT + || static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions()) + ->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint), + "QSGGeometry::QSGGeometry", + "GL_UNSIGNED_INT is not supported, geometry will not render" + ); + + if (indexType != GL_UNSIGNED_BYTE + && indexType != GL_UNSIGNED_SHORT + && indexType != GL_UNSIGNED_INT) { + qFatal("QSGGeometry: Unsupported index type, %x.\n", indexType); + } + + + // Because allocate reads m_vertex_count, m_index_count and m_owns_data, these + // need to be set before calling allocate... + allocate(vertexCount, indexCount); +} + +/*! + \fn int QSGGeometry::sizeOfVertex() const + + Returns the size in bytes of one vertex. + + This value comes from the attributes. + */ + +/*! + \fn int QSGGeometry::sizeOfIndex() const + + Returns the byte size of the index type. + + This value is either 1 when index type is GL_UNSIGNED_BYTE or 2 when + index type is GL_UNSIGNED_SHORT. For Desktop OpenGL, GL_UNSIGNED_INT + with the value 4 is also supported. + */ + +QSGGeometry::~QSGGeometry() +{ + if (m_owns_data) + qFree(m_data); + + if (m_server_data) + delete m_server_data; +} + +/*! + \fn int QSGGeometry::vertexCount() const + + Returns the number of vertices in this geometry object. + */ + +/*! + \fn int QSGGeometry::indexCount() const + + Returns the number of indices in this geometry object. + */ + + + +/*! + \fn void *QSGGeometry::vertexData() + + Returns a pointer to the raw vertex data of this geometry object. + + \sa vertexDataAsPoint2D(), vertexDataAsTexturedPoint2D + */ + +/*! + \fn const void *QSGGeometry::vertexData() const + + Returns a pointer to the raw vertex data of this geometry object. + + \sa vertexDataAsPoint2D(), vertexDataAsTexturedPoint2D + */ + +/*! + Returns a pointer to the raw index data of this geometry object. + + \sa indexDataAsUShort(), indexDataAsUInt() + */ +void *QSGGeometry::indexData() +{ + return m_index_data_offset < 0 + ? 0 + : ((char *) m_data + m_index_data_offset); +} + +/*! + Returns a pointer to the raw index data of this geometry object. + + \sa indexDataAsUShort(), indexDataAsUInt() + */ +const void *QSGGeometry::indexData() const +{ + return m_index_data_offset < 0 + ? 0 + : ((char *) m_data + m_index_data_offset); +} + +/*! + Sets the drawing mode to be used for this geometry. + + The default value is GL_TRIANGLE_STRIP. + */ +void QSGGeometry::setDrawingMode(GLenum mode) +{ + m_drawing_mode = mode; +} + +/*! + \fn int QSGGeometry::drawingMode() const + + Returns the drawing mode of this geometry. + + The default value is GL_TRIANGLE_STRIP. + */ + +/*! + \fn int QSGGeometry::indexType() const + + Returns the primitive type used for indices in this + geometry object. + */ + + +/*! + Resizes the vertex and index data of this geometry object to fit \a vertexCount + vertices and \a indexCount indices. + + Vertex and index data will be invalidated after this call and the caller must + */ +void QSGGeometry::allocate(int vertexCount, int indexCount) +{ + if (vertexCount == m_vertex_count && indexCount == m_index_count) + return; + + m_vertex_count = vertexCount; + m_index_count = indexCount; + + bool canUsePrealloc = m_index_count <= 0; + int vertexByteSize = m_attributes.stride * m_vertex_count; + + if (m_owns_data) + qFree(m_data); + + if (canUsePrealloc && vertexByteSize <= (int) sizeof(m_prealloc)) { + m_data = (void *) &m_prealloc[0]; + m_index_data_offset = -1; + m_owns_data = false; + } else { + Q_ASSERT(m_index_type == GL_UNSIGNED_INT || m_index_type == GL_UNSIGNED_SHORT); + int indexByteSize = indexCount * (m_index_type == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32)); + m_data = (void *) qMalloc(vertexByteSize + indexByteSize); + m_index_data_offset = vertexByteSize; + m_owns_data = true; + } + + // If we have associated vbo data we could potentially crash later if + // the old buffers are used with the new vertex and index count, so we force + // an update in the renderer in that case. This is really the users responsibility + // but it is cheap for us to enforce this, so why not... + if (m_server_data) { + markIndexDataDirty(); + markVertexDataDirty(); + } + +} + +/*! + Updates the geometry \a g with the coordinates in \a rect. + + The function assumes the geometry object contains a single triangle strip + of QSGGeometry::Point2D vertices + */ +void QSGGeometry::updateRectGeometry(QSGGeometry *g, const QRectF &rect) +{ + Point2D *v = g->vertexDataAsPoint2D(); + v[0].x = rect.left(); + v[0].y = rect.top(); + + v[1].x = rect.left(); + v[1].y = rect.bottom(); + + v[2].x = rect.right(); + v[2].y = rect.top(); + + v[3].x = rect.right(); + v[3].y = rect.bottom(); +} + +/*! + Updates the geometry \a g with the coordinates in \a rect and texture + coordinates from \a textureRect. + + \a textureRect should be in normalized coordinates. + + \a g is assumed to be a triangle strip of four vertices of type + QSGGeometry::TexturedPoint2D. + */ +void QSGGeometry::updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &textureRect) +{ + TexturedPoint2D *v = g->vertexDataAsTexturedPoint2D(); + v[0].x = rect.left(); + v[0].y = rect.top(); + v[0].tx = textureRect.left(); + v[0].ty = textureRect.top(); + + v[1].x = rect.left(); + v[1].y = rect.bottom(); + v[1].tx = textureRect.left(); + v[1].ty = textureRect.bottom(); + + v[2].x = rect.right(); + v[2].y = rect.top(); + v[2].tx = textureRect.right(); + v[2].ty = textureRect.top(); + + v[3].x = rect.right(); + v[3].y = rect.bottom(); + v[3].tx = textureRect.right(); + v[3].ty = textureRect.bottom(); +} + + + +/*! + \enum QSGGeometry::DataPattern + + The DataPattern enum is used to specify the use pattern for the vertex + and index data in a geometry object. + + \value AlwaysUploadPattern The data is always uploaded. This means that + the user does not need to explicitly mark index and vertex data as + dirty after changing it. This is the default. + + \value DynamicPattern The data is modified repeatedly and drawn many times. + This is a hint that may provide better performance. When set + the user must make sure to mark the data as dirty after changing it. + + \value StaticPattern The data is modified once and drawn many times. This is + a hint that may provide better performance. When set the user must make sure + to mark the data as dirty after changing it. + */ + + +/*! + \fn QSGGeometry::DataPattern QSGGeometry::indexDataPattern() const + + Returns the usage pattern for indices in this geometry. The default + pattern is AlwaysUploadPattern. + */ + +/*! + Sets the usage pattern for indices to \a p. + + The default is AlwaysUploadPattern. When set to anything other than + the default, the user must call markIndexDataDirty() after changing + the index data. + */ + +void QSGGeometry::setIndexDataPattern(DataPattern p) +{ + m_index_usage_pattern = p; +} + + + + +/*! + \fn QSGGeometry::DataPattern QSGGeometry::vertexDataPattern() const + + Returns the usage pattern for vertices in this geometry. The default + pattern is AlwaysUploadPattern. + */ + +/*! + Sets the usage pattern for vertices to \a p. + + The default is AlwaysUploadPattern. When set to anything other than + the default, the user must call markVertexDataDirty() after changing + the vertex data. + */ + +void QSGGeometry::setVertexDataPattern(DataPattern p) +{ + m_vertex_usage_pattern = p; +} + + + + +/*! + Mark that the vertices in this geometry has changed and must be uploaded + again. + + This function only has an effect when the usage pattern for vertices is + StaticData and the renderer that renders this geometry uploads the geometry + into Vertex Buffer Objects (VBOs). + */ +void QSGGeometry::markIndexDataDirty() +{ + m_dirty_index_data = true; +} + + + +/*! + Mark that the vertices in this geometry has changed and must be uploaded + again. + + This function only has an effect when the usage pattern for vertices is + StaticData and the renderer that renders this geometry uploads the geometry + into Vertex Buffer Objects (VBOs). + */ +void QSGGeometry::markVertexDataDirty() +{ + m_dirty_vertex_data = true; +} + + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h new file mode 100644 index 0000000000..aea6f0b94b --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsggeometry.h @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt scene graph research project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGGEOMETRY_H +#define QSGGEOMETRY_H + +#include <QtQuick/qtquickglobal.h> +#include <QtGui/qopengl.h> +#include <QRectF> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGGeometryData; + +class Q_QUICK_EXPORT QSGGeometry +{ +public: + + struct Attribute + { + int position; + int tupleSize; + int type; + + uint isVertexCoordinate : 1; + uint migrateYourCodeToUseTheCreateFunction: 31; // ### Remove before release + + static Attribute create(int pos, int tupleSize, int primitiveType, bool isPosition = false); + }; + + struct AttributeSet { + int count; + int stride; + const Attribute *attributes; + }; + + struct Point2D { + float x, y; + void set(float nx, float ny) { + x = nx; y = ny; + } + }; + struct TexturedPoint2D { + float x, y; + float tx, ty; + void set(float nx, float ny, float ntx, float nty) { + x = nx; y = ny; tx = ntx; ty = nty; + } + }; + struct ColoredPoint2D { + float x, y; + unsigned char r, g, b, a; + void set(float nx, float ny, uchar nr, uchar ng, uchar nb, uchar na) { + x = nx; y = ny; + r = nr; g = ng, b = nb; a = na; + } + }; + + static const AttributeSet &defaultAttributes_Point2D(); + static const AttributeSet &defaultAttributes_TexturedPoint2D(); + static const AttributeSet &defaultAttributes_ColoredPoint2D(); + + enum DataPattern { + AlwaysUploadPattern = 0, + StreamPattern = 1, + DynamicPattern = 2, + StaticPattern = 3 + }; + + QSGGeometry(const QSGGeometry::AttributeSet &attribs, + int vertexCount, + int indexCount = 0, + int indexType = GL_UNSIGNED_SHORT); + virtual ~QSGGeometry(); + + void setDrawingMode(GLenum mode); + inline GLenum drawingMode() const { return m_drawing_mode; } + + void allocate(int vertexCount, int indexCount = 0); + + int vertexCount() const { return m_vertex_count; } + + void *vertexData() { return m_data; } + inline Point2D *vertexDataAsPoint2D(); + inline TexturedPoint2D *vertexDataAsTexturedPoint2D(); + inline ColoredPoint2D *vertexDataAsColoredPoint2D(); + + inline const void *vertexData() const { return m_data; } + inline const Point2D *vertexDataAsPoint2D() const; + inline const TexturedPoint2D *vertexDataAsTexturedPoint2D() const; + inline const ColoredPoint2D *vertexDataAsColoredPoint2D() const; + + inline int indexType() const { return m_index_type; } + + int indexCount() const { return m_index_count; } + + void *indexData(); + inline uint *indexDataAsUInt(); + inline quint16 *indexDataAsUShort(); + + inline int sizeOfIndex() const; + + const void *indexData() const; + inline const uint *indexDataAsUInt() const; + inline const quint16 *indexDataAsUShort() const; + + inline int attributeCount() const { return m_attributes.count; } + inline const Attribute *attributes() const { return m_attributes.attributes; } + inline int sizeOfVertex() const { return m_attributes.stride; } + + static void updateRectGeometry(QSGGeometry *g, const QRectF &rect); + static void updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &sourceRect); + + void setIndexDataPattern(DataPattern p); + DataPattern indexDataPattern() const { return (DataPattern) m_index_usage_pattern; } + + void setVertexDataPattern(DataPattern p); + DataPattern vertexDataPattern() const { return (DataPattern) m_vertex_usage_pattern; } + + void markIndexDataDirty(); + void markVertexDataDirty(); + +private: + friend class QSGGeometryData; + + int m_drawing_mode; + int m_vertex_count; + int m_index_count; + int m_index_type; + const AttributeSet &m_attributes; + void *m_data; + int m_index_data_offset; + + QSGGeometryData *m_server_data; + + uint m_owns_data : 1; + uint m_index_usage_pattern : 2; + uint m_vertex_usage_pattern : 2; + uint m_dirty_index_data : 1; + uint m_dirty_vertex_data : 1; + uint m_reserved_bits : 27; + + float m_prealloc[16]; +}; + +inline uint *QSGGeometry::indexDataAsUInt() +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_INT); + return (uint *) indexData(); +} + +inline quint16 *QSGGeometry::indexDataAsUShort() +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT); + return (quint16 *) indexData(); +} + +inline const uint *QSGGeometry::indexDataAsUInt() const +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_INT); + return (uint *) indexData(); +} + +inline const quint16 *QSGGeometry::indexDataAsUShort() const +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT); + return (quint16 *) indexData(); +} + +inline QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() +{ + Q_ASSERT(m_attributes.count == 1); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].position == 0); + return (Point2D *) m_data; +} + +inline QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoint2D() +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 4 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT); + return (TexturedPoint2D *) m_data; +} + +inline QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2D() +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 4); + Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE); + return (ColoredPoint2D *) m_data; +} + +inline const QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() const +{ + Q_ASSERT(m_attributes.count == 1); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].position == 0); + return (const Point2D *) m_data; +} + +inline const QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoint2D() const +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 4 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT); + return (const TexturedPoint2D *) m_data; +} + +inline const QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2D() const +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 4); + Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE); + return (const ColoredPoint2D *) m_data; +} + +int QSGGeometry::sizeOfIndex() const +{ + if (m_index_type == GL_UNSIGNED_SHORT) return 2; + else if (m_index_type == GL_UNSIGNED_BYTE) return 1; + else if (m_index_type == GL_UNSIGNED_INT) return 4; + return 0; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGGEOMETRY_H diff --git a/src/quick/scenegraph/coreapi/qsggeometry_p.h b/src/quick/scenegraph/coreapi/qsggeometry_p.h new file mode 100644 index 0000000000..ef2935ae4c --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsggeometry_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGGEOMETRY_P_H +#define QSGGEOMETRY_P_H + +#include "qsggeometry.h" + +QT_BEGIN_NAMESPACE + +class QSGGeometryData +{ +public: + virtual ~QSGGeometryData() {} + + static inline QSGGeometryData *data(const QSGGeometry *g) { + return g->m_server_data; + } + + static inline void install(const QSGGeometry *g, QSGGeometryData *data) { + Q_ASSERT(!g->m_server_data); + const_cast<QSGGeometry *>(g)->m_server_data = data; + } + + static bool inline hasDirtyVertexData(const QSGGeometry *g) { return g->m_dirty_vertex_data; } + static void inline clearDirtyVertexData(const QSGGeometry *g) { const_cast<QSGGeometry *>(g)->m_dirty_vertex_data = false; } + + static bool inline hasDirtyIndexData(const QSGGeometry *g) { return g->m_dirty_vertex_data; } + static void inline clearDirtyIndexData(const QSGGeometry *g) { const_cast<QSGGeometry *>(g)->m_dirty_index_data = false; } + +}; + +QT_END_NAMESPACE + +#endif // QSGGEOMETRY_P_H diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp new file mode 100644 index 0000000000..36b50e89b6 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp @@ -0,0 +1,535 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgmaterial.h" +#include "qsgrenderer_p.h" + +QT_BEGIN_NAMESPACE + + +/*! + \class QSGMaterialShader + \brief The QSGMaterialShader class implements a material renders geometry. + + The QSGMaterial and QSGMaterialShader form a tight relationship. For one + scene graph (including nested graphs), there is one unique QSGMaterialShader + instance which encapsulates the QOpenGLShaderProgram the scene graph uses + to render that material, such as a shader to flat coloring of geometry. + Each QSGGeometryNode can have a unique QSGMaterial containing the + how the shader should be configured when drawing that node, such as + the actual color to used to render the geometry. + + An instance of QSGMaterialShader is never created explicitely by the user, + it will be created on demand by the scene graph through + QSGMaterial::createShader(). The scene graph will make sure that there + is only one instance of each shader implementation through a scene graph. + + The source code returned from vertexShader() is used to control what the + material does with the vertiex data that comes in from the geometry. + The source code returned from the fragmentShader() is used to control + what how the material should fill each individual pixel in the geometry. + The vertex and fragment source code is queried once during initialization, + changing what is returned from these functions later will not have + any effect. + + The activate() function is called by the scene graph when a shader is + is starting to be used. The deactivate function is called by the scene + graph when the shader is no longer going to be used. While active, + the scene graph may make one or more calls to updateState() which + will update the state of the shader for each individual geometry to + render. + + The attributeNames() returns the name of the attributes used in the + vertexShader(). These are used in the default implementation of + activate() and deactive() to decide whice vertex registers are enabled. + + The initialize() function is called during program creation to allow + subclasses to prepare for use, such as resolve uniform names in the + vertexShader() and fragmentShader(). + + A minimal example: + \code + class Shader : public QSGMaterialShader + { + public: + const char *vertexShader() const { + return + "attribute highp vec4 vertex; \n" + "uniform highp mat4 matrix; \n" + "void main() { \n" + " gl_Position = matrix * vertex; \n" + "}"; + } + + const char *fragmentShader() const { + return + "uniform lowp float opacity; \n" + "void main() { \n" + " gl_FragColor = vec4(1, 0, 0, 1) * opacity; \n" + "}"; + } + + char const *const *attributeNames() const + { + static char const *const names[] = { "vertex", 0 }; + return names; + } + + void initialize() + { + QSGMaterialShader::initialize(); + m_id_matrix = program()->uniformLocation("matrix"); + m_id_opacity = program()->uniformLocation("opacity"); + } + + void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) + { + Q_ASSERT(program()->isLinked()); + if (state.isMatrixDirty()) + program()->setUniformValue(m_id_matrix, state.combinedMatrix()); + if (state.isOpacityDirty()) + program()->setUniformValue(m_id_opacity, state.opacity()); + } + + private: + int m_id_matrix; + int m_id_opacity; + }; + \endcode + + \warning Instances of QSGMaterialShader belongs to the Scene Graph rendering + thread, and cannot be used from the GUI thread. + + */ + + + +/*! + Creates a new QSGMaterialShader. + */ +QSGMaterialShader::QSGMaterialShader() +{ +} + + + +/*! + \fn QOpenGLShaderProgram *QSGMaterialShader::program() const + + Returns the shader program used by this QSGMaterialShader. + */ + + + +/*! + This function is called by the scene graph to indicate that geometry is + about to be rendered using this shader. + + State that is global for all uses of the shader, independent of the geometry + that is being drawn, can be setup in this function. + + If reimplemented, make sure to either call the base class implementation to + enable the vertex attribute registers. + */ + +void QSGMaterialShader::activate() +{ + Q_ASSERT(program()->isLinked()); + + program()->bind(); + char const *const *attr = attributeNames(); + for (int i = 0; attr[i]; ++i) { + if (*attr[i]) + program()->enableAttributeArray(i); + } +} + + + +/*! + This function is called by the scene graph to indicate that geometry will + no longer to be rendered using this shader. + + If reimplemented, make sure to either call the base class implementation to + disable the vertex attribute registers. + */ + +void QSGMaterialShader::deactivate() +{ + char const *const *attr = attributeNames(); + for (int i = 0; attr[i]; ++i) { + if (*attr[i]) + program()->disableAttributeArray(i); + } +} + + + +/*! + This function is called by the scene graph before geometry is rendered + to make sure the shader is in the right state. + + The current rendering \a state is passed from the scene graph. If the state + indicates that any state is dirty, the updateState implementation must + update accordingly for the geometry to render correctly. + + The subclass specific state, such as the color of a flat color material, should + be extracted from \a newMaterial to update the color uniforms accordingly. + + The \a oldMaterial can be used to minimze state changes when updating + material states. The \a oldMaterial is 0 if this shader was just activated. + + \sa activate(), deactivate() + */ + +void QSGMaterialShader::updateState(const RenderState & /* state */, QSGMaterial * /* newMaterial */, QSGMaterial * /* oldMaterial */) +{ +} + + + +/*! + This function is called when the shader is initialized to compile the + actual QOpenGLShaderProgram. Do not call it explicitely. + + The default implementation will extract the vertexShader() and + fragmentShader() and bind the names returned from attributeNames() + to consecutive vertex attribute registers starting at 0. + */ + +void QSGMaterialShader::compile() +{ + Q_ASSERT_X(!m_program.isLinked(), "QSGSMaterialShader::compile()", "Compile called multiple times!"); + + program()->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader()); + program()->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader()); + + char const *const *attr = attributeNames(); +#ifndef QT_NO_DEBUG + int maxVertexAttribs = 0; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs); + for (int i = 0; attr[i]; ++i) { + if (i >= maxVertexAttribs) { + qFatal("List of attribute names is either too long or not null-terminated.\n" + "Maximum number of attributes on this hardware is %i.\n" + "Vertex shader:\n%s\n" + "Fragment shader:\n%s\n", + maxVertexAttribs, vertexShader(), fragmentShader()); + } + if (*attr[i]) + program()->bindAttributeLocation(attr[i], i); + } +#else + for (int i = 0; attr[i]; ++i) { + if (*attr[i]) + program()->bindAttributeLocation(attr[i], i); + } +#endif + + if (!program()->link()) { + qWarning("QSGMaterialShader: Shader compilation failed:"); + qWarning() << program()->log(); + } +} + + + +/*! + \class QSGMaterialShader::RenderState + \brief The QSGMaterialShader::RenderState encapsulates the current rendering state + during a call to QSGMaterialShader::updateState(). + + The render state contains a number of accessors that the shader needs to respect + in order to conform to the current state of the scene graph. + + The instance is only valid inside a call to QSGMaterialShader::updateState() and + should not be used outisde this function. + */ + + + +/*! + \enum QSGMaterialShader::RenderState::DirtyState + + \value DirtyMatrix Used to indicate that the matrix has changed and must be updated. + + \value DirtyOpacity Used to indicate that the opacity has changed and must be updated. + */ + + + +/*! + \fn bool QSGMaterialShader::RenderState::isMatrixDirty() const + + Convenience function to check if the dirtyStates() indicates that the matrix + needs to be updated. + */ + + + +/*! + \fn bool QSGMaterialShader::RenderState::isOpacityDirty() const + + Conveience function to check if the dirtyStates() indicates that the opacity + needs to be updated. + */ + + + +/*! + \fn QSGMaterialShader::RenderState::DirtyStates QSGMaterialShader::RenderState::dirtyStates() const + + Returns which rendering states that have changed and needs to be updated + for geometry rendered with this material to conform to the current + rendering state. + */ + + + +/*! + Returns the accumulated opacity to be used for rendering + */ + +float QSGMaterialShader::RenderState::opacity() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->currentOpacity(); +} + + + +/*! + Returns the matrix combined of modelview matrix and project matrix. + */ + +QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix(); +} + + + +/*! + Returns the model view matrix. + */ + +QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix(); +} + + + +/*! + Returns the viewport rect of the surface being rendered to. + */ + +QRect QSGMaterialShader::RenderState::viewportRect() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->viewportRect(); +} + + + +/*! + Returns the device rect of the surface being rendered to + */ + +QRect QSGMaterialShader::RenderState::deviceRect() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->deviceRect(); +} + + + +/*! + Returns the QOpenGLContext that is being used for rendering + */ + +QOpenGLContext *QSGMaterialShader::RenderState::context() const +{ + return static_cast<const QSGRenderer *>(m_data)->glContext(); +} + + +#ifndef QT_NO_DEBUG +static int qt_material_count = 0; + +static void qt_print_material_count() +{ + qDebug("Number of leaked materials: %i", qt_material_count); + qt_material_count = -1; +} +#endif + +/*! + \class QSGMaterialType + \brief The QSGMaterialType class is used as a unique type token in combination with QSGMaterial. + + It serves no purpose outside the QSGMaterial::type() function. + */ + +/*! + \class QSGMaterial + \brief The QSGMaterial class encapsulates rendering state for a shader program. + + The QSGMaterial and QSGMaterialShader subclasses form a tight relationship. For + one scene graph (including nested graphs), there is one unique QSGMaterialShader + instance which encapsulates the QOpenGLShaderProgram the scene graph uses + to render that material, such as a shader to flat coloring of geometry. + Each QSGGeometryNode can have a unique QSGMaterial containing the + how the shader should be configured when drawing that node, such as + the actual color to used to render the geometry. + + The QSGMaterial has two virtual functions that both need to be implemented. + The function type() should return a unique instance for all instances of a + specific subclass. The createShader() function should return a new instance + of QSGMaterialShader, specific to the subclass of QSGMaterial. + + A minimal QSGMaterial implementation could look like this: + \code + class Material : public QSGMaterial + { + public: + QSGMaterialType *type() const { static QSGMaterialType type; return &type; } + QSGMaterialShader *createShader() const { return new Shader; } + }; + \endcode + + \warning Instances of QSGMaterial belongs to the Scene Graph rendering thread, + and cannot be used from the GUI thread. + */ + +QSGMaterial::QSGMaterial() + : m_flags(0) +{ +#ifndef QT_NO_DEBUG + ++qt_material_count; + static bool atexit_registered = false; + if (!atexit_registered) { + atexit(qt_print_material_count); + atexit_registered = true; + } +#endif +} + +QSGMaterial::~QSGMaterial() +{ +#ifndef QT_NO_DEBUG + --qt_material_count; + if (qt_material_count < 0) + qDebug("Material destroyed after qt_print_material_count() was called."); +#endif +} + + + +/*! + \enum QSGMaterial::Flag + + \value Blending Set this flag to true if the material requires GL_BLEND to be + enabled during rendering. + */ + + + +/*! + Sets the flags \a flags on this material if \a on is true; + otherwise clears the attribute. +*/ + +void QSGMaterial::setFlag(Flags flags, bool on) +{ + if (on) + m_flags |= flags; + else + m_flags &= ~flags; +} + + + +/*! + Compares this material to \a other and returns 0 if they are equal; -1 if + this material should sort before \a other and 1 if \a other should sort + before. + + The scene graph can reorder geometry nodes to minimize state changes. + The compare function is called during the sorting process so that + the materials can be sorted to minimize state changes in each + call to QSGMaterialShader::updateState(). + + The this pointer and \a other is guaranteed to have the same type(). + */ + +int QSGMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + return qint64(this) - qint64(other); +} + + + +/*! + \fn QSGMaterialType QSGMaterial::type() const + + This function is called by the scene graph to return a unique instance + per subclass. + */ + + + +/*! + \fn QSGMaterialShader *QSGMaterial::createShader() const + + This function returns a new instance of a the QSGMaterialShader + implementatation used to render geometry for a specifc implementation + of QSGMaterial. + + The function will be called only once for each material type that + exists in the scene graph and will be cached internally. +*/ + + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h new file mode 100644 index 0000000000..d8411b4d88 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgmaterial.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MATERIAL_H +#define MATERIAL_H + +#include <QtQuick/qtquickglobal.h> +#include <qopenglshaderprogram.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGMaterial; + +class Q_QUICK_EXPORT QSGMaterialShader +{ +public: + class Q_QUICK_EXPORT RenderState { + public: + enum DirtyState + { + DirtyMatrix = 0x0001, + DirtyOpacity = 0x0002 + }; + Q_DECLARE_FLAGS(DirtyStates, DirtyState) + + inline DirtyStates dirtyStates() const { return m_dirty; } + + inline bool isMatrixDirty() const { return m_dirty & DirtyMatrix; } + inline bool isOpacityDirty() const { return m_dirty & DirtyOpacity; } + + float opacity() const; + QMatrix4x4 combinedMatrix() const; + QMatrix4x4 modelViewMatrix() const; + QRect viewportRect() const; + QRect deviceRect() const; + + QOpenGLContext *context() const; + + private: + friend class QSGRenderer; + DirtyStates m_dirty; + const void *m_data; + }; + + QSGMaterialShader(); + + virtual void activate(); + virtual void deactivate(); + // First time a material is used, oldMaterial is null. + virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial); + virtual char const *const *attributeNames() const = 0; // Array must end with null. + + inline QOpenGLShaderProgram *program() { return &m_program; } + +protected: + + friend class QSGContext; + + virtual void compile(); + virtual void initialize() { } + + virtual const char *vertexShader() const = 0; + virtual const char *fragmentShader() const = 0; + +private: + QOpenGLShaderProgram m_program; + void *m_reserved; +}; + +struct QSGMaterialType { }; + +class Q_QUICK_EXPORT QSGMaterial +{ +public: + enum Flag { + Blending = 0x0001 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + QSGMaterial(); + virtual ~QSGMaterial(); + + virtual QSGMaterialType *type() const = 0; + virtual QSGMaterialShader *createShader() const = 0; + virtual int compare(const QSGMaterial *other) const; + + QSGMaterial::Flags flags() const { return m_flags; } + void setFlag(Flags flags, bool on = true); + +private: + Flags m_flags; + void *m_reserved; + Q_DISABLE_COPY(QSGMaterial) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterial::Flags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialShader::RenderState::DirtyStates) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp new file mode 100644 index 0000000000..2d6828323c --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -0,0 +1,1264 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgnode.h" +#include "qsgrenderer_p.h" +#include "qsgnodeupdater_p.h" +#include "qsgmaterial.h" + +#include "limits.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_DEBUG +static int qt_node_count = 0; + +static void qt_print_node_count() +{ + qDebug("Number of leaked nodes: %i", qt_node_count); + qt_node_count = -1; +} +#endif + +/*! + \class QSGNode + \brief The QSGNode class is the base class for all nodes in the scene graph. + + \inmodule QtQuick + + The QSGNode class can be used as a child container. Children are added with + the appendChildNode(), prependChildNode(), insertChildNodeBefore() and + insertChildNodeAfter(). Ordering of nodes is important as geometry nodes + will be rendered in the order they are added to the scene graph. + Actually, the scene may reorder nodes freely, but the resulting visual + order is still guaranteed. + + If nodes change every frame, the preprocess() function can be used to + apply changes to a node for every frame its rendered. The use of preprocess() + must be explicitly enabled by setting the QSGNode::UsePreprocess flag + on the node. + + The virtual isSubtreeBlocked() function can be used to disable a subtree all + together. Nodes in a blocked subtree will not be preprocessed() and not + rendered. + + Anything related to QSGNode should happen on the scene graph rendering thread. + */ + +QSGNode::QSGNode() + : m_parent(0) + , m_type(BasicNodeType) + , m_firstChild(0) + , m_lastChild(0) + , m_nextSibling(0) + , m_previousSibling(0) + , m_subtreeGeometryCount(0) + , m_nodeFlags(OwnedByParent) + , m_flags(0) +{ + init(); +} + +QSGNode::QSGNode(NodeType type) + : m_parent(0) + , m_type(type) + , m_firstChild(0) + , m_lastChild(0) + , m_nextSibling(0) + , m_previousSibling(0) + , m_subtreeGeometryCount(type == GeometryNodeType ? 1 : 0) + , m_nodeFlags(OwnedByParent) + , m_flags(0) +{ + init(); +} + +void QSGNode::init() +{ +#ifndef QT_NO_DEBUG + ++qt_node_count; + static bool atexit_registered = false; + if (!atexit_registered) { + atexit(qt_print_node_count); + atexit_registered = true; + } +#endif +} + +QSGNode::~QSGNode() +{ +#ifndef QT_NO_DEBUG + --qt_node_count; + if (qt_node_count < 0) + qDebug("Node destroyed after qt_print_node_count() was called."); +#endif + destroy(); +} + + +/*! + \fn void QSGNode::preprocess() + + Override this function to do processing on the node before it is rendered. + + Preprocessing needs to be explicitly enabled by setting the flag + QSGNode::UsePreprocess. The flag needs to be set before the node is added + to the scene graph and will cause the preprocess() function to be called + for every frame the node is rendered. + + The preprocess function is called before the update pass that propegates + opacity and transformations through the scene graph. That means that + functions like QSGOpacityNode::combinedOpacity() and + QSGTransformNode::combinedMatrix() will not contain up-to-date values. + If such values are changed during the preprocess, these changes will be + propegated through the scene graph before it is rendered. + + \warning Beware of deleting nodes while they are being preprocessed. It is + possible, with a small performance hit, to delete a single node during its + own preprocess call. Deleting a subtree which has nodes that also use + preprocessing may result in a segmentation fault. This is done for + performance reasons. + */ + + + + +/*! + Returns whether this node and its subtree is available for use. + + Blocked subtrees will not get their dirty states updated and they + will not be rendered. + + The QSGOpacityNode will return a blocked subtree when accumulated opacity + is 0, for instance. + */ + +bool QSGNode::isSubtreeBlocked() const +{ + return m_subtreeGeometryCount == 0; +} + +/*! + \internal + Detaches the node from the scene graph and deletes any children it owns. + + This function is called from QSGNode's and QSGRootNode's destructor. It + should not be called explicitly in user code. QSGRootNode needs to call + destroy() because destroy() calls removeChildNode() which in turn calls + markDirty() which type-casts the node to QSGRootNode. This type-cast is not + valid at the time QSGNode's destructor is called because the node will + already be partially destroyed at that point. +*/ + +void QSGNode::destroy() +{ + if (m_parent) { + m_parent->removeChildNode(this); + Q_ASSERT(m_parent == 0); + } + while (m_firstChild) { + QSGNode *child = m_firstChild; + removeChildNode(child); + Q_ASSERT(child->m_parent == 0); + if (child->flags() & OwnedByParent) + delete child; + } + + Q_ASSERT(m_firstChild == 0 && m_lastChild == 0); +} + + +/*! + Prepends \a node to this node's the list of children. + + Ordering of nodes is important as geometry nodes will be rendered in the + order they are added to the scene graph. + */ + +void QSGNode::prependChildNode(QSGNode *node) +{ + //Q_ASSERT_X(!m_children.contains(node), "QSGNode::prependChildNode", "QSGNode is already a child!"); + Q_ASSERT_X(!node->m_parent, "QSGNode::prependChildNode", "QSGNode already has a parent"); + +#ifndef QT_NO_DEBUG + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); + Q_ASSERT_X(g->material(), "QSGNode::prependChildNode", "QSGGeometryNode is missing material"); + Q_ASSERT_X(g->geometry(), "QSGNode::prependChildNode", "QSGGeometryNode is missing geometry"); + } +#endif + + if (m_firstChild) + m_firstChild->m_previousSibling = node; + else + m_lastChild = node; + node->m_nextSibling = m_firstChild; + m_firstChild = node; + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + +/*! + Appends \a node to this node's list of children. + + Ordering of nodes is important as geometry nodes will be rendered in the + order they are added to the scene graph. + */ + +void QSGNode::appendChildNode(QSGNode *node) +{ + //Q_ASSERT_X(!m_children.contains(node), "QSGNode::appendChildNode", "QSGNode is already a child!"); + Q_ASSERT_X(!node->m_parent, "QSGNode::appendChildNode", "QSGNode already has a parent"); + +#ifndef QT_NO_DEBUG + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); + Q_ASSERT_X(g->material(), "QSGNode::appendChildNode", "QSGGeometryNode is missing material"); + Q_ASSERT_X(g->geometry(), "QSGNode::appendChildNode", "QSGGeometryNode is missing geometry"); + } +#endif + + if (m_lastChild) + m_lastChild->m_nextSibling = node; + else + m_firstChild = node; + node->m_previousSibling = m_lastChild; + m_lastChild = node; + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + + + +/*! + Inserts \a node to this node's list of children before the node specified with \a before. + + Ordering of nodes is important as geometry nodes will be rendered in the + order they are added to the scene graph. + */ + +void QSGNode::insertChildNodeBefore(QSGNode *node, QSGNode *before) +{ + //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeBefore", "QSGNode is already a child!"); + Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeBefore", "QSGNode already has a parent"); + Q_ASSERT_X(before && before->m_parent == this, "QSGNode::insertChildNodeBefore", "The parent of \'before\' is wrong"); + +#ifndef QT_NO_DEBUG + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); + Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeBefore", "QSGGeometryNode is missing material"); + Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeBefore", "QSGGeometryNode is missing geometry"); + } +#endif + + QSGNode *previous = before->m_previousSibling; + if (previous) + previous->m_nextSibling = node; + else + m_firstChild = node; + node->m_previousSibling = previous; + node->m_nextSibling = before; + before->m_previousSibling = node; + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + + + +/*! + Inserts \a node to this node's list of children after the node specified with \a after. + + Ordering of nodes is important as geometry nodes will be rendered in the + order they are added to the scene graph. + */ + +void QSGNode::insertChildNodeAfter(QSGNode *node, QSGNode *after) +{ + //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeAfter", "QSGNode is already a child!"); + Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeAfter", "QSGNode already has a parent"); + Q_ASSERT_X(after && after->m_parent == this, "QSGNode::insertChildNodeBefore", "The parent of \'before\' is wrong"); + +#ifndef QT_NO_DEBUG + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); + Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeAfter", "QSGGeometryNode is missing material"); + Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeAfter", "QSGGeometryNode is missing geometry"); + } +#endif + + QSGNode *next = after->m_nextSibling; + if (next) + next->m_previousSibling = node; + else + m_lastChild = node; + node->m_nextSibling = next; + node->m_previousSibling = after; + after->m_nextSibling = node; + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + + + +/*! + Removes \a node from this node's list of children. + */ + +void QSGNode::removeChildNode(QSGNode *node) +{ + //Q_ASSERT(m_children.contains(node)); + Q_ASSERT(node->parent() == this); + + QSGNode *previous = node->m_previousSibling; + QSGNode *next = node->m_nextSibling; + if (previous) + previous->m_nextSibling = next; + else + m_firstChild = next; + if (next) + next->m_previousSibling = previous; + else + m_lastChild = previous; + node->m_previousSibling = 0; + node->m_nextSibling = 0; + + node->markDirty(DirtyNodeRemoved); + node->m_parent = 0; +} + + +/*! + Removes all child nodes from this node's list of children. + */ + +void QSGNode::removeAllChildNodes() +{ + while (m_firstChild) { + QSGNode *node = m_firstChild; + m_firstChild = node->m_nextSibling; + node->m_nextSibling = 0; + if (m_firstChild) + m_firstChild->m_previousSibling = 0; + else + m_lastChild = 0; + node->markDirty(DirtyNodeRemoved); + node->m_parent = 0; + } +} + + +int QSGNode::childCount() const +{ + int count = 0; + QSGNode *n = m_firstChild; + while (n) { + ++count; + n = n->m_nextSibling; + } + return count; +} + + +QSGNode *QSGNode::childAtIndex(int i) const +{ + QSGNode *n = m_firstChild; + while (i && n) { + --i; + n = n->m_nextSibling; + } + return n; +} + + +/*! + Sets the flag \a f on this node if \a enabled is true; + otherwise clears the flag. + + \sa flags() +*/ + +void QSGNode::setFlag(Flag f, bool enabled) +{ + if (enabled) + m_nodeFlags |= f; + else + m_nodeFlags &= ~f; +} + + +/*! + Sets the flags \a f on this node if \a enabled is true; + otherwise clears the flags. + + \sa flags() +*/ + +void QSGNode::setFlags(Flags f, bool enabled) +{ + if (enabled) + m_nodeFlags |= f; + else + m_nodeFlags &= ~f; +} + + + +/*! + Marks this node with the states in \a flags as dirty. + + When a node is marked dirty, it recursively mark the parent chain + as dirty and notify all connected renderers that the has dirty states. + */ + +void QSGNode::markDirty(DirtyFlags flags) +{ + m_flags |= (flags & DirtyPropagationMask); + + DirtyFlags subtreeFlags = DirtyFlags((flags & DirtyPropagationMask) << 16); + + int geometryCountDiff = 0; + if (flags & DirtyNodeAdded) + geometryCountDiff += m_subtreeGeometryCount; + if (flags & DirtyNodeRemoved) + geometryCountDiff -= m_subtreeGeometryCount; + + QSGNode *p = m_parent; + while (p) { + p->m_flags |= subtreeFlags; + p->m_subtreeGeometryCount += geometryCountDiff; + if (p->type() == RootNodeType) + static_cast<QSGRootNode *>(p)->notifyNodeChange(this, flags); + p = p->m_parent; + } +} + + + +/*! + \class QSGBasicGeometryNode + \brief The QSGBasicGeometryNode class serves as a baseclass for geometry based nodes + + \inmodule QtQuick + + The QSGBasicGeometryNode class should not be used by itself. It is only encapsulates + shared functionality between the QSGGeometryNode and QSGClipNode classes. + */ + + +/*! + Creates a new basic geometry node. + */ +QSGBasicGeometryNode::QSGBasicGeometryNode(NodeType type) + : QSGNode(type) + , m_geometry(0) + , m_matrix(0) + , m_clip_list(0) +{ +} + + +/*! + Deletes this QSGBasicGeometryNode. + + If the node has the flag QSGNode::OwnsGeometry set, it will also delete the + geometry object it is pointing to. This flag is not set by default. + */ + +QSGBasicGeometryNode::~QSGBasicGeometryNode() +{ + if (flags() & OwnsGeometry) + delete m_geometry; +} + + +/*! + \fn QSGGeometry *QSGBasicGeometryNode::geometry() const + + Returns this node's geometry. + + The geometry is null by default. + */ + + +/*! + Sets the geometry of this node to \a geometry. + + If the node has the flag QSGNode::OwnsGeometry set, it will also delete the + geometry object it is pointing to. This flag is not set by default. + */ + +void QSGBasicGeometryNode::setGeometry(QSGGeometry *geometry) +{ + if (flags() & OwnsGeometry) + delete m_geometry; + m_geometry = geometry; + markDirty(DirtyGeometry); +} + + + +/*! + \class QSGGeometryNode + \brief The QSGGeometryNode class is used for all rendered content in the scene graph. + + \inmodule QtQuick + + The QSGGeometryNode consists of geometry and material. The geometry defines the mesh, + the vertices and their structure, to be drawn. The Material defines how the shape is + filled. + + A geometry node must have both geometry and a normal material before it is added to + the scene graph. + + The geometry node supports two types of materials, the opaqueMaterial and the normal + material. The opaqueMaterial is used when the accumulated scene graph opacity at the + time of rendering is 1. The primary usecase is to special case opaque rendering + to avoid an extra operation in the fragment shader can have significant performance + impact on embedded graphics chips. The opaque material is optional. + + */ + + +/*! + Creates a new geometry node without geometry and material. + */ + +QSGGeometryNode::QSGGeometryNode() + : QSGBasicGeometryNode(GeometryNodeType) + , m_render_order(0) + , m_material(0) + , m_opaque_material(0) + , m_opacity(1) +{ +} + + +/*! + Deletes this geometry node. + + The flags QSGNode::OwnsMaterial, QSGNode::OwnsOpaqueMaterial and + QSGNode::OwnsGeometry decides weither the geometry node should also + delete the materials and geometry. By default, these flags are disabled. + */ + +QSGGeometryNode::~QSGGeometryNode() +{ + if (flags() & OwnsMaterial) + delete m_material; + if (flags() & OwnsOpaqueMaterial) + delete m_opaque_material; +} + + + +/*! + \fn int QSGGeometryNode::renderOrder() const + + Returns the render order of this geometry node. + + \internal + */ + + +/*! + Sets the render order of this node to be \a order. + + GeometryNodes are rendered in an order that visually looks like + low order nodes are rendered prior to high order nodes. For opaque + geometry there is little difference as z-testing will handle + the discard, but for translucent objects, the rendering should + normally be specified in the order of back-to-front. + + The default render order is 0. + + \internal + */ +void QSGGeometryNode::setRenderOrder(int order) +{ + m_render_order = order; +} + + + +/*! + Sets the material of this geometry node to \a material. + + Geometry nodes must have a material before they can be added to the + scene graph. + */ +void QSGGeometryNode::setMaterial(QSGMaterial *material) +{ + if (flags() & OwnsMaterial) + delete m_material; + m_material = material; +#ifndef QT_NO_DEBUG + if (m_material != 0 && m_opaque_material == m_material) + qWarning("QSGGeometryNode: using same material for both opaque and translucent"); +#endif + markDirty(DirtyMaterial); +} + + + +/*! + Sets the opaque material of this geometry to \a material. + + The opaque material will be preferred by the renderer over the + default material, as returned by the material() function, if + it is not null and the geometry item has an inherited opacity of + 1. + + The opaqueness refers to scene graph opacity, the material is still + allowed to set QSGMaterial::Blending to true and draw transparent + pixels. + */ +void QSGGeometryNode::setOpaqueMaterial(QSGMaterial *material) +{ + if (flags() & OwnsOpaqueMaterial) + delete m_opaque_material; + m_opaque_material = material; +#ifndef QT_NO_DEBUG + if (m_opaque_material != 0 && m_opaque_material == m_material) + qWarning("QSGGeometryNode: using same material for both opaque and translucent"); +#endif + + markDirty(DirtyMaterial); +} + + + +/*! + Returns the material which should currently be used for geometry node. + + If the inherited opacity of the node is 1 and there is an opaque material + set on this node, it will be returned; otherwise, the default material + will be returned. + + \warning This function requires the scene graph above this item to be + completely free of dirty states, so it can only be called during rendering + + \internal + + \sa setMaterial, setOpaqueMaterial + */ +QSGMaterial *QSGGeometryNode::activeMaterial() const +{ + Q_ASSERT_X(dirtyFlags() == 0, "QSGGeometryNode::activeMaterial()", "function assumes that all dirty states are cleaned up"); + if (m_opaque_material && m_opacity > 0.999) + return m_opaque_material; + return m_material; +} + + +/*! + Sets the inherited opacity of this geometry to \a opacity. + + This function is meant to be called by the node preprocessing + prior to rendering the tree, so it will not mark the tree as + dirty. + + \internal + */ +void QSGGeometryNode::setInheritedOpacity(qreal opacity) +{ + Q_ASSERT(opacity >= 0 && opacity <= 1); + m_opacity = opacity; +} + + +/*! + \class QSGClipNode + \brief The QSGClipNode class implements the clipping functionality in the scene graph. + + \inmodule QtQuick + + Clipping applies to the node's subtree and can be nested. Multiple clip nodes will be + accumulated by intersecting all their geometries. The accumulation happens + as part of the rendering. + + Clip nodes must have a geometry before they can be added to the scene graph. + + Clipping is usually implemented by using the stencil buffer. + */ + + + +/*! + Creates a new QSGClipNode without a geometry. + + The clip node must have a geometry before it can be added to the + scene graph. + */ + +QSGClipNode::QSGClipNode() + : QSGBasicGeometryNode(ClipNodeType) +{ +} + + + +/*! + Deletes this QSGClipNode. + + If the flag QSGNode::OwnsGeometry is set, the geometry will also be + deleted. + */ + +QSGClipNode::~QSGClipNode() +{ +} + + + +/*! + \fn bool QSGClipNode::isRectangular() const + + Returns if this clip node has a rectangular clip. + */ + + + +/*! + Sets whether this clip node has a rectangular clip to \a rectHint. + + This is an optimization hint which means that the renderer can + use scissoring instead of stencil, which is significnatly faster. + + When this hint is and it is applicable, the clip region will be + generated from clipRect() rather than geometry(). + */ + +void QSGClipNode::setIsRectangular(bool rectHint) +{ + m_is_rectangular = rectHint; +} + + + +/*! + \fn void QSGClipNode::clipRect() const + + Returns the clip rect of this node. + */ + + +/*! + Sets the clip rect of this clip node to \a rect. + + When a rectangular clip is set in combination with setIsRectangular + the renderer may in some cases use a more optimal clip method. + */ +void QSGClipNode::setClipRect(const QRectF &rect) +{ + m_clip_rect = rect; +} + + +/*! + \class QSGTransformNode + \brief The QSGTransformNode class implements transformations in the scene graph + + \inmodule QtQuick + + Transformations apply the node's subtree and can be nested. Multiple transform nodes + will be accumulated by intersecting all their matrices. The accumulation happens + as part of the rendering. + + The transform nodes implement a 4x4 matrix which in theory supports full 3D + transformations. However, because the renderer optimizes for 2D use-cases rather + than 3D use-cases, rendering a scene with full 3D transformations needs to + be done with some care. + */ + +QSGTransformNode::QSGTransformNode() + : QSGNode(TransformNodeType) +{ +} + + + +/*! + Deletes this transform node. + */ + +QSGTransformNode::~QSGTransformNode() +{ +} + + + +/*! + \fn QMatrix4x4 QSGTransformNode::matrix() const + + Returns this transform node's matrix. + */ + + + +/*! + Sets this transform node's matrix to \a matrix. + */ + +void QSGTransformNode::setMatrix(const QMatrix4x4 &matrix) +{ + m_matrix = matrix; + markDirty(DirtyMatrix); +} + + +/*! + Sets the combined matrix of this matrix to \a transform. + + This function is meant to be called by the node preprocessing + prior to rendering the tree, so it will not mark the tree as + dirty. + + \internal + */ +void QSGTransformNode::setCombinedMatrix(const QMatrix4x4 &matrix) +{ + m_combined_matrix = matrix; +} + + + +/*! + \class QSGRootNode + \brief The QSGRootNode is the toplevel root of any scene graph. + + The root node is used to attach a scene graph to a renderer. + + \internal + */ + + + +/*! + \fn QSGRootNode::QSGRootNode() + + Creates a new root node. + */ + +QSGRootNode::QSGRootNode() + : QSGNode(RootNodeType) +{ +} + + +/*! + Deletes the root node. + + When a root node is deleted it removes itself from all of renderers + that are referencing it. + */ + +QSGRootNode::~QSGRootNode() +{ + while (!m_renderers.isEmpty()) + m_renderers.last()->setRootNode(0); + destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode. +} + + + +/*! + Called to notify all renderers that \a node has been marked as dirty + with \a flags. + */ + +void QSGRootNode::notifyNodeChange(QSGNode *node, DirtyFlags flags) +{ + for (int i=0; i<m_renderers.size(); ++i) { + m_renderers.at(i)->nodeChanged(node, flags); + } +} + + + +/*! + \class QSGOpacityNode + \brief The QSGOpacityNode class is used to change opacity of nodes. + + \inmodule QtQuick + + Opacity applies to its subtree and can be nested. Multiple opacity nodes + will be accumulated by multiplying their opacity. The accumulation happens + as part of the rendering. + + When nested opacity gets below a certain threshold, the subtree might + be marked as blocked, causing isSubtreeBlocked() to return true. This + is done for performance reasons. + + */ + + + +/*! + Constructs an opacity node with a default opacity of 1. + + Opacity accumulates downwards in the scene graph so a node with two + QSGOpacityNode instances above it, both with opacity of 0.5, will have + effective opacity of 0.25. + + The default opacity of nodes is 1. + */ +QSGOpacityNode::QSGOpacityNode() + : QSGNode(OpacityNodeType) + , m_opacity(1) + , m_combined_opacity(1) +{ +} + + + +/*! + Deletes the opacity node. + */ + +QSGOpacityNode::~QSGOpacityNode() +{ +} + + + +/*! + \fn qreal QSGOpacityNode::opacity() const + + Returns this opacity node's opacity. + */ + + + +/*! + Sets the opacity of this node to \a opacity. + + Before rendering the graph, the renderer will do an update pass + over the subtree to propegate the opacity to its children. + + The value will be bounded to the range 0 to 1. + */ + +void QSGOpacityNode::setOpacity(qreal opacity) +{ + opacity = qBound<qreal>(0, opacity, 1); + if (m_opacity == opacity) + return; + m_opacity = opacity; + markDirty(DirtyOpacity); +} + + + +/*! + \fn qreal QSGOpacityNode::combinedOpacity() const + + Returns this node's accumulated opacity. + + This vaule is calculated during rendering and only stored + in the opacity node temporarily. + + \internal + */ + + + +/*! + Sets the combined opacity of this node to \a opacity. + + This function is meant to be called by the node preprocessing + prior to rendering the tree, so it will not mark the tree as + dirty. + + \internal + */ + +void QSGOpacityNode::setCombinedOpacity(qreal opacity) +{ + m_combined_opacity = opacity; +} + + + +/*! + For performance reasons, we block the subtree when the opacity + is below a certain threshold. + + \internal + */ + +bool QSGOpacityNode::isSubtreeBlocked() const +{ + return QSGNode::isSubtreeBlocked() || m_opacity < 0.001; +} + + +/*! + \class QSGNodeVisitor + \brief The QSGNodeVisitor class is a helper class for traversing the scene graph. + + \internal + */ + +QSGNodeVisitor::~QSGNodeVisitor() +{ + +} + + +void QSGNodeVisitor::visitNode(QSGNode *n) +{ + switch (n->type()) { + case QSGNode::TransformNodeType: { + QSGTransformNode *t = static_cast<QSGTransformNode *>(n); + enterTransformNode(t); + visitChildren(t); + leaveTransformNode(t); + break; } + case QSGNode::GeometryNodeType: { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(n); + enterGeometryNode(g); + visitChildren(g); + leaveGeometryNode(g); + break; } + case QSGNode::ClipNodeType: { + QSGClipNode *c = static_cast<QSGClipNode *>(n); + enterClipNode(c); + visitChildren(c); + leaveClipNode(c); + break; } + case QSGNode::OpacityNodeType: { + QSGOpacityNode *o = static_cast<QSGOpacityNode *>(n); + enterOpacityNode(o); + visitChildren(o); + leaveOpacityNode(o); + break; } + default: + visitChildren(n); + break; + } +} + +void QSGNodeVisitor::visitChildren(QSGNode *n) +{ + for (QSGNode *c = n->firstChild(); c; c = c->nextSibling()) + visitNode(c); +} + + + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const QSGGeometryNode *n) +{ + if (!n) { + d << "QSGGeometryNode(null)"; + return d; + } + d << "QSGGeometryNode(" << hex << (void *) n << dec; + + const QSGGeometry *g = n->geometry(); + + if (!g) { + d << "no geometry"; + } else { + + switch (g->drawingMode()) { + case GL_TRIANGLE_STRIP: d << "strip"; break; + case GL_TRIANGLE_FAN: d << "fan"; break; + case GL_TRIANGLES: d << "triangles"; break; + default: break; + } + + d << g->vertexCount(); + + if (g->attributeCount() > 0 && g->attributes()->type == GL_FLOAT) { + float x1 = 1e10, x2 = -1e10, y1=1e10, y2=-1e10; + int stride = g->sizeOfVertex(); + for (int i = 0; i < g->vertexCount(); ++i) { + float x = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[0]; + float y = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[1]; + + x1 = qMin(x1, x); + x2 = qMax(x2, x); + y1 = qMin(y1, y); + y2 = qMax(y2, y); + } + + d << "x1=" << x1 << "y1=" << y1 << "x2=" << x2 << "y2=" << y2; + } + } + + d << "order=" << n->renderOrder(); + if (n->material()) + d << "effect=" << n->material() << "type=" << n->material()->type(); + + + d << ")"; +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << "dirty=" << hex << (int) n->dirtyFlags() << dec; + return d; +} + +QDebug operator<<(QDebug d, const QSGClipNode *n) +{ + if (!n) { + d << "QSGClipNode(null)"; + return d; + } + d << "QSGClipNode(" << hex << (void *) n << dec; + + if (n->childCount()) + d << "children=" << n->childCount(); + + d << "is rect?" << (n->isRectangular() ? "yes" : "no"); + + d << ")"; +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << "dirty=" << hex << (int) n->dirtyFlags() << dec << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); + return d; +} + +QDebug operator<<(QDebug d, const QSGTransformNode *n) +{ + if (!n) { + d << "QSGTransformNode(null)"; + return d; + } + const QMatrix4x4 m = n->matrix(); + d << "QSGTransformNode("; + d << hex << (void *) n << dec; + if (m.isIdentity()) + d << "identity"; + else if (m.determinant() == 1 && m(0, 0) == 1 && m(1, 1) == 1 && m(2, 2) == 1) + d << "translate" << m(0, 3) << m(1, 3) << m(2, 3); + else + d << "det=" << n->matrix().determinant(); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << "dirty=" << hex << (int) n->dirtyFlags() << dec << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); + d << ")"; + return d; +} + +QDebug operator<<(QDebug d, const QSGOpacityNode *n) +{ + if (!n) { + d << "QSGOpacityNode(null)"; + return d; + } + d << "QSGOpacityNode("; + d << hex << (void *) n << dec; + d << "opacity=" << n->opacity() + << "combined=" << n->combinedOpacity() + << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << "dirty=" << hex << (int) n->dirtyFlags() << dec; + d << ")"; + return d; +} + + +QDebug operator<<(QDebug d, const QSGRootNode *n) +{ + if (!n) { + d << "QSGRootNode(null)"; + return d; + } + d << "QSGRootNode" << hex << (void *) n << "dirty=" << (int) n->dirtyFlags() << dec + << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << ")"; + return d; +} + + + +QDebug operator<<(QDebug d, const QSGNode *n) +{ + if (!n) { + d << "QSGNode(null)"; + return d; + } + switch (n->type()) { + case QSGNode::GeometryNodeType: + d << static_cast<const QSGGeometryNode *>(n); + break; + case QSGNode::TransformNodeType: + d << static_cast<const QSGTransformNode *>(n); + break; + case QSGNode::ClipNodeType: + d << static_cast<const QSGClipNode *>(n); + break; + case QSGNode::RootNodeType: + d << static_cast<const QSGRootNode *>(n); + break; + case QSGNode::OpacityNodeType: + d << static_cast<const QSGOpacityNode *>(n); + break; + default: + d << "QSGNode(" << hex << (void *) n << dec + << "dirty=" << hex << (int) n->dirtyFlags() + << "flags=" << (int) n->flags() << dec + << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << ")"; + break; + } + return d; +} + +#endif + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h new file mode 100644 index 0000000000..41c63e27b6 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgnode.h @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NODE_H +#define NODE_H + +#include "qsggeometry.h" +#include <QtGui/QMatrix4x4> + +#include <float.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +#define QML_RUNTIME_TESTING + +class QSGRenderer; + +class QSGNode; +class QSGRootNode; +class QSGGeometryNode; +class QSGTransformNode; +class QSGClipNode; + +class Q_QUICK_EXPORT QSGNode +{ +public: + enum NodeType { + BasicNodeType, + RootNodeType, + GeometryNodeType, + TransformNodeType, + ClipNodeType, + OpacityNodeType, + UserNodeType = 1024 + }; + + enum DirtyFlag { + DirtyMatrix = 0x0001, + DirtyClipList = 0x0002, + DirtyNodeAdded = 0x0004, + DirtyNodeRemoved = 0x0008, + DirtyGeometry = 0x0010, + DirtyMaterial = 0x0040, + DirtyOpacity = 0x0080, + DirtyForceUpdate = 0x0100, + + DirtyPropagationMask = DirtyMatrix + | DirtyClipList + | DirtyNodeAdded + | DirtyOpacity + | DirtyForceUpdate + + }; + Q_DECLARE_FLAGS(DirtyFlags, DirtyFlag) + + enum Flag { + // Lower 16 bites reserved for general node + OwnedByParent = 0x0001, + UsePreprocess = 0x0002, + ChildrenDoNotOverlap = 0x0004, + + // Upper 16 bits reserved for node subclasses + + // QSGBasicGeometryNode + OwnsGeometry = 0x00010000, + OwnsMaterial = 0x00020000, + OwnsOpaqueMaterial = 0x00040000 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + QSGNode(); + virtual ~QSGNode(); + + QSGNode *parent() const { return m_parent; } + + void removeChildNode(QSGNode *node); + void removeAllChildNodes(); + void prependChildNode(QSGNode *node); + void appendChildNode(QSGNode *node); + void insertChildNodeBefore(QSGNode *node, QSGNode *before); + void insertChildNodeAfter(QSGNode *node, QSGNode *after); + + int childCount() const; + QSGNode *childAtIndex(int i) const; + QSGNode *firstChild() const { return m_firstChild; } + QSGNode *lastChild() const { return m_lastChild; } + QSGNode *nextSibling() const { return m_nextSibling; } + QSGNode* previousSibling() const { return m_previousSibling; } + + inline NodeType type() const { return m_type; } + + void clearDirty() { m_flags = 0; } + void markDirty(DirtyFlags flags); + DirtyFlags dirtyFlags() const { return m_flags; } + + virtual bool isSubtreeBlocked() const; + + Flags flags() const { return m_nodeFlags; } + void setFlag(Flag, bool = true); + void setFlags(Flags, bool = true); + + virtual void preprocess() { } + +#ifdef QML_RUNTIME_TESTING + QString description; +#endif + +protected: + QSGNode(NodeType type); + +private: + friend class QSGRootNode; + + void init(); + void destroy(); + + QSGNode *m_parent; + NodeType m_type; + QSGNode *m_firstChild; + QSGNode *m_lastChild; + QSGNode *m_nextSibling; + QSGNode *m_previousSibling; + int m_subtreeGeometryCount; + + Flags m_nodeFlags; + DirtyFlags m_flags; + + void *m_reserved; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGNode::DirtyFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGNode::Flags) + +class Q_QUICK_EXPORT QSGBasicGeometryNode : public QSGNode +{ +public: +// enum UsagePattern { +// Static, +// Dynamic, +// Stream +// }; +// void setUsagePattern(UsagePattern pattern); +// UsagePattern usagePattern() const { return m_pattern; } + + ~QSGBasicGeometryNode(); + + void setGeometry(QSGGeometry *geometry); + const QSGGeometry *geometry() const { return m_geometry; } + QSGGeometry *geometry() { return m_geometry; } + + const QMatrix4x4 *matrix() const { return m_matrix; } + const QSGClipNode *clipList() const { return m_clip_list; } + +protected: + QSGBasicGeometryNode(NodeType type); + +private: + friend class QSGNodeUpdater; + QSGGeometry *m_geometry; + + int m_reserved_start_index; + int m_reserved_end_index; + + const QMatrix4x4 *m_matrix; + const QSGClipNode *m_clip_list; + +// UsagePattern m_pattern; +}; + +class QSGMaterial; + +class Q_QUICK_EXPORT QSGGeometryNode : public QSGBasicGeometryNode +{ +public: + QSGGeometryNode(); + ~QSGGeometryNode(); + + void setMaterial(QSGMaterial *material); + QSGMaterial *material() const { return m_material; } + + void setOpaqueMaterial(QSGMaterial *material); + QSGMaterial *opaqueMaterial() const { return m_opaque_material; } + + QSGMaterial *activeMaterial() const; + + void setRenderOrder(int order); + int renderOrder() const { return m_render_order; } + + void setInheritedOpacity(qreal opacity); + qreal inheritedOpacity() const { return m_opacity; } + +private: + friend class QSGNodeUpdater; + + int m_render_order; + QSGMaterial *m_material; + QSGMaterial *m_opaque_material; + + qreal m_opacity; +}; + +class Q_QUICK_EXPORT QSGClipNode : public QSGBasicGeometryNode +{ +public: + QSGClipNode(); + ~QSGClipNode(); + + void setIsRectangular(bool rectHint); + bool isRectangular() const { return m_is_rectangular; } + + void setClipRect(const QRectF &); + QRectF clipRect() const { return m_clip_rect; } + +private: + uint m_is_rectangular : 1; + uint m_reserved : 31; + + QRectF m_clip_rect; +}; + + +class Q_QUICK_EXPORT QSGTransformNode : public QSGNode +{ +public: + QSGTransformNode(); + ~QSGTransformNode(); + + void setMatrix(const QMatrix4x4 &matrix); + const QMatrix4x4 &matrix() const { return m_matrix; } + + void setCombinedMatrix(const QMatrix4x4 &matrix); + const QMatrix4x4 &combinedMatrix() const { return m_combined_matrix; } + +private: + QMatrix4x4 m_matrix; + QMatrix4x4 m_combined_matrix; +}; + + +class Q_QUICK_EXPORT QSGRootNode : public QSGNode +{ +public: + QSGRootNode(); + ~QSGRootNode(); + +private: + void notifyNodeChange(QSGNode *node, DirtyFlags flags); + + friend class QSGRenderer; + friend class QSGNode; + friend class QSGGeometryNode; + + QList<QSGRenderer *> m_renderers; +}; + + +class Q_QUICK_EXPORT QSGOpacityNode : public QSGNode +{ +public: + QSGOpacityNode(); + ~QSGOpacityNode(); + + void setOpacity(qreal opacity); + qreal opacity() const { return m_opacity; } + + void setCombinedOpacity(qreal opacity); + qreal combinedOpacity() const { return m_combined_opacity; } + + bool isSubtreeBlocked() const; + +private: + qreal m_opacity; + qreal m_combined_opacity; +}; + +class Q_QUICK_EXPORT QSGNodeVisitor { +public: + virtual ~QSGNodeVisitor(); + +protected: + virtual void enterTransformNode(QSGTransformNode *) {} + virtual void leaveTransformNode(QSGTransformNode *) {} + virtual void enterClipNode(QSGClipNode *) {} + virtual void leaveClipNode(QSGClipNode *) {} + virtual void enterGeometryNode(QSGGeometryNode *) {} + virtual void leaveGeometryNode(QSGGeometryNode *) {} + virtual void enterOpacityNode(QSGOpacityNode *) {} + virtual void leaveOpacityNode(QSGOpacityNode *) {} + virtual void visitNode(QSGNode *n); + virtual void visitChildren(QSGNode *n); +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_QUICK_EXPORT QDebug operator<<(QDebug, const QSGNode *n); +Q_QUICK_EXPORT QDebug operator<<(QDebug, const QSGGeometryNode *n); +Q_QUICK_EXPORT QDebug operator<<(QDebug, const QSGTransformNode *n); +Q_QUICK_EXPORT QDebug operator<<(QDebug, const QSGOpacityNode *n); +Q_QUICK_EXPORT QDebug operator<<(QDebug, const QSGRootNode *n); + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // NODE_H diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp b/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp new file mode 100644 index 0000000000..a7e5b08dc1 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgnodeupdater_p.h" + +QT_BEGIN_NAMESPACE + +// #define QSG_UPDATER_DEBUG + +QSGNodeUpdater::QSGNodeUpdater() + : m_combined_matrix_stack(64) + , m_opacity_stack(64) + , m_current_clip(0) + , m_force_update(0) +{ + m_opacity_stack.add(1); +} + +void QSGNodeUpdater::updateStates(QSGNode *n) +{ + m_current_clip = 0; + m_force_update = 0; + + Q_ASSERT(m_opacity_stack.size() == 1); // The one we added in the constructr... + Q_ASSERT(m_combined_matrix_stack.isEmpty()); + + visitNode(n); +} + +/*! + \fn void QSGNodeUpdater::setToplevelOpacity(qreal opacity) + + Sets the toplevel opacity that will be multiplied with the + + The default opacity is 1. Any other value will cause artifacts, and is + primarily useful for debug purposes. + + The changing the value during an update pass will have undefined results + */ + +/*! + \fn qreal QSGNodeUpdater::toplevelOpacity() const + + Returns the toplevel opacity for the node updater. The default + value is 1. + */ + + +/*! + Returns true if \a node is has something that blocks it in the chain from + \a node to \a root doing a full state update pass. + + This function does not process dirty states, simply does a simple traversion + up to the top. + + The function assumes that \a root exists in the parent chain of \a node. + */ + +bool QSGNodeUpdater::isNodeBlocked(QSGNode *node, QSGNode *root) const +{ + qreal opacity = 1; + while (node != root) { + if (node->type() == QSGNode::OpacityNodeType) { + opacity *= static_cast<QSGOpacityNode *>(node)->opacity(); + if (opacity < 0.001) + return true; + } + node = node->parent(); + + Q_ASSERT_X(node, "QSGNodeUpdater::isNodeBlocked", "node is not in the subtree of root"); + } + + return false; +} + + +void QSGNodeUpdater::enterTransformNode(QSGTransformNode *t) +{ + if (t->dirtyFlags() & QSGNode::DirtyMatrix) + ++m_force_update; + +#ifdef QSG_UPDATER_DEBUG + qDebug() << "enter transform:" << t << "force=" << m_force_update; +#endif + + if (!t->matrix().isIdentity()) { + if (!m_combined_matrix_stack.isEmpty()) { + t->setCombinedMatrix(*m_combined_matrix_stack.last() * t->matrix()); + } else { + t->setCombinedMatrix(t->matrix()); + } + m_combined_matrix_stack.add(&t->combinedMatrix()); + } else { + if (!m_combined_matrix_stack.isEmpty()) { + t->setCombinedMatrix(*m_combined_matrix_stack.last()); + } else { + t->setCombinedMatrix(QMatrix4x4()); + } + } +} + + +void QSGNodeUpdater::leaveTransformNode(QSGTransformNode *t) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "leave transform:" << t; +#endif + + if (t->dirtyFlags() & QSGNode::DirtyMatrix) + --m_force_update; + + if (!t->matrix().isIdentity()) { + m_combined_matrix_stack.pop_back(); + } + +} + + +void QSGNodeUpdater::enterClipNode(QSGClipNode *c) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "enter clip:" << c; +#endif + + if (c->dirtyFlags() & QSGNode::DirtyClipList) + ++m_force_update; + + c->m_matrix = m_combined_matrix_stack.isEmpty() ? 0 : m_combined_matrix_stack.last(); + c->m_clip_list = m_current_clip; + m_current_clip = c; +} + + +void QSGNodeUpdater::leaveClipNode(QSGClipNode *c) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "leave clip:" << c; +#endif + + if (c->dirtyFlags() & QSGNode::DirtyClipList) + --m_force_update; + + m_current_clip = c->m_clip_list; +} + + +void QSGNodeUpdater::enterGeometryNode(QSGGeometryNode *g) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "enter geometry:" << g; +#endif + + g->m_matrix = m_combined_matrix_stack.isEmpty() ? 0 : m_combined_matrix_stack.last(); + g->m_clip_list = m_current_clip; + g->setInheritedOpacity(m_opacity_stack.last()); +} + +void QSGNodeUpdater::leaveGeometryNode(QSGGeometryNode *g) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "leave geometry" << g; +#else + Q_UNUSED(g) +#endif +} + +void QSGNodeUpdater::enterOpacityNode(QSGOpacityNode *o) +{ + if (o->dirtyFlags() & QSGNode::DirtyOpacity) + ++m_force_update; + + qreal opacity = m_opacity_stack.last() * o->opacity(); + o->setCombinedOpacity(opacity); + m_opacity_stack.add(opacity); + +#ifdef QSG_UPDATER_DEBUG + qDebug() << "enter opacity" << o; +#endif +} + +void QSGNodeUpdater::leaveOpacityNode(QSGOpacityNode *o) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "leave opacity" << o; +#endif + if (o->flags() & QSGNode::DirtyOpacity) + --m_force_update; + + m_opacity_stack.pop_back(); +} + +void QSGNodeUpdater::visitChildren(QSGNode *n) +{ + for (QSGNode *c = n->firstChild(); c; c = c->nextSibling()) + visitNode(c); +} + +void QSGNodeUpdater::visitNode(QSGNode *n) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "enter:" << n; +#endif + + if (!n->dirtyFlags() && !m_force_update) + return; + if (n->isSubtreeBlocked()) + return; + + bool forceUpdate = n->dirtyFlags() & (QSGNode::DirtyNodeAdded | QSGNode::DirtyForceUpdate); + if (forceUpdate) + ++m_force_update; + + switch (n->type()) { + case QSGNode::TransformNodeType: { + QSGTransformNode *t = static_cast<QSGTransformNode *>(n); + enterTransformNode(t); + visitChildren(t); + leaveTransformNode(t); + break; } + case QSGNode::GeometryNodeType: { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(n); + enterGeometryNode(g); + visitChildren(g); + leaveGeometryNode(g); + break; } + case QSGNode::ClipNodeType: { + QSGClipNode *c = static_cast<QSGClipNode *>(n); + enterClipNode(c); + visitChildren(c); + leaveClipNode(c); + break; } + case QSGNode::OpacityNodeType: { + QSGOpacityNode *o = static_cast<QSGOpacityNode *>(n); + enterOpacityNode(o); + visitChildren(o); + leaveOpacityNode(o); + break; } + default: + visitChildren(n); + break; + } + + if (forceUpdate) + --m_force_update; + + n->clearDirty(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h new file mode 100644 index 0000000000..446bdefdc5 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NODEUPDATER_P_H +#define NODEUPDATER_P_H + +#include "qsgnode.h" +#include <QtGui/private/qdatabuffer_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGNodeUpdater +{ +public: + QSGNodeUpdater(); + + virtual void updateStates(QSGNode *n); + virtual bool isNodeBlocked(QSGNode *n, QSGNode *root) const; + + void setToplevelOpacity(qreal alpha) { m_opacity_stack.last() = alpha; } + qreal toplevelOpacity() const { return m_opacity_stack.last(); } + +protected: + virtual void enterTransformNode(QSGTransformNode *); + virtual void leaveTransformNode(QSGTransformNode *); + void enterClipNode(QSGClipNode *c); + void leaveClipNode(QSGClipNode *c); + void enterOpacityNode(QSGOpacityNode *o); + void leaveOpacityNode(QSGOpacityNode *o); + void enterGeometryNode(QSGGeometryNode *); + void leaveGeometryNode(QSGGeometryNode *); + + void visitNode(QSGNode *n); + void visitChildren(QSGNode *n); + + + QDataBuffer<const QMatrix4x4 *> m_combined_matrix_stack; + QDataBuffer<qreal> m_opacity_stack; + const QSGClipNode *m_current_clip; + + int m_force_update; + + qreal m_toplevel_alpha; +}; + +QT_END_NAMESPACE + +#endif // NODEUPDATER_P_H diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp new file mode 100644 index 0000000000..b22631afae --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -0,0 +1,743 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgrenderer_p.h" +#include "qsgnode.h" +#include "qsgmaterial.h" +#include "qsgnodeupdater_p.h" +#include "qsggeometry_p.h" + +#include <private/qsgadaptationlayer_p.h> + +#include <QOpenGLShaderProgram> +#include <qopenglframebufferobject.h> +#include <QtGui/qguiapplication.h> + +#include <qdatetime.h> + +QT_BEGIN_NAMESPACE + +//#define RENDERER_DEBUG +//#define QT_GL_NO_SCISSOR_TEST + + + +#define QSG_RENDERER_TIMING +#ifdef QSG_RENDERER_TIMING +static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty(); +static QTime frameTimer; +static int preprocessTime; +static int updatePassTime; +#endif + +void QSGBindable::clear(QSGRenderer::ClearMode mode) const +{ + GLuint bits = 0; + if (mode & QSGRenderer::ClearColorBuffer) bits |= GL_COLOR_BUFFER_BIT; + if (mode & QSGRenderer::ClearDepthBuffer) bits |= GL_DEPTH_BUFFER_BIT; + if (mode & QSGRenderer::ClearStencilBuffer) bits |= GL_STENCIL_BUFFER_BIT; + glClear(bits); +} + +// Reactivate the color buffer after switching to the stencil. +void QSGBindable::reactivate() const +{ + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + +QSGBindableFbo::QSGBindableFbo(QOpenGLFramebufferObject *fbo) : m_fbo(fbo) +{ +} + + +void QSGBindableFbo::bind() const +{ + m_fbo->bind(); +} + +/*! + \class QSGRenderer + \brief The renderer class is the abstract baseclass use for rendering the + QML scene graph. + + The renderer is not tied to any particular surface. It expects a context to + be current and will render into that surface according to how the device rect, + viewport rect and projection transformation are set up. + + Rendering is a sequence of steps initiated by calling renderScene(). This will + effectively draw the scene graph starting at the root node. The QSGNode::preprocess() + function will be called for all the nodes in the graph, followed by an update + pass which updates all matrices, opacity, clip states and similar in the graph. + Because the update pass is called after preprocess, it is safe to modify the graph + during preprocess. To run a custom update pass over the graph, install a custom + QSGNodeUpdater using setNodeUpdater(). Once all the graphs dirty states are updated, + the virtual render() function is called. + + The render() function is implemented by QSGRenderer subclasses to render the graph + in the most optimal way for a given hardware. + + The renderer can make use of stencil, depth and color buffers in addition to the + scissor rect. + + \internal + */ + + +QSGRenderer::QSGRenderer(QSGContext *context) + : QObject() + , m_clear_color(Qt::transparent) + , m_clear_mode(ClearColorBuffer | ClearDepthBuffer) + , m_current_opacity(1) + , m_context(context) + , m_root_node(0) + , m_node_updater(0) + , m_bindable(0) + , m_changed_emitted(false) + , m_mirrored(false) + , m_is_rendering(false) + , m_vertex_buffer_bound(false) + , m_index_buffer_bound(false) +{ + initializeGLFunctions(); +} + + +QSGRenderer::~QSGRenderer() +{ + setRootNode(0); + delete m_node_updater; +} + +/*! + Returns the scene graph context for this renderer. + + \internal + */ + +QSGContext *QSGRenderer::context() +{ + return m_context; +} + + + + +/*! + Returns the node updater that this renderer uses to update states in the + scene graph. + + If no updater is specified a default one is constructed. + */ + +QSGNodeUpdater *QSGRenderer::nodeUpdater() const +{ + if (!m_node_updater) + const_cast<QSGRenderer *>(this)->m_node_updater = new QSGNodeUpdater(); + return m_node_updater; +} + + +/*! + Sets the node updater that this renderer uses to update states in the + scene graph. + + This will delete and override any existing node updater + */ +void QSGRenderer::setNodeUpdater(QSGNodeUpdater *updater) +{ + if (m_node_updater) + delete m_node_updater; + m_node_updater = updater; +} + + +void QSGRenderer::setRootNode(QSGRootNode *node) +{ + if (m_root_node == node) + return; + if (m_root_node) { + m_root_node->m_renderers.removeOne(this); + nodeChanged(m_root_node, QSGNode::DirtyNodeRemoved); + } + m_root_node = node; + if (m_root_node) { + Q_ASSERT(!m_root_node->m_renderers.contains(this)); + m_root_node->m_renderers << this; + nodeChanged(m_root_node, QSGNode::DirtyNodeAdded); + } +} + + +void QSGRenderer::renderScene() +{ + class B : public QSGBindable + { + public: + void bind() const { QOpenGLFramebufferObject::bindDefault(); } + } b; + renderScene(b); +} + +void QSGRenderer::renderScene(const QSGBindable &bindable) +{ + if (!m_root_node) + return; + + m_is_rendering = true; + + +#ifdef QSG_RENDERER_TIMING + if (qsg_render_timing) + frameTimer.start(); + int bindTime; + int renderTime; +#endif + + m_bindable = &bindable; + preprocess(); + + bindable.bind(); +#ifdef QSG_RENDERER_TIMING + if (qsg_render_timing) + bindTime = frameTimer.elapsed(); +#endif + +#ifndef QT_NO_DEBUG + // Sanity check that attribute registers are disabled + { + GLint count; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &count); + GLint enabled; + for (int i=0; i<count; ++i) { + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled); + if (enabled) { + qWarning("QSGRenderer: attribute %d is enabled, this can lead to memory corruption and crashes.", i); + } + } + } +#endif + + render(); +#ifdef QSG_RENDERER_TIMING + if (qsg_render_timing) + renderTime = frameTimer.elapsed(); +#endif + + glDisable(GL_SCISSOR_TEST); + m_is_rendering = false; + m_changed_emitted = false; + m_bindable = 0; + + if (m_vertex_buffer_bound) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + m_vertex_buffer_bound = false; + } + + if (m_index_buffer_bound) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + m_index_buffer_bound = false; + } + +#ifdef QSG_RENDERER_TIMING + if (qsg_render_timing) { + printf(" - Breakdown of frametime: preprocess=%d, updates=%d, binding=%d, render=%d, total=%d\n", + preprocessTime, + updatePassTime - preprocessTime, + bindTime - updatePassTime, + renderTime - bindTime, + renderTime); + } +#endif +} + +void QSGRenderer::setProjectionMatrixToDeviceRect() +{ + setProjectionMatrixToRect(m_device_rect); +} + +void QSGRenderer::setProjectionMatrixToRect(const QRectF &rect) +{ + QMatrix4x4 matrix; + matrix.ortho(rect.x(), + rect.x() + rect.width(), + rect.y() + rect.height(), + rect.y(), + qreal(0.01), + -1); + setProjectionMatrix(matrix); +} + +void QSGRenderer::setProjectionMatrix(const QMatrix4x4 &matrix) +{ + m_projection_matrix = matrix; + // Mirrored relative to the usual Qt coordinate system with origin in the top left corner. + m_mirrored = matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0; +} + +void QSGRenderer::setClearColor(const QColor &color) +{ + m_clear_color = color; +} + +/*! + Updates internal data structures and emits the sceneGraphChanged() signal. + + If \a flags contains the QSGNode::DirtyNodeRemoved flag, the node might be + in the process of being destroyed. It is then not safe to downcast the node + pointer. +*/ + +void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags) +{ + Q_UNUSED(node); + Q_UNUSED(flags); + + if (flags & QSGNode::DirtyNodeAdded) + addNodesToPreprocess(node); + if (flags & QSGNode::DirtyNodeRemoved) + removeNodesToPreprocess(node); + + if (!m_changed_emitted && !m_is_rendering) { + // Premature overoptimization to avoid excessive signal emissions + m_changed_emitted = true; + emit sceneGraphChanged(); + } +} + +void QSGRenderer::materialChanged(QSGGeometryNode *, QSGMaterial *, QSGMaterial *) +{ +} + +void QSGRenderer::preprocess() +{ + Q_ASSERT(m_root_node); + + // We need to take a copy here, in case any of the preprocess calls deletes a node that + // is in the preprocess list and thus, changes the m_nodes_to_preprocess behind our backs + // For the default case, when this does not happen, the cost is neglishible. + QSet<QSGNode *> items = m_nodes_to_preprocess; + + for (QSet<QSGNode *>::const_iterator it = items.constBegin(); + it != items.constEnd(); ++it) { + QSGNode *n = *it; + if (!nodeUpdater()->isNodeBlocked(n, m_root_node)) { + n->preprocess(); + } + } + +#ifdef QSG_RENDERER_TIMING + if (qsg_render_timing) + preprocessTime = frameTimer.elapsed(); +#endif + + nodeUpdater()->setToplevelOpacity(context()->renderAlpha()); + nodeUpdater()->updateStates(m_root_node); + +#ifdef QSG_RENDERER_TIMING + if (qsg_render_timing) + updatePassTime = frameTimer.elapsed(); +#endif + +} + +void QSGRenderer::addNodesToPreprocess(QSGNode *node) +{ + for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) + addNodesToPreprocess(c); + if (node->flags() & QSGNode::UsePreprocess) + m_nodes_to_preprocess.insert(node); +} + +void QSGRenderer::removeNodesToPreprocess(QSGNode *node) +{ + for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) + removeNodesToPreprocess(c); + if (node->flags() & QSGNode::UsePreprocess) + m_nodes_to_preprocess.remove(node); +} + + +/*! + Convenience function to set up the stencil buffer for clipping based on \a clip. + + If the clip is a pixel aligned rectangle, this function will use glScissor instead + of stencil. + */ + +QSGRenderer::ClipType QSGRenderer::updateStencilClip(const QSGClipNode *clip) +{ + if (!clip) { + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); + return NoClip; + } + + bool stencilEnabled = false; + bool scissorEnabled = false; + + glDisable(GL_SCISSOR_TEST); + + int clipDepth = 0; + QRect clipRect; + while (clip) { + QMatrix4x4 m = m_current_projection_matrix; + if (clip->matrix()) + m *= *clip->matrix(); + + // TODO: Check for multisampling and pixel grid alignment. + bool isRectangleWithNoPerspective = clip->isRectangular() + && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1)); + bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0)); + bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1)); + + if (isRectangleWithNoPerspective && (noRotate || isRotate90)) { + QRectF bbox = clip->clipRect(); + qreal invW = 1 / m(3, 3); + qreal fx1, fy1, fx2, fy2; + if (noRotate) { + fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW; + fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW; + fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW; + fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW; + } else { + Q_ASSERT(isRotate90); + fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW; + fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW; + fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW; + fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW; + } + + if (fx1 > fx2) + qSwap(fx1, fx2); + if (fy1 > fy2) + qSwap(fy1, fy2); + + GLint ix1 = qRound((fx1 + 1) * m_device_rect.width() * qreal(0.5)); + GLint iy1 = qRound((fy1 + 1) * m_device_rect.height() * qreal(0.5)); + GLint ix2 = qRound((fx2 + 1) * m_device_rect.width() * qreal(0.5)); + GLint iy2 = qRound((fy2 + 1) * m_device_rect.height() * qreal(0.5)); + + if (!scissorEnabled) { + clipRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1); + glEnable(GL_SCISSOR_TEST); + scissorEnabled = true; + } else { + clipRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1); + } + + glScissor(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); + } else { + if (!stencilEnabled) { + if (!m_clip_program.isLinked()) { + m_clip_program.addShaderFromSourceCode(QOpenGLShader::Vertex, + "attribute highp vec4 vCoord; \n" + "uniform highp mat4 matrix; \n" + "void main() { \n" + " gl_Position = matrix * vCoord; \n" + "}"); + m_clip_program.addShaderFromSourceCode(QOpenGLShader::Fragment, + "void main() { \n" + " gl_FragColor = vec4(0.81, 0.83, 0.12, 1.0); \n" // Trolltech green ftw! + "}"); + m_clip_program.bindAttributeLocation("vCoord", 0); + m_clip_program.link(); + m_clip_matrix_id = m_clip_program.uniformLocation("matrix"); + } + + glStencilMask(0xff); // write mask + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + glEnable(GL_STENCIL_TEST); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glDepthMask(GL_FALSE); + + m_clip_program.bind(); + m_clip_program.enableAttributeArray(0); + + stencilEnabled = true; + } + + glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // stencil fail, z fail, z pass + + const QSGGeometry *g = clip->geometry(); + Q_ASSERT(g->attributeCount() > 0); + const QSGGeometry::Attribute *a = g->attributes(); + glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, g->sizeOfVertex(), g->vertexData()); + + m_clip_program.setUniformValue(m_clip_matrix_id, m); + if (g->indexCount()) { + glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); + } else { + glDrawArrays(g->drawingMode(), 0, g->vertexCount()); + } + + ++clipDepth; + } + + clip = clip->clipList(); + } + + if (stencilEnabled) { + m_clip_program.disableAttributeArray(0); + glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // stencil fail, z fail, z pass + glStencilMask(0); // write mask + bindable()->reactivate(); + } else { + glDisable(GL_STENCIL_TEST); + } + + if (!scissorEnabled) + glDisable(GL_SCISSOR_TEST); + + return stencilEnabled ? StencilClip : ScissorClip; +} + + + +static inline int size_of_type(GLenum type) +{ + static int sizes[] = { + sizeof(char), + sizeof(unsigned char), + sizeof(short), + sizeof(unsigned short), + sizeof(int), + sizeof(unsigned int), + sizeof(float), + 2, + 3, + 4, + sizeof(double) + }; + Q_ASSERT(type >= GL_BYTE && type <= 0x140A); // the value of GL_DOUBLE + return sizes[type - GL_BYTE]; +} + + +class QSGRendererVBOGeometryData : public QSGGeometryData +{ +public: + QSGRendererVBOGeometryData() + : vertexBuffer(0) + , indexBuffer(0) + { + } + + ~QSGRendererVBOGeometryData() + { + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) + return; + QOpenGLFunctions *func = ctx->functions(); + if (vertexBuffer) + func->glDeleteBuffers(1, &vertexBuffer); + if (indexBuffer) + func->glDeleteBuffers(1, &indexBuffer); + } + + GLuint vertexBuffer; + GLuint indexBuffer; + + static QSGRendererVBOGeometryData *get(const QSGGeometry *g) { + QSGRendererVBOGeometryData *gd = static_cast<QSGRendererVBOGeometryData *>(QSGGeometryData::data(g)); + if (!gd) { + gd = new QSGRendererVBOGeometryData; + QSGGeometryData::install(g, gd); + } + return gd; + } + +}; + +static inline GLenum qt_drawTypeForPattern(QSGGeometry::DataPattern p) +{ + Q_ASSERT(p > 0 && p <= 3); + static GLenum drawTypes[] = { 0, + GL_STREAM_DRAW, + GL_DYNAMIC_DRAW, + GL_STATIC_DRAW + }; + return drawTypes[p]; +} + + +/*! + Issues the GL draw call for the geometry \a g using the material \a shader. + + The function assumes that attributes have been bound and set up prior + to making this call. + + \internal + */ + +void QSGRenderer::draw(const QSGMaterialShader *shader, const QSGGeometry *g) +{ + // ### remove before final release... + static bool use_vbo = !QGuiApplication::arguments().contains(QLatin1String("--no-vbo")); + + const void *vertexData; + int vertexByteSize = g->vertexCount() * g->sizeOfVertex(); + if (use_vbo && g->vertexDataPattern() != QSGGeometry::AlwaysUploadPattern && vertexByteSize > 1024) { + + // The base pointer for a VBO is 0 + vertexData = 0; + + bool updateData = QSGGeometryData::hasDirtyVertexData(g); + QSGRendererVBOGeometryData *gd = QSGRendererVBOGeometryData::get(g); + if (!gd->vertexBuffer) { + glGenBuffers(1, &gd->vertexBuffer); + updateData = true; + } + + glBindBuffer(GL_ARRAY_BUFFER, gd->vertexBuffer); + m_vertex_buffer_bound = true; + + if (updateData) { + glBufferData(GL_ARRAY_BUFFER, vertexByteSize, g->vertexData(), + qt_drawTypeForPattern(g->vertexDataPattern())); + QSGGeometryData::clearDirtyVertexData(g); + } + + } else { + if (m_vertex_buffer_bound) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + m_vertex_buffer_bound = false; + } + vertexData = g->vertexData(); + } + + // Bind the vertices to attributes... + char const *const *attrNames = shader->attributeNames(); + int offset = 0; + for (int j = 0; attrNames[j]; ++j) { + if (!*attrNames[j]) + continue; + Q_ASSERT_X(j < g->attributeCount(), "QSGRenderer::bindGeometry()", "Geometry lacks attribute required by material"); + const QSGGeometry::Attribute &a = g->attributes()[j]; + Q_ASSERT_X(j == a.position, "QSGRenderer::bindGeometry()", "Geometry does not have continuous attribute positions"); + +#if defined(QT_OPENGL_ES_2) + GLboolean normalize = a.type != GL_FLOAT; +#else + GLboolean normalize = a.type != GL_FLOAT && a.type != GL_DOUBLE; +#endif + glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, g->sizeOfVertex(), (char *) vertexData + offset); + offset += a.tupleSize * size_of_type(a.type); + } + + // Set up the indices... + const void *indexData; + if (use_vbo && g->indexDataPattern() != QSGGeometry::AlwaysUploadPattern && g->indexCount() > 512) { + + // Base pointer for a VBO is 0 + indexData = 0; + + bool updateData = QSGGeometryData::hasDirtyIndexData(g); + QSGRendererVBOGeometryData *gd = QSGRendererVBOGeometryData::get(g); + if (!gd->indexBuffer) { + glGenBuffers(1, &gd->indexBuffer); + updateData = true; + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gd->indexBuffer); + m_index_buffer_bound = true; + + if (updateData) { + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + g->indexCount() * g->sizeOfIndex(), + g->indexData(), + qt_drawTypeForPattern(g->indexDataPattern())); + QSGGeometryData::clearDirtyIndexData(g); + } + + } else { + if (m_index_buffer_bound) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + m_index_buffer_bound = false; + } + indexData = g->indexData(); + } + + + // draw the stuff... + if (g->indexCount()) { + glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), indexData); + } else { + glDrawArrays(g->drawingMode(), 0, g->vertexCount()); + } + + // We leave buffers bound for now... They will be reset by bind on next draw() or + // set back to 0 if next draw is not using VBOs + +} + +/*! + \class QSGNodeDumper + \brief The QSGNodeDumper class provides a way of dumping a scene grahp to the console. + + This class is solely for debugging purposes. + + \internal + */ + +void QSGNodeDumper::dump(QSGNode *n) +{ + QSGNodeDumper dump; + dump.visitNode(n); +} + +void QSGNodeDumper::visitNode(QSGNode *n) +{ + qDebug() << QString(m_indent * 2, QLatin1Char(' ')) << n; + QSGNodeVisitor::visitNode(n); +} + +void QSGNodeDumper::visitChildren(QSGNode *n) +{ + ++m_indent; + QSGNodeVisitor::visitChildren(n); + --m_indent; +} + + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h new file mode 100644 index 0000000000..e5667c710a --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RENDERER_H +#define RENDERER_H + +#include <qset.h> +#include <qhash.h> + +#include <qcolor.h> +#include <qopenglfunctions.h> +#include <qopenglshaderprogram.h> + +#include "qsgnode.h" +#include "qsgmaterial.h" +#include <QtQuick/qsgtexture.h> + +#include <QtQuick/private/qsgcontext_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGMaterialShader; +struct QSGMaterialType; +class QOpenGLFramebufferObject; +class TextureReference; +class QSGBindable; +class QSGNodeUpdater; + +class Q_QUICK_EXPORT QSGRenderer : public QObject, public QOpenGLFunctions +{ + Q_OBJECT +public: + enum ClipType + { + NoClip, + ScissorClip, + StencilClip + }; + + enum ClearModeBit + { + ClearColorBuffer = 0x0001, + ClearDepthBuffer = 0x0002, + ClearStencilBuffer = 0x0004 + }; + Q_DECLARE_FLAGS(ClearMode, ClearModeBit) + + QSGRenderer(QSGContext *context); + virtual ~QSGRenderer(); + + void setRootNode(QSGRootNode *node); + QSGRootNode *rootNode() const { return m_root_node; } + + void setDeviceRect(const QRect &rect) { m_device_rect = rect; } + inline void setDeviceRect(const QSize &size) { setDeviceRect(QRect(QPoint(), size)); } + QRect deviceRect() const { return m_device_rect; } + + void setViewportRect(const QRect &rect) { m_viewport_rect = rect; } + inline void setViewportRect(const QSize &size) { setViewportRect(QRect(QPoint(), size)); } + QRect viewportRect() const { return m_viewport_rect; } + + // Accessed by QSGMaterialShader::RenderState. + QMatrix4x4 currentProjectionMatrix() const { return m_current_projection_matrix; } + QMatrix4x4 currentModelViewMatrix() const { return m_current_model_view_matrix; } + QMatrix4x4 currentCombinedMatrix() const { return m_current_projection_matrix * m_current_model_view_matrix; } + qreal currentOpacity() const { return m_current_opacity; } + + void setProjectionMatrixToDeviceRect(); + void setProjectionMatrixToRect(const QRectF &rect); + void setProjectionMatrix(const QMatrix4x4 &matrix); + QMatrix4x4 projectionMatrix() const { return m_projection_matrix; } + bool isMirrored() const { return m_mirrored; } + + void setClearColor(const QColor &color); + QColor clearColor() const { return m_clear_color; } + + QOpenGLContext *glContext() const { Q_ASSERT(m_context); return m_context->glContext(); } + + QSGContext *context(); + + void renderScene(); + void renderScene(const QSGBindable &bindable); + virtual void nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags); + virtual void materialChanged(QSGGeometryNode *node, QSGMaterial *from, QSGMaterial *to); + + QSGNodeUpdater *nodeUpdater() const; + void setNodeUpdater(QSGNodeUpdater *updater); + + inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const; + + void setClearMode(ClearMode mode) { m_clear_mode = mode; } + ClearMode clearMode() const { return m_clear_mode; } + +signals: + void sceneGraphChanged(); // Add, remove, ChangeFlags changes... + +protected: + void draw(const QSGMaterialShader *material, const QSGGeometry *g); + + virtual void render() = 0; + QSGRenderer::ClipType updateStencilClip(const QSGClipNode *clip); + + const QSGBindable *bindable() const { return m_bindable; } + + virtual void preprocess(); + + void addNodesToPreprocess(QSGNode *node); + void removeNodesToPreprocess(QSGNode *node); + + + QColor m_clear_color; + ClearMode m_clear_mode; + QMatrix4x4 m_current_projection_matrix; + QMatrix4x4 m_current_model_view_matrix; + qreal m_current_opacity; + + QSGContext *m_context; + +private: + QSGRootNode *m_root_node; + QSGNodeUpdater *m_node_updater; + + QRect m_device_rect; + QRect m_viewport_rect; + + QSet<QSGNode *> m_nodes_to_preprocess; + + QMatrix4x4 m_projection_matrix; + QOpenGLShaderProgram m_clip_program; + int m_clip_matrix_id; + + const QSGBindable *m_bindable; + + uint m_changed_emitted : 1; + uint m_mirrored : 1; + uint m_is_rendering : 1; + + uint m_vertex_buffer_bound : 1; + uint m_index_buffer_bound : 1; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderer::ClearMode) + +class Q_QUICK_EXPORT QSGBindable +{ +public: + virtual ~QSGBindable() { } + virtual void bind() const = 0; + virtual void clear(QSGRenderer::ClearMode mode) const; + virtual void reactivate() const; +}; + +class QSGBindableFbo : public QSGBindable +{ +public: + QSGBindableFbo(QOpenGLFramebufferObject *fbo); + virtual void bind() const; +private: + QOpenGLFramebufferObject *m_fbo; +}; + + + +QSGMaterialShader::RenderState QSGRenderer::state(QSGMaterialShader::RenderState::DirtyStates dirty) const +{ + QSGMaterialShader::RenderState s; + s.m_dirty = dirty; + s.m_data = this; + return s; +} + + +class Q_QUICK_EXPORT QSGNodeDumper : public QSGNodeVisitor { + +public: + static void dump(QSGNode *n); + + QSGNodeDumper() : m_indent(0) {} + void visitNode(QSGNode *n); + void visitChildren(QSGNode *n); + +private: + int m_indent; +}; + + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // RENDERER_H diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp new file mode 100644 index 0000000000..972bff80e0 --- /dev/null +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -0,0 +1,315 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgadaptationlayer_p.h" + +#include <qmath.h> +#include <QtQuick/private/qsgdistancefieldutil_p.h> +#include <QtQuick/private/qsgdistancefieldglyphnode_p.h> +#include <private/qrawfont_p.h> +#include <QtGui/qguiapplication.h> +#include <qdir.h> + +QT_BEGIN_NAMESPACE + + +QHash<QString, QOpenGLMultiGroupSharedResource> QSGDistanceFieldGlyphCache::m_caches_data; + +QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font) + : ctx(c) + , m_manager(man) +{ + Q_ASSERT(font.isValid()); + m_font = font; + + m_cacheData = cacheData(); + + QRawFontPrivate *fontD = QRawFontPrivate::get(m_font); + m_glyphCount = fontD->fontEngine->glyphCount(); + + m_cacheData->doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT; + + m_referenceFont = m_font; + m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(m_cacheData->doubleGlyphResolution)); + Q_ASSERT(m_referenceFont.isValid()); +} + +QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache() +{ +} + +QSGDistanceFieldGlyphCache::GlyphCacheData *QSGDistanceFieldGlyphCache::cacheData() +{ + QString key = QString::fromLatin1("%1_%2_%3_%4") + .arg(m_font.familyName()) + .arg(m_font.styleName()) + .arg(m_font.weight()) + .arg(m_font.style()); + return m_caches_data[key].value<QSGDistanceFieldGlyphCache::GlyphCacheData>(ctx); +} + +qreal QSGDistanceFieldGlyphCache::fontScale() const +{ + return qreal(m_font.pixelSize()) / QT_DISTANCEFIELD_BASEFONTSIZE(m_cacheData->doubleGlyphResolution); +} + +int QSGDistanceFieldGlyphCache::distanceFieldRadius() const +{ + return QT_DISTANCEFIELD_DEFAULT_RADIUS / QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution); +} + +QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph) +{ + QHash<glyph_t, Metrics>::iterator metric = m_metrics.find(glyph); + if (metric == m_metrics.end()) { + QPainterPath path = m_font.pathForGlyph(glyph); + QRectF br = path.boundingRect(); + + Metrics m; + m.width = br.width(); + m.height = br.height(); + m.baselineX = br.x(); + m.baselineY = -br.y(); + + metric = m_metrics.insert(glyph, m); + } + + return metric.value(); +} + +QSGDistanceFieldGlyphCache::TexCoord QSGDistanceFieldGlyphCache::glyphTexCoord(glyph_t glyph) const +{ + return m_cacheData->texCoords.value(glyph); +} + +static QSGDistanceFieldGlyphCache::Texture g_emptyTexture; + +const QSGDistanceFieldGlyphCache::Texture *QSGDistanceFieldGlyphCache::glyphTexture(glyph_t glyph) const +{ + QHash<glyph_t, Texture*>::const_iterator it = m_cacheData->glyphTextures.find(glyph); + if (it == m_cacheData->glyphTextures.constEnd()) + return &g_emptyTexture; + return it.value(); +} + +void QSGDistanceFieldGlyphCache::populate(const QVector<glyph_t> &glyphs) +{ + QSet<glyph_t> newGlyphs; + int count = glyphs.count(); + for (int i = 0; i < count; ++i) { + glyph_t glyphIndex = glyphs.at(i); + if ((int) glyphIndex >= glyphCount()) { + qWarning("Warning: distance-field glyph is not available with index %d", glyphIndex); + continue; + } + + if (m_cacheData->texCoords.contains(glyphIndex) || newGlyphs.contains(glyphIndex)) + continue; + + QPainterPath path = m_referenceFont.pathForGlyph(glyphIndex); + m_cacheData->glyphPaths.insert(glyphIndex, path); + if (path.isEmpty()) { + TexCoord c; + c.width = 0; + c.height = 0; + m_cacheData->texCoords.insert(glyphIndex, c); + continue; + } + + newGlyphs.insert(glyphIndex); + } + + if (newGlyphs.isEmpty()) + return; + + QVector<glyph_t> glyphsVec; + QSet<glyph_t>::const_iterator it = newGlyphs.constBegin(); + while (it != newGlyphs.constEnd()) { + glyphsVec.append(*it); + ++it; + } + requestGlyphs(glyphsVec); +} + +void QSGDistanceFieldGlyphCache::release(const QVector<glyph_t> &glyphs) +{ + releaseGlyphs(glyphs); +} + +void QSGDistanceFieldGlyphCache::update() +{ + if (m_cacheData->pendingGlyphs.isEmpty()) + return; + + QHash<glyph_t, QImage> distanceFields; + + // ### Remove before final release + static bool cacheDistanceFields = QGuiApplication::arguments().contains(QLatin1String("--cache-distance-fields")); + + QString tmpPath = QString::fromLatin1("%1/.qt/").arg(QDir::tempPath()); + QString keyBase = QString::fromLatin1("%1%2%3_%4_%5_%6.fontblob") + .arg(tmpPath) + .arg(m_font.familyName()) + .arg(m_font.styleName()) + .arg(m_font.weight()) + .arg(m_font.style()); + + if (cacheDistanceFields && !QFile::exists(tmpPath)) + QDir(tmpPath).mkpath(tmpPath); + + for (int i = 0; i < m_cacheData->pendingGlyphs.size(); ++i) { + glyph_t glyphIndex = m_cacheData->pendingGlyphs.at(i); + + if (cacheDistanceFields) { + QString key = keyBase.arg(glyphIndex); + QFile file(key); + if (file.open(QFile::ReadOnly)) { + int fileSize = file.size(); + int dim = sqrt(float(fileSize)); + QByteArray blob = file.readAll(); + QImage df(dim, dim, QImage::Format_Indexed8); + memcpy(df.bits(), blob.constData(), fileSize); + distanceFields.insert(glyphIndex, df); + continue; + } + } + + QImage distanceField = qt_renderDistanceFieldGlyph(m_font, glyphIndex, m_cacheData->doubleGlyphResolution); + distanceFields.insert(glyphIndex, distanceField); + + if (cacheDistanceFields) { + QString key = keyBase.arg(glyphIndex); + QFile file(key); + file.open(QFile::WriteOnly); + file.write((const char *) distanceField.constBits(), distanceField.width() * distanceField.height()); + } + } + + m_cacheData->pendingGlyphs.reset(); + + storeGlyphs(distanceFields); +} + +void QSGDistanceFieldGlyphCache::addGlyphPositions(const QList<GlyphPosition> &glyphs) +{ + int count = glyphs.count(); + for (int i = 0; i < count; ++i) { + GlyphPosition glyph = glyphs.at(i); + + QPainterPath path = m_cacheData->glyphPaths.value(glyph.glyph); + QRectF br = path.boundingRect(); + TexCoord c; + c.xMargin = QT_DISTANCEFIELD_RADIUS(m_cacheData->doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution)); + c.yMargin = QT_DISTANCEFIELD_RADIUS(m_cacheData->doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution)); + c.x = glyph.position.x(); + c.y = glyph.position.y(); + c.width = br.width(); + c.height = br.height(); + + m_cacheData->texCoords.insert(glyph.glyph, c); + } +} + +void QSGDistanceFieldGlyphCache::addGlyphTextures(const QVector<glyph_t> &glyphs, const Texture &tex) +{ + int i = m_cacheData->textures.indexOf(tex); + if (i == -1) { + m_cacheData->textures.append(tex); + i = m_cacheData->textures.size() - 1; + } else { + m_cacheData->textures[i].size = tex.size; + } + Texture *texture = &(m_cacheData->textures[i]); + + int count = glyphs.count(); + for (int j = 0; j < count; ++j) + m_cacheData->glyphTextures.insert(glyphs.at(j), texture); + + QLinkedList<QSGDistanceFieldGlyphNode *>::iterator it = m_cacheData->m_registeredNodes.begin(); + while (it != m_cacheData->m_registeredNodes.end()) { + (*it)->updateGeometry(); + ++it; + } +} + +void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QVector<glyph_t> &glyphs) +{ + int count = glyphs.count(); + for (int i = 0; i < count; ++i) + m_cacheData->pendingGlyphs.add(glyphs.at(i)); +} + +void QSGDistanceFieldGlyphCache::removeGlyph(glyph_t glyph) +{ + m_cacheData->texCoords.remove(glyph); + m_cacheData->glyphTextures.remove(glyph); +} + +void QSGDistanceFieldGlyphCache::updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize) +{ + int count = m_cacheData->textures.count(); + for (int i = 0; i < count; ++i) { + Texture &tex = m_cacheData->textures[i]; + if (tex.textureId == oldTex) { + tex.textureId = newTex; + tex.size = newTexSize; + return; + } + } +} + +bool QSGDistanceFieldGlyphCache::containsGlyph(glyph_t glyph) const +{ + return m_cacheData->texCoords.contains(glyph); +} + +void QSGDistanceFieldGlyphCache::registerGlyphNode(QSGDistanceFieldGlyphNode *node) +{ + m_cacheData->m_registeredNodes.append(node); +} + +void QSGDistanceFieldGlyphCache::unregisterGlyphNode(QSGDistanceFieldGlyphNode *node) +{ + m_cacheData->m_registeredNodes.removeOne(node); +} + + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h new file mode 100644 index 0000000000..5912802a84 --- /dev/null +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ADAPTATIONINTERFACES_H +#define ADAPTATIONINTERFACES_H + +#include <QtQuick/qsgnode.h> +#include <QtQuick/qsgtexture.h> +#include <QtCore/qobject.h> +#include <QtCore/qrect.h> +#include <QtGui/qbrush.h> +#include <QtGui/qcolor.h> +#include <QtCore/qsharedpointer.h> +#include <QtGui/qglyphrun.h> +#include <QtCore/qurl.h> +#include <private/qfontengine_p.h> +#include <QtGui/private/qdatabuffer_p.h> +#include <private/qopenglcontext_p.h> + +// ### remove +#include <QtQuick/private/qquicktext_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGNode; +class QImage; +class TextureReference; +class QSGDistanceFieldGlyphCacheManager; +class QSGDistanceFieldGlyphNode; + +// TODO: Rename from XInterface to AbstractX. +class Q_QUICK_EXPORT QSGRectangleNode : public QSGGeometryNode +{ +public: + virtual void setRect(const QRectF &rect) = 0; + virtual void setColor(const QColor &color) = 0; + virtual void setPenColor(const QColor &color) = 0; + virtual void setPenWidth(qreal width) = 0; + virtual void setGradientStops(const QGradientStops &stops) = 0; + virtual void setRadius(qreal radius) = 0; + virtual void setAligned(bool aligned) = 0; + + virtual void update() = 0; +}; + + +class Q_QUICK_EXPORT QSGImageNode : public QSGGeometryNode +{ +public: + virtual void setTargetRect(const QRectF &rect) = 0; + virtual void setSourceRect(const QRectF &rect) = 0; + virtual void setTexture(QSGTexture *texture) = 0; + + virtual void setMipmapFiltering(QSGTexture::Filtering filtering) = 0; + virtual void setFiltering(QSGTexture::Filtering filtering) = 0; + virtual void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) = 0; + virtual void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) = 0; + + virtual void update() = 0; +}; + + +class Q_QUICK_EXPORT QSGGlyphNode : public QSGGeometryNode +{ +public: + enum AntialiasingMode + { + GrayAntialiasing, + LowQualitySubPixelAntialiasing, + HighQualitySubPixelAntialiasing + }; + + virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) = 0; + virtual void setColor(const QColor &color) = 0; + virtual void setStyle(QQuickText::TextStyle style) = 0; + virtual void setStyleColor(const QColor &color) = 0; + virtual QPointF baseLine() const = 0; + + virtual QRectF boundingRect() const { return m_bounding_rect; } + virtual void setBoundingRect(const QRectF &bounds) { m_bounding_rect = bounds; } + + virtual void setPreferredAntialiasingMode(AntialiasingMode) = 0; + + virtual void update() = 0; + +protected: + QRectF m_bounding_rect; +}; + +class Q_QUICK_EXPORT QSGDistanceFieldGlyphCache +{ +public: + QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font); + virtual ~QSGDistanceFieldGlyphCache(); + + struct Metrics { + qreal width; + qreal height; + qreal baselineX; + qreal baselineY; + + bool isNull() const { return width == 0 || height == 0; } + }; + + struct TexCoord { + qreal x; + qreal y; + qreal width; + qreal height; + qreal xMargin; + qreal yMargin; + + TexCoord() : x(0), y(0), width(-1), height(-1), xMargin(0), yMargin(0) { } + + bool isNull() const { return width <= 0 || height <= 0; } + bool isValid() const { return width >= 0 && height >= 0; } + }; + + struct Texture { + GLuint textureId; + QSize size; + + Texture() : textureId(0), size(QSize()) { } + bool operator == (const Texture &other) const { return textureId == other.textureId; } + }; + + const QSGDistanceFieldGlyphCacheManager *manager() const { return m_manager; } + + const QRawFont &font() const { return m_font; } + + qreal fontScale() const; + int distanceFieldRadius() const; + int glyphCount() const { return m_glyphCount; } + bool doubleGlyphResolution() const { return m_cacheData->doubleGlyphResolution; } + + Metrics glyphMetrics(glyph_t glyph); + TexCoord glyphTexCoord(glyph_t glyph) const; + const Texture *glyphTexture(glyph_t glyph) const; + + void populate(const QVector<glyph_t> &glyphs); + void release(const QVector<glyph_t> &glyphs); + + void update(); + + void registerGlyphNode(QSGDistanceFieldGlyphNode *node); + void unregisterGlyphNode(QSGDistanceFieldGlyphNode *node); + +protected: + struct GlyphPosition { + glyph_t glyph; + QPointF position; + }; + + virtual void requestGlyphs(const QVector<glyph_t> &glyphs) = 0; + virtual void storeGlyphs(const QHash<glyph_t, QImage> &glyphs) = 0; + virtual void releaseGlyphs(const QVector<glyph_t> &glyphs) = 0; + + void addGlyphPositions(const QList<GlyphPosition> &glyphs); + void addGlyphTextures(const QVector<glyph_t> &glyphs, const Texture &tex); + void markGlyphsToRender(const QVector<glyph_t> &glyphs); + void removeGlyph(glyph_t glyph); + + void updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize); + + bool containsGlyph(glyph_t glyph) const; + + QOpenGLContext *ctx; + +private: + struct GlyphCacheData : public QOpenGLSharedResource { + QList<Texture> textures; + QHash<glyph_t, Texture*> glyphTextures; + QHash<glyph_t, TexCoord> texCoords; + QDataBuffer<glyph_t> pendingGlyphs; + QHash<glyph_t, QPainterPath> glyphPaths; + bool doubleGlyphResolution; + QLinkedList<QSGDistanceFieldGlyphNode*> m_registeredNodes; + + GlyphCacheData(QOpenGLContext *ctx) + : QOpenGLSharedResource(ctx->shareGroup()) + , pendingGlyphs(64) + , doubleGlyphResolution(false) + {} + + void invalidateResource() + { + textures.clear(); + glyphTextures.clear(); + texCoords.clear(); + } + + void freeResource(QOpenGLContext *) + { + } + }; + + QSGDistanceFieldGlyphCacheManager *m_manager; + + QRawFont m_font; + QRawFont m_referenceFont; + + int m_glyphCount; + QHash<glyph_t, Metrics> m_metrics; + + GlyphCacheData *cacheData(); + GlyphCacheData *m_cacheData; + static QHash<QString, QOpenGLMultiGroupSharedResource> m_caches_data; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp new file mode 100644 index 0000000000..93fea15921 --- /dev/null +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -0,0 +1,513 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgcontext_p.h" +#include <QtQuick/private/qsgrenderer_p.h> +#include <QtQuick/qsgnode.h> + +#include <QtQuick/private/qdeclarativepixmapcache_p.h> + +#include <private/qsgdefaultrenderer_p.h> + +#include <QtQuick/private/qsgdistancefieldutil_p.h> +#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h> +#include <private/qsgdefaultrectanglenode_p.h> +#include <private/qsgdefaultimagenode_p.h> +#include <private/qsgdefaultglyphnode_p.h> +#include <private/qsgdistancefieldglyphnode_p.h> + +#include <QtQuick/private/qsgtexture_p.h> +#include <QGuiApplication> +#include <QOpenGLContext> + +#include <QDeclarativeImageProvider> + +#include <private/qobject_p.h> +#include <qmutex.h> +#include <private/qdeclarativeglobal_p.h> + +DEFINE_BOOL_CONFIG_OPTION(qmlFlashMode, QML_FLASH_MODE) +DEFINE_BOOL_CONFIG_OPTION(qmlTranslucentMode, QML_TRANSLUCENT_MODE) +DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) + +/* + Comments about this class from Gunnar: + + The QSGContext class is right now two things.. The first is the + adaptation layer and central storage ground for all the things + in the scene graph, like textures and materials. This part really + belongs inside the scene graph coreapi. + + The other part is the QML adaptation classes, like how to implement + rectangle nodes. This is not part of the scene graph core API, but + more part of the QML adaptation of scene graph. + + If we ever move the scene graph core API into its own thing, this class + needs to be split in two. Right now its one because we're lazy when it comes + to defining plugin interfaces.. +*/ + + +QT_BEGIN_NAMESPACE + +class QSGContextPrivate : public QObjectPrivate +{ +public: + QSGContextPrivate() + : rootNode(0) + , renderer(0) + , gl(0) + , distanceFieldCacheManager(0) + , flashMode(qmlFlashMode()) + , distanceFieldDisabled(qmlDisableDistanceField()) + { + renderAlpha = qmlTranslucentMode() ? 0.5 : 1; + } + + ~QSGContextPrivate() + { + } + + QSGRootNode *rootNode; + QSGRenderer *renderer; + + QOpenGLContext *gl; + + QHash<QSGMaterialType *, QSGMaterialShader *> materials; + QHash<QDeclarativeTextureFactory *, QSGTexture *> textures; + + QSGDistanceFieldGlyphCacheManager *distanceFieldCacheManager; + + bool flashMode; + float renderAlpha; + bool distanceFieldDisabled; +}; + + +/*! + \class QSGContext + + \brief The QSGContext holds the scene graph entry points for one QML engine. + + The context is not ready for use until it has a QOpenGLContext. Once that happens, + the scene graph population can start. + + \internal + */ + +QSGContext::QSGContext(QObject *parent) : + QObject(*(new QSGContextPrivate), parent) +{ +} + + +QSGContext::~QSGContext() +{ + Q_D(QSGContext); + qDeleteAll(d->textures.values()); + d->textures.clear(); + delete d->renderer; + delete d->rootNode; + qDeleteAll(d->materials.values()); + delete d->distanceFieldCacheManager; +} + + +QSGTexture *QSGContext::textureForFactory(QDeclarativeTextureFactory *factory) +{ + Q_D(QSGContext); + if (!factory) + return 0; + + QSGTexture *texture = d->textures.value(factory); + if (!texture) { + if (QDeclarativeDefaultTextureFactory *dtf = qobject_cast<QDeclarativeDefaultTextureFactory *>(factory)) + texture = createTexture(dtf->image()); + else + texture = factory->createTexture(); + d->textures.insert(factory, texture); + connect(factory, SIGNAL(destroyed(QObject *)), this, SLOT(textureFactoryDestroyed(QObject *))); + } + return texture; +} + + +void QSGContext::textureFactoryDestroyed(QObject *o) +{ + Q_D(QSGContext); + QDeclarativeTextureFactory *f = static_cast<QDeclarativeTextureFactory *>(o); + + // This function will only be called on the scene graph thread, so it is + // safe to directly delete the texture here. + delete d->textures.take(f); +} + + + +/*! + Returns the renderer. The renderer instance is created through the adaptation layer. + */ +QSGRenderer *QSGContext::renderer() const +{ + Q_D(const QSGContext); + return d->renderer; +} + + +/*! + Returns the root node. The root node instance is only created once the scene graph + context becomes ready. + */ +QSGRootNode *QSGContext::rootNode() const +{ + Q_D(const QSGContext); + return d->rootNode; +} + + +QOpenGLContext *QSGContext::glContext() const +{ + Q_D(const QSGContext); + return d->gl; +} + +/*! + Initializes the scene graph context with the GL context \a context. This also + emits the ready() signal so that the QML graph can start building scene graph nodes. + */ +void QSGContext::initialize(QOpenGLContext *context) +{ + Q_D(QSGContext); + + Q_ASSERT(!d->gl); + + d->gl = context; + + d->renderer = createRenderer(); + d->renderer->setClearColor(Qt::white); + + d->rootNode = new QSGRootNode(); + d->renderer->setRootNode(d->rootNode); + + emit ready(); +} + + +/*! + Returns if the scene graph context is ready or not, meaning that it has a valid + GL context. + */ +bool QSGContext::isReady() const +{ + Q_D(const QSGContext); + return d->gl; +} + + +void QSGContext::renderNextFrame(QOpenGLFramebufferObject *fbo) +{ + Q_D(QSGContext); + + if (fbo) { + QSGBindableFbo bindable(fbo); + d->renderer->renderScene(bindable); + } else { + d->renderer->renderScene(); + } + +} + +/*! + Factory function for scene graph backends of the Rectangle element. + */ +QSGRectangleNode *QSGContext::createRectangleNode() +{ + return new QSGDefaultRectangleNode(this); +} + +/*! + Factory function for scene graph backends of the Image element. + */ +QSGImageNode *QSGContext::createImageNode() +{ + return new QSGDefaultImageNode; +} + +/*! + Factory function for scene graph backends of the distance-field glyph cache. + */ +QSGDistanceFieldGlyphCache *QSGContext::createDistanceFieldGlyphCache(const QRawFont &font) +{ + Q_D(QSGContext); + return new QSGDefaultDistanceFieldGlyphCache(d->distanceFieldCacheManager, glContext(), font); +} + +/*! + Factory function for scene graph backends of the Text elements; + */ +QSGGlyphNode *QSGContext::createGlyphNode() +{ + Q_D(QSGContext); + + // ### Do something with these before final release... + static bool doSubpixel = qApp->arguments().contains(QLatin1String("--text-subpixel-antialiasing")); + static bool doLowQualSubpixel = qApp->arguments().contains(QLatin1String("--text-subpixel-antialiasing-lowq")); + static bool doGray = qApp->arguments().contains(QLatin1String("--text-gray-antialiasing")); + + if (d->distanceFieldDisabled) { + return new QSGDefaultGlyphNode; + } else { + if (!d->distanceFieldCacheManager) { + d->distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager(this); + if (doSubpixel) + d->distanceFieldCacheManager->setDefaultAntialiasingMode(QSGGlyphNode::HighQualitySubPixelAntialiasing); + else if (doLowQualSubpixel) + d->distanceFieldCacheManager->setDefaultAntialiasingMode(QSGGlyphNode::LowQualitySubPixelAntialiasing); + else if (doGray) + d->distanceFieldCacheManager->setDefaultAntialiasingMode(QSGGlyphNode::GrayAntialiasing); + } + + QSGGlyphNode *node = new QSGDistanceFieldGlyphNode(d->distanceFieldCacheManager); + return node; + } +} + +/*! + Factory function for the scene graph renderers. + + The renderers are used for the toplevel renderer and once for every + QQuickShaderEffectSource used in the QML scene. + */ +QSGRenderer *QSGContext::createRenderer() +{ + // ### Do something with this before release... + static bool doFrontToBack = qApp->arguments().contains(QLatin1String("--opaque-front-to-back")); + QSGDefaultRenderer *renderer = new QSGDefaultRenderer(this); + if (doFrontToBack) { + printf("QSGContext: Sorting opaque nodes front to back...\n"); + renderer->setSortFrontToBackEnabled(true); + } + return renderer; +} + + + +/*! + Return true if the image provider supports direct decoding of images, + straight into textures without going through a QImage first. + + If the implementation returns true from this function, the decodeImageToTexture() function + will be called to read data from a QIODevice, rather than QML decoding + the image using QImageReader and passing the result to setImage(). + + \warning This function will be called from outside the GUI and rendering threads + and must not make use of OpenGL. + */ + +bool QSGContext::canDecodeImageToTexture() const +{ + return true; +} + + + +/*! + Decode the data in \a dev directly to a texture provider of \a requestSize size. + The size of the decoded data should be written to \a impsize. + + If the implementation fails to decode the image data, it should return 0. The + image data will then be decoded normally. + + \warning This function will be called from outside the GUI and renderer threads + and must not make use of GL calls. + */ + +QSGTexture *QSGContext::decodeImageToTexture(QIODevice *dev, + QSize *size, + const QSize &requestSize) +{ + Q_UNUSED(dev); + Q_UNUSED(size); + Q_UNUSED(requestSize); + return 0; +} + + + +QSurfaceFormat QSGContext::defaultSurfaceFormat() const +{ + QSurfaceFormat format; + format.setDepthBufferSize(24); + format.setStencilBufferSize(8); + format.setSamples(16); + return format; +} + + +/*! + Factory function for texture objects. + + If \a image is a valid image, the QSGTexture::setImage function + will be called with \a image as argument. + */ + +QSGTexture *QSGContext::createTexture(const QImage &image) const +{ + QSGPlainTexture *t = new QSGPlainTexture(); + if (!image.isNull()) + t->setImage(image); + return t; +} + + + +/*! + Returns the minimum supported framebuffer object size. + */ + +QSize QSGContext::minimumFBOSize() const +{ +#ifdef Q_OS_MAC + return QSize(33, 33); +#else + return QSize(1, 1); +#endif +} + + + +/*! + Returns a material shader for the given material. + */ + +QSGMaterialShader *QSGContext::prepareMaterial(QSGMaterial *material) +{ + Q_D(QSGContext); + QSGMaterialType *type = material->type(); + QSGMaterialShader *shader = d->materials.value(type); + if (shader) + return shader; + + shader = material->createShader(); + shader->compile(); + shader->initialize(); + d->materials[type] = shader; + + return shader; +} + + + +/*! + Sets whether the scene graph should render with flashing update rectangles or not + */ + +void QSGContext::setFlashModeEnabled(bool enabled) +{ + d_func()->flashMode = enabled; +} + + +/*! + Returns true if the scene graph should be rendered with flashing update rectangles + */ +bool QSGContext::isFlashModeEnabled() const +{ + return d_func()->flashMode; +} + + +/*! + Sets the toplevel opacity for rendering. This value will be multiplied into all + drawing calls where possible. + + The default value is 1. Any other value will cause artifacts and is primarily + useful for debugging. + */ +void QSGContext::setRenderAlpha(qreal renderAlpha) +{ + d_func()->renderAlpha = renderAlpha; +} + + +/*! + Returns the toplevel opacity used for rendering. + + The default value is 1. + + \sa setRenderAlpha() + */ +qreal QSGContext::renderAlpha() const +{ + return d_func()->renderAlpha; +} + + +/*! + Sets whether or not the scene graph should use the distance field technique to render text + */ +void QSGContext::setDistanceFieldEnabled(bool enabled) +{ + d_func()->distanceFieldDisabled = !enabled; +} + + +/*! + Returns true if the scene graph uses the distance field technique to render text + */ +bool QSGContext::isDistanceFieldEnabled() const +{ + return !d_func()->distanceFieldDisabled; +} + + + +/*! + Creates a new animation driver. + */ + +QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent) +{ + return new QAnimationDriver(parent); +} + + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h new file mode 100644 index 0000000000..ded9d2727a --- /dev/null +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGCONTEXT_H +#define QSGCONTEXT_H + +#include <QtCore/QObject> +#include <QtCore/qabstractanimation.h> + +#include <QtGui/QImage> +#include <QtGui/QSurfaceFormat> + +#include <private/qrawfont_p.h> + +#include <QtQuick/qsgnode.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGContextPrivate; +class QSGRectangleNode; +class QSGImageNode; +class QSGGlyphNode; +class QSGRenderer; +class QSGDistanceFieldGlyphCache; + +class QSGTexture; +class QSGMaterial; +class QSGMaterialShader; +class QSGEngine; + +class QOpenGLContext; +class QOpenGLFramebufferObject; + +class QDeclarativeTextureFactory; + +class Q_QUICK_EXPORT QSGContext : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QSGContext) + +public: + explicit QSGContext(QObject *parent = 0); + ~QSGContext(); + + virtual void initialize(QOpenGLContext *context); + + QSGRenderer *renderer() const; + + void setRootNode(QSGRootNode *node); + QSGRootNode *rootNode() const; + + QOpenGLContext *glContext() const; + + bool isReady() const; + + QSGMaterialShader *prepareMaterial(QSGMaterial *material); + + virtual void renderNextFrame(QOpenGLFramebufferObject *fbo = 0); + + virtual QSGDistanceFieldGlyphCache *createDistanceFieldGlyphCache(const QRawFont &font); + + virtual QSGRectangleNode *createRectangleNode(); + virtual QSGImageNode *createImageNode(); + virtual QSGGlyphNode *createGlyphNode(); + virtual QSGRenderer *createRenderer(); + + virtual bool canDecodeImageToTexture() const; + virtual QSGTexture *decodeImageToTexture(QIODevice *dev, + QSize *size, + const QSize &requestSize); + virtual QSGTexture *createTexture(const QImage &image = QImage()) const; + virtual QSize minimumFBOSize() const; + + virtual QSurfaceFormat defaultSurfaceFormat() const; + + QSGTexture *textureForFactory(QDeclarativeTextureFactory *factory); + + static QSGContext *createDefaultContext(); + + void setFlashModeEnabled(bool enabled); + bool isFlashModeEnabled() const; + + void setRenderAlpha(qreal renderAlpha); + qreal renderAlpha() const; + + void setDistanceFieldEnabled(bool enabled); + bool isDistanceFieldEnabled() const; + + virtual QAnimationDriver *createAnimationDriver(QObject *parent); + +signals: + void ready(); + +public slots: + void textureFactoryDestroyed(QObject *o); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGCONTEXT_H diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp new file mode 100644 index 0000000000..a07793f44f --- /dev/null +++ b/src/quick/scenegraph/qsgcontextplugin.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgcontextplugin_p.h" +#include <QtQuick/private/qsgcontext_p.h> +#include <QtGui/qguiapplication.h> +#include <QtCore/private/qfactoryloader_p.h> +#include <QtCore/qlibraryinfo.h> + +QT_BEGIN_NAMESPACE + +QSGContextPlugin::QSGContextPlugin(QObject *parent) + : QObject(parent) +{ +} + +QSGContextPlugin::~QSGContextPlugin() +{ +} + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QSGContextFactoryInterface_iid, QLatin1String("/scenegraph"))) +#endif + +/*! + \fn QSGContext *QSGContext::createDefaultContext() + + Creates a default scene graph context for the current hardware. + This may load a device-specific plugin. +*/ +QSGContext *QSGContext::createDefaultContext() +{ + const QStringList args = QGuiApplication::arguments(); + QString device; + for (int index = 0; index < args.count(); ++index) { + if (args.at(index).startsWith(QLatin1String("--device="))) { + device = args.at(index).mid(9); + break; + } + } + if (device.isEmpty()) + device = QString::fromLocal8Bit(qgetenv("QMLSCENE_DEVICE")); + if (device.isEmpty()) + return new QSGContext(); + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + if (QSGContextFactoryInterface *factory + = qobject_cast<QSGContextFactoryInterface*> + (loader()->instance(device))) { + QSGContext *context = factory->create(device); + if (context) + return context; + } +#ifndef QT_NO_DEBUG + qWarning("Could not create scene graph context for device '%s'" + " - check that plugins are installed correctly in %s", + qPrintable(device), + qPrintable(QLibraryInfo::location(QLibraryInfo::PluginsPath))); +#endif +#endif // QT_NO_LIBRARY || QT_NO_SETTINGS + + return new QSGContext(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgcontextplugin_p.h b/src/quick/scenegraph/qsgcontextplugin_p.h new file mode 100644 index 0000000000..a480ee51eb --- /dev/null +++ b/src/quick/scenegraph/qsgcontextplugin_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGCONTEXTPLUGIN_H +#define QSGCONTEXTPLUGIN_H + +#include <QtQuick/qtquickglobal.h> +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGContext; + +struct Q_QUICK_EXPORT QSGContextFactoryInterface : public QFactoryInterface +{ + virtual QSGContext *create(const QString &key) const = 0; +}; + +#define QSGContextFactoryInterface_iid \ + "com.trolltech.Qt.QSGContextFactoryInterface" +Q_DECLARE_INTERFACE(QSGContextFactoryInterface, QSGContextFactoryInterface_iid) + +class Q_QUICK_EXPORT QSGContextPlugin : public QObject, public QSGContextFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QSGContextFactoryInterface:QFactoryInterface) +public: + explicit QSGContextPlugin(QObject *parent = 0); + virtual ~QSGContextPlugin(); + + virtual QStringList keys() const = 0; + virtual QSGContext *create(const QString &key) const = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGCONTEXTPLUGIN_H diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp new file mode 100644 index 0000000000..95ccc8f437 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdefaultdistancefieldglyphcache_p.h" + +#include <QtQuick/private/qsgdistancefieldutil_p.h> +#include <qopenglfunctions.h> + +QT_BEGIN_NAMESPACE + +QHash<QString, QOpenGLMultiGroupSharedResource> QSGDefaultDistanceFieldGlyphCache::m_textures_data; + +QSGDefaultDistanceFieldGlyphCache::DistanceFieldTextureData *QSGDefaultDistanceFieldGlyphCache::textureData(QOpenGLContext *c) +{ + QString key = QString::fromLatin1("%1_%2_%3_%4") + .arg(font().familyName()) + .arg(font().styleName()) + .arg(font().weight()) + .arg(font().style()); + return m_textures_data[key].value<QSGDefaultDistanceFieldGlyphCache::DistanceFieldTextureData>(c); +} + +QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font) + : QSGDistanceFieldGlyphCache(man, c, font) + , m_maxTextureSize(0) +{ + m_textureData = textureData(c); +} + +void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QVector<glyph_t> &glyphs) +{ + int count = glyphs.count(); + + // Avoid useless and costly glyph re-generation + if (cacheIsFull() && !m_textureData->unusedGlyphs.isEmpty()) { + for (int i = 0; i < count; ++i) { + glyph_t glyphIndex = glyphs.at(i); + if (containsGlyph(glyphIndex) && m_textureData->unusedGlyphs.contains(glyphIndex)) + m_textureData->unusedGlyphs.remove(glyphIndex); + } + } + + QList<GlyphPosition> glyphPositions; + QVector<glyph_t> glyphsToRender; + + for (int i = 0; i < count; ++i) { + glyph_t glyphIndex = glyphs.at(i); + + if (++m_textureData->glyphRefCount[glyphIndex] == 1) + m_textureData->unusedGlyphs.remove(glyphIndex); + + if (cacheIsFull() && m_textureData->unusedGlyphs.isEmpty()) + continue; + + GlyphPosition p; + p.glyph = glyphIndex; + p.position = QPointF(m_textureData->currX, m_textureData->currY); + + if (!cacheIsFull()) { + m_textureData->currX += QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()); + if (m_textureData->currX >= maxTextureSize()) { + m_textureData->currX = 0; + m_textureData->currY += QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()); + } + } else { + // Recycle glyphs + if (!m_textureData->unusedGlyphs.isEmpty()) { + glyph_t unusedGlyph = *m_textureData->unusedGlyphs.constBegin(); + TexCoord unusedCoord = glyphTexCoord(unusedGlyph); + p.position = QPointF(unusedCoord.x, unusedCoord.y); + m_textureData->unusedGlyphs.remove(unusedGlyph); + removeGlyph(unusedGlyph); + } + } + + if (p.position.y() < maxTextureSize()) { + glyphPositions.append(p); + glyphsToRender.append(glyphIndex); + } + } + + addGlyphPositions(glyphPositions); + markGlyphsToRender(glyphsToRender); +} + +void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs) +{ + int requiredWidth = maxTextureSize(); + int rows = 128 / (requiredWidth / QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution())); // Enough rows to fill the latin1 set by default.. + int requiredHeight = qMin(maxTextureSize(), + qMax(m_textureData->currY + QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()), + QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()) * rows)); + + resizeTexture((requiredWidth), (requiredHeight)); + glBindTexture(GL_TEXTURE_2D, m_textureData->texture); + + QVector<glyph_t> glyphTextures; + + QHash<glyph_t, QImage>::const_iterator it; + for (it = glyphs.constBegin(); it != glyphs.constEnd(); ++it) { + glyph_t glyphIndex = it.key(); + TexCoord c = glyphTexCoord(glyphIndex); + + glyphTextures.append(glyphIndex); + + QImage glyph = it.value(); + + if (useWorkaroundBrokenFBOReadback()) { + uchar *inBits = glyph.scanLine(0); + uchar *outBits = m_textureData->image.scanLine(int(c.y)) + int(c.x); + for (int y = 0; y < glyph.height(); ++y) { + qMemCopy(outBits, inBits, glyph.width()); + inBits += glyph.bytesPerLine(); + outBits += m_textureData->image.bytesPerLine(); + } + } + + glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, glyph.width(), glyph.height(), GL_ALPHA, GL_UNSIGNED_BYTE, glyph.constBits()); + } + + Texture t; + t.textureId = m_textureData->texture; + t.size = m_textureData->size; + addGlyphTextures(glyphTextures, t); +} + +void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QVector<glyph_t> &glyphs) +{ + int count = glyphs.count(); + for (int i = 0; i < count; ++i) { + glyph_t glyphIndex = glyphs.at(i); + if (--m_textureData->glyphRefCount[glyphIndex] == 0 && !glyphTexCoord(glyphIndex).isNull()) + m_textureData->unusedGlyphs.insert(glyphIndex); + } +} + +void QSGDefaultDistanceFieldGlyphCache::createTexture(int width, int height) +{ + if (useWorkaroundBrokenFBOReadback() && m_textureData->image.isNull()) + m_textureData->image = QImage(width, height, QImage::Format_Indexed8); + + while (glGetError() != GL_NO_ERROR) { } + + glGenTextures(1, &m_textureData->texture); + glBindTexture(GL_TEXTURE_2D, m_textureData->texture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + m_textureData->size = QSize(width, height); + + GLuint error = glGetError(); + if (error != GL_NO_ERROR) { + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &m_textureData->texture); + m_textureData->texture = 0; + } + +} + +void QSGDefaultDistanceFieldGlyphCache::resizeTexture(int width, int height) +{ + int oldWidth = m_textureData->size.width(); + int oldHeight = m_textureData->size.height(); + if (width == oldWidth && height == oldHeight) + return; + + GLuint oldTexture = m_textureData->texture; + createTexture(width, height); + + if (!oldTexture) + return; + + updateTexture(oldTexture, m_textureData->texture, m_textureData->size); + + if (useWorkaroundBrokenFBOReadback()) { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, oldWidth, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, m_textureData->image.constBits()); + m_textureData->image = m_textureData->image.copy(0, 0, width, height); + glDeleteTextures(1, &oldTexture); + return; + } + + if (!m_textureData->blitProgram) + m_textureData->createBlitProgram(); + + Q_ASSERT(m_textureData->blitProgram); + + if (!m_textureData->fbo) + ctx->functions()->glGenFramebuffers(1, &m_textureData->fbo); + ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_textureData->fbo); + + GLuint tmp_texture; + glGenTextures(1, &tmp_texture); + glBindTexture(GL_TEXTURE_2D, tmp_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + ctx->functions()->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, tmp_texture, 0); + + ctx->functions()->glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, oldTexture); + + // save current render states + GLboolean stencilTestEnabled; + GLboolean depthTestEnabled; + GLboolean scissorTestEnabled; + GLboolean blendEnabled; + GLint viewport[4]; + GLint oldProgram; + glGetBooleanv(GL_STENCIL_TEST, &stencilTestEnabled); + glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled); + glGetBooleanv(GL_SCISSOR_TEST, &scissorTestEnabled); + glGetBooleanv(GL_BLEND, &blendEnabled); + glGetIntegerv(GL_VIEWPORT, &viewport[0]); + glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgram); + + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + + glViewport(0, 0, oldWidth, oldHeight); + + ctx->functions()->glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_textureData->blitVertexCoordinateArray); + ctx->functions()->glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_textureData->blitTextureCoordinateArray); + + m_textureData->blitProgram->bind(); + m_textureData->blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); + m_textureData->blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR)); + m_textureData->blitProgram->disableAttributeArray(int(QT_OPACITY_ATTR)); + m_textureData->blitProgram->setUniformValue("imageTexture", GLuint(0)); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glBindTexture(GL_TEXTURE_2D, m_textureData->texture); + + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight); + + ctx->functions()->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, 0); + glDeleteTextures(1, &tmp_texture); + glDeleteTextures(1, &oldTexture); + + ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // restore render states + if (stencilTestEnabled) + glEnable(GL_STENCIL_TEST); + if (depthTestEnabled) + glEnable(GL_DEPTH_TEST); + if (scissorTestEnabled) + glEnable(GL_SCISSOR_TEST); + if (blendEnabled) + glEnable(GL_BLEND); + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + ctx->functions()->glUseProgram(oldProgram); +} + +bool QSGDefaultDistanceFieldGlyphCache::useWorkaroundBrokenFBOReadback() const +{ + static bool set = false; + static bool useWorkaround = false; + if (!set) { + QOpenGLContextPrivate *ctx_p = static_cast<QOpenGLContextPrivate *>(QOpenGLContextPrivate::get(ctx)); + useWorkaround = ctx_p->workaround_brokenFBOReadBack; + set = true; + } + return useWorkaround; +} + +int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const +{ + if (!m_maxTextureSize) + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize); + return m_maxTextureSize; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h new file mode 100644 index 0000000000..b52ec45107 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGDEFAULTDISTANCEFIELDGLYPHCACHE_H +#define QSGDEFAULTDISTANCEFIELDGLYPHCACHE_H + +#include <QtGui/qopenglfunctions.h> +#include <private/qsgadaptationlayer_p.h> +#include <qopenglshaderprogram.h> +#include <QtGui/private/qopenglengineshadersource_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGDefaultDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache +{ +public: + QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font); + + void requestGlyphs(const QVector<glyph_t> &glyphs); + void storeGlyphs(const QHash<glyph_t, QImage> &glyphs); + void releaseGlyphs(const QVector<glyph_t> &glyphs); + + bool cacheIsFull() const { return m_textureData->currY >= maxTextureSize(); } + bool useWorkaroundBrokenFBOReadback() const; + int maxTextureSize() const; + +private: + void createTexture(int width, int height); + void resizeTexture(int width, int height); + + mutable int m_maxTextureSize; + + struct DistanceFieldTextureData : public QOpenGLSharedResource { + GLuint texture; + GLuint fbo; + QSize size; + QHash<glyph_t, quint32> glyphRefCount; + QSet<glyph_t> unusedGlyphs; + int currX; + int currY; + QImage image; + + QOpenGLShaderProgram *blitProgram; + GLfloat blitVertexCoordinateArray[8]; + GLfloat blitTextureCoordinateArray[8]; + + DistanceFieldTextureData(QOpenGLContext *ctx) + : QOpenGLSharedResource(ctx->shareGroup()) + , texture(0) + , fbo(0) + , currX(0) + , currY(0) + , blitProgram(0) + { + blitVertexCoordinateArray[0] = -1.0f; + blitVertexCoordinateArray[1] = -1.0f; + blitVertexCoordinateArray[2] = 1.0f; + blitVertexCoordinateArray[3] = -1.0f; + blitVertexCoordinateArray[4] = 1.0f; + blitVertexCoordinateArray[5] = 1.0f; + blitVertexCoordinateArray[6] = -1.0f; + blitVertexCoordinateArray[7] = 1.0f; + + blitTextureCoordinateArray[0] = 0.0f; + blitTextureCoordinateArray[1] = 0.0f; + blitTextureCoordinateArray[2] = 1.0f; + blitTextureCoordinateArray[3] = 0.0f; + blitTextureCoordinateArray[4] = 1.0f; + blitTextureCoordinateArray[5] = 1.0f; + blitTextureCoordinateArray[6] = 0.0f; + blitTextureCoordinateArray[7] = 1.0f; + } + + void invalidateResource() + { + texture = 0; + fbo = 0; + size = QSize(); + delete blitProgram; + blitProgram = 0; + } + + void freeResource(QOpenGLContext *ctx) + { + glDeleteTextures(1, &texture); + ctx->functions()->glDeleteFramebuffers(1, &fbo); + delete blitProgram; + blitProgram = 0; + } + + void createBlitProgram() + { + blitProgram = new QOpenGLShaderProgram; + { + QString source; + source.append(QLatin1String(qopenglslMainWithTexCoordsVertexShader)); + source.append(QLatin1String(qopenglslUntransformedPositionVertexShader)); + + QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, blitProgram); + vertexShader->compileSourceCode(source); + + blitProgram->addShader(vertexShader); + } + { + QString source; + source.append(QLatin1String(qopenglslMainFragmentShader)); + source.append(QLatin1String(qopenglslImageSrcFragmentShader)); + + QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, blitProgram); + fragmentShader->compileSourceCode(source); + + blitProgram->addShader(fragmentShader); + } + blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + blitProgram->link(); + } + }; + + DistanceFieldTextureData *textureData(QOpenGLContext *c); + DistanceFieldTextureData *m_textureData; + static QHash<QString, QOpenGLMultiGroupSharedResource> m_textures_data; +}; + +QT_END_NAMESPACE + +#endif // QSGDEFAULTDISTANCEFIELDGLYPHCACHE_H diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp new file mode 100644 index 0000000000..f41fbe486a --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdefaultglyphnode_p.h" +#include "qsgdefaultglyphnode_p_p.h" + +#include <qopenglshaderprogram.h> +#include <private/qfont_p.h> + +QT_BEGIN_NAMESPACE + +QSGDefaultGlyphNode::QSGDefaultGlyphNode() + : m_material(0) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) +{ + m_geometry.setDrawingMode(GL_TRIANGLES); + setGeometry(&m_geometry); +} + +QSGDefaultGlyphNode::~QSGDefaultGlyphNode() +{ + delete m_material; +} + +void QSGDefaultGlyphNode::setColor(const QColor &color) +{ + m_color = color; + if (m_material != 0) { + m_material->setColor(color); + markDirty(DirtyMaterial); + } +} + +void QSGDefaultGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) +{ + if (m_material != 0) + delete m_material; + + QRawFont font = glyphs.rawFont(); + m_material = new QSGTextMaskMaterial(font); + m_material->setColor(m_color); + + QRectF boundingRect; + m_material->populate(position, glyphs.glyphIndexes(), glyphs.positions(), geometry(), + &boundingRect, &m_baseLine); + + setMaterial(m_material); + setBoundingRect(boundingRect); + + markDirty(DirtyGeometry); + +#ifdef QML_RUNTIME_TESTING + description = QLatin1String("glyphs"); +#endif +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp new file mode 100644 index 0000000000..36efe9b570 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp @@ -0,0 +1,306 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdefaultglyphnode_p_p.h" + +#include <qopenglshaderprogram.h> + +#include <QtGui/private/qopengltextureglyphcache_p.h> +#include <private/qfontengine_p.h> +#include <private/qopenglextensions_p.h> + +#include <QtQuick/private/qsgtexture_p.h> + +#include <private/qrawfont_p.h> + +QT_BEGIN_NAMESPACE + +class QSGTextMaskMaterialData : public QSGMaterialShader +{ +public: + QSGTextMaskMaterialData(); + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual char const *const *attributeNames() const; +private: + virtual void initialize(); + virtual const char *vertexShader() const; + virtual const char *fragmentShader() const; + + int m_matrix_id; + int m_color_id; + int m_textureScale_id; +}; + +const char *QSGTextMaskMaterialData::vertexShader() const { + return + "uniform highp mat4 matrix; \n" + "uniform highp vec2 textureScale; \n" + "attribute highp vec4 vCoord; \n" + "attribute highp vec2 tCoord; \n" + "varying highp vec2 sampleCoord; \n" + "void main() { \n" + " sampleCoord = tCoord * textureScale; \n" + " gl_Position = matrix * vCoord; \n" + "}"; +} + +const char *QSGTextMaskMaterialData::fragmentShader() const { + return + "varying highp vec2 sampleCoord; \n" + "uniform sampler2D texture; \n" + "uniform lowp vec4 color; \n" + "void main() { \n" + " gl_FragColor = color * texture2D(texture, sampleCoord).a; \n" + "}"; +} + +char const *const *QSGTextMaskMaterialData::attributeNames() const +{ + static char const *const attr[] = { "vCoord", "tCoord", 0 }; + return attr; +} + +QSGTextMaskMaterialData::QSGTextMaskMaterialData() +{ +} + +void QSGTextMaskMaterialData::initialize() +{ + m_matrix_id = program()->uniformLocation("matrix"); + m_color_id = program()->uniformLocation("color"); + m_textureScale_id = program()->uniformLocation("textureScale"); +} + +void QSGTextMaskMaterialData::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect); + QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect); + + if (oldMaterial == 0 || material->color() != oldMaterial->color() || state.isOpacityDirty()) { + QVector4D color(material->color().redF(), material->color().greenF(), + material->color().blueF(), material->color().alphaF()); + color *= state.opacity(); + program()->setUniformValue(m_color_id, color); + } + + bool updated = material->ensureUpToDate(); + Q_ASSERT(material->texture()); + + Q_ASSERT(oldMaterial == 0 || oldMaterial->texture()); + if (updated + || oldMaterial == 0 + || oldMaterial->texture()->textureId() != material->texture()->textureId()) { + program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->cacheTextureWidth(), + 1.0 / material->cacheTextureHeight())); + glBindTexture(GL_TEXTURE_2D, material->texture()->textureId()); + + // Set the mag/min filters to be linear. We only need to do this when the texture + // has been recreated. + if (updated) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + } + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); +} + +QSGTextMaskMaterial::QSGTextMaskMaterial(const QRawFont &font) + : m_texture(0), m_glyphCache(), m_font(font) +{ + init(); +} + +QSGTextMaskMaterial::~QSGTextMaskMaterial() +{ +} + +void QSGTextMaskMaterial::init() +{ + Q_ASSERT(m_font.isValid()); + + QFontEngineGlyphCache::Type type = QFontEngineGlyphCache::Raster_A8; + setFlag(Blending, true); + + QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); + Q_ASSERT(ctx != 0); + + QRawFontPrivate *fontD = QRawFontPrivate::get(m_font); + if (fontD->fontEngine != 0) { + m_glyphCache = fontD->fontEngine->glyphCache(ctx, type, QTransform()); + if (!m_glyphCache || m_glyphCache->cacheType() != type) { + m_glyphCache = new QOpenGLTextureGlyphCache(type, QTransform()); + fontD->fontEngine->setGlyphCache(ctx, m_glyphCache.data()); + } + } +} + +void QSGTextMaskMaterial::populate(const QPointF &p, + const QVector<quint32> &glyphIndexes, + const QVector<QPointF> &glyphPositions, + QSGGeometry *geometry, + QRectF *boundingRect, + QPointF *baseLine) +{ + Q_ASSERT(m_font.isValid()); + QVector<QFixedPoint> fixedPointPositions; + for (int i=0; i<glyphPositions.size(); ++i) + fixedPointPositions.append(QFixedPoint::fromPointF(glyphPositions.at(i))); + + QTextureGlyphCache *cache = glyphCache(); + + QRawFontPrivate *fontD = QRawFontPrivate::get(m_font); + cache->populate(fontD->fontEngine, glyphIndexes.size(), glyphIndexes.constData(), + fixedPointPositions.data()); + cache->fillInPendingGlyphs(); + + int margin = cache->glyphMargin(); + + Q_ASSERT(geometry->indexType() == GL_UNSIGNED_SHORT); + geometry->allocate(glyphIndexes.size() * 4, glyphIndexes.size() * 6); + QVector4D *vp = (QVector4D *)geometry->vertexDataAsTexturedPoint2D(); + Q_ASSERT(geometry->sizeOfVertex() == sizeof(QVector4D)); + ushort *ip = geometry->indexDataAsUShort(); + + QPointF position(p.x(), p.y() - m_font.ascent()); + bool supportsSubPixelPositions = fontD->fontEngine->supportsSubPixelPositions(); + for (int i=0; i<glyphIndexes.size(); ++i) { + QFixed subPixelPosition; + if (supportsSubPixelPositions) + subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPositions.at(i).x())); + + QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i), subPixelPosition); + const QTextureGlyphCache::Coord &c = cache->coords.value(glyph); + + QPointF glyphPosition = glyphPositions.at(i) + position; + int x = qRound(glyphPosition.x()) + c.baseLineX - margin; + int y = qRound(glyphPosition.y()) - c.baseLineY - margin; + + *boundingRect |= QRectF(x + margin, y + margin, c.w, c.h); + + float cx1 = x; + float cx2 = x + c.w; + float cy1 = y; + float cy2 = y + c.h; + + float tx1 = c.x; + float tx2 = (c.x + c.w); + float ty1 = c.y; + float ty2 = (c.y + c.h); + + if (baseLine->isNull()) + *baseLine = glyphPosition; + + vp[4 * i + 0] = QVector4D(cx1, cy1, tx1, ty1); + vp[4 * i + 1] = QVector4D(cx2, cy1, tx2, ty1); + vp[4 * i + 2] = QVector4D(cx1, cy2, tx1, ty2); + vp[4 * i + 3] = QVector4D(cx2, cy2, tx2, ty2); + + int o = i * 4; + ip[6 * i + 0] = o + 0; + ip[6 * i + 1] = o + 2; + ip[6 * i + 2] = o + 3; + ip[6 * i + 3] = o + 3; + ip[6 * i + 4] = o + 1; + ip[6 * i + 5] = o + 0; + } +} + +QSGMaterialType *QSGTextMaskMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +QOpenGLTextureGlyphCache *QSGTextMaskMaterial::glyphCache() const +{ + return static_cast<QOpenGLTextureGlyphCache*>(m_glyphCache.data()); +} + +QSGMaterialShader *QSGTextMaskMaterial::createShader() const +{ + return new QSGTextMaskMaterialData; +} + +int QSGTextMaskMaterial::compare(const QSGMaterial *o) const +{ + Q_ASSERT(o && type() == o->type()); + const QSGTextMaskMaterial *other = static_cast<const QSGTextMaskMaterial *>(o); + if (m_glyphCache != other->m_glyphCache) + return m_glyphCache - other->m_glyphCache; + QRgb c1 = m_color.rgba(); + QRgb c2 = other->m_color.rgba(); + return int(c2 < c1) - int(c1 < c2); +} + +bool QSGTextMaskMaterial::ensureUpToDate() +{ + QSize glyphCacheSize(glyphCache()->width(), glyphCache()->height()); + if (glyphCacheSize != m_size) { + if (m_texture) + delete m_texture; + m_texture = new QSGPlainTexture(); + m_texture->setTextureId(glyphCache()->texture()); + m_texture->setTextureSize(QSize(glyphCache()->width(), glyphCache()->height())); + m_texture->setOwnsTexture(false); + + m_size = glyphCacheSize; + + return true; + } else { + return false; + } +} + +int QSGTextMaskMaterial::cacheTextureWidth() const +{ + return glyphCache()->width(); +} + +int QSGTextMaskMaterial::cacheTextureHeight() const +{ + return glyphCache()->height(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h new file mode 100644 index 0000000000..cc14d33a30 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DEFAULT_GLYPHNODE_H +#define DEFAULT_GLYPHNODE_H + +#include <private/qsgadaptationlayer_p.h> +#include <QtQuick/qsgnode.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGlyphs; +class QSGTextMaskMaterial; +class QSGDefaultGlyphNode: public QSGGlyphNode +{ +public: + QSGDefaultGlyphNode(); + ~QSGDefaultGlyphNode(); + + virtual QPointF baseLine() const { return m_baseLine; } + virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs); + virtual void setColor(const QColor &color); + + virtual void setPreferredAntialiasingMode(AntialiasingMode) { } + virtual void setStyle(QQuickText::TextStyle) { } + virtual void setStyleColor(const QColor &) { } + + virtual void update() { } + +private: + QGlyphRun m_glyphs; + QPointF m_position; + QColor m_color; + + QPointF m_baseLine; + QSGTextMaskMaterial *m_material; + + QSGGeometry m_geometry; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // DEFAULT_GLYPHNODE_H diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h new file mode 100644 index 0000000000..2378178a1b --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEXTMASKMATERIAL_H +#define TEXTMASKMATERIAL_H + +#include <qcolor.h> +#include <QtQuick/qsgmaterial.h> +#include <QtQuick/qsgtexture.h> +#include <QtQuick/qsggeometry.h> +#include <qshareddata.h> +#include <QtQuick/private/qsgtexture_p.h> +#include <qrawfont.h> + +QT_BEGIN_NAMESPACE + +class QFontEngineGlyphCache; +class QOpenGLTextureGlyphCache; +class QFontEngine; +class Geometry; +class QSGTextMaskMaterial: public QSGMaterial +{ +public: + QSGTextMaskMaterial(const QRawFont &font); + ~QSGTextMaskMaterial(); + + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; + virtual int compare(const QSGMaterial *other) const; + + void setColor(const QColor &color) { m_color = color; } + const QColor &color() const { return m_color; } + + QSGTexture *texture() const { return m_texture; } + + int cacheTextureWidth() const; + int cacheTextureHeight() const; + + bool ensureUpToDate(); + + QOpenGLTextureGlyphCache *glyphCache() const; + void populate(const QPointF &position, + const QVector<quint32> &glyphIndexes, const QVector<QPointF> &glyphPositions, + QSGGeometry *geometry, QRectF *boundingRect, QPointF *baseLine); + +private: + void init(); + + QSGPlainTexture *m_texture; + QExplicitlySharedDataPointer<QFontEngineGlyphCache> m_glyphCache; + QRawFont m_font; + QColor m_color; + QSize m_size; +}; + +QT_END_NAMESPACE + +#endif // TEXTMASKMATERIAL_H diff --git a/src/quick/scenegraph/qsgdefaultimagenode.cpp b/src/quick/scenegraph/qsgdefaultimagenode.cpp new file mode 100644 index 0000000000..f360aaef0d --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultimagenode.cpp @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdefaultimagenode_p.h" + +#include <QtQuick/private/qsgtextureprovider_p.h> + +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qmath.h> +#include <QtGui/qopenglfunctions.h> + +QT_BEGIN_NAMESPACE + +QSGDefaultImageNode::QSGDefaultImageNode() + : m_sourceRect(0, 0, 1, 1) + , m_dirtyGeometry(false) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) +{ + setMaterial(&m_materialO); + setOpaqueMaterial(&m_material); + setGeometry(&m_geometry); + +#ifdef QML_RUNTIME_TESTING + description = QLatin1String("image"); +#endif +} + +void QSGDefaultImageNode::setTargetRect(const QRectF &rect) +{ + if (rect == m_targetRect) + return; + m_targetRect = rect; + m_dirtyGeometry = true; +} + +void QSGDefaultImageNode::setSourceRect(const QRectF &rect) +{ + if (rect == m_sourceRect) + return; + m_sourceRect = rect; + m_dirtyGeometry = true; +} + + +void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.filtering() == filtering) + return; + + m_material.setFiltering(filtering); + m_materialO.setFiltering(filtering); + markDirty(DirtyMaterial); +} + + +void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.mipmapFiltering() == filtering) + return; + + m_material.setMipmapFiltering(filtering); + m_materialO.setMipmapFiltering(filtering); + markDirty(DirtyMaterial); +} + +void QSGDefaultImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) +{ + if (m_material.verticalWrapMode() == wrapMode) + return; + + m_material.setVerticalWrapMode(wrapMode); + m_materialO.setVerticalWrapMode(wrapMode); + markDirty(DirtyMaterial); +} + +void QSGDefaultImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) +{ + if (m_material.horizontalWrapMode() == wrapMode) + return; + + m_material.setHorizontalWrapMode(wrapMode); + m_materialO.setHorizontalWrapMode(wrapMode); + markDirty(DirtyMaterial); +} + + +void QSGDefaultImageNode::setTexture(QSGTexture *texture) +{ + if (texture == m_material.texture()) + return; + + m_material.setTexture(texture); + m_materialO.setTexture(texture); + // Texture cleanup +// if (!texture.isNull()) +// m_material.setBlending(texture->hasAlphaChannel()); + markDirty(DirtyMaterial); + + // Because the texture can be a different part of the atlas, we need to update it... + m_dirtyGeometry = true; +} + +void QSGDefaultImageNode::update() +{ + if (m_dirtyGeometry) + updateGeometry(); +} + +void QSGDefaultImageNode::preprocess() +{ + bool doDirty = false; + QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(m_material.texture()); + if (t) { + doDirty = t->updateTexture(); + updateGeometry(); + } +// ### texture cleanup +// bool alpha = m_material.blending(); +// if (!m_material->texture().isNull() && alpha != m_material.texture()->hasAlphaChannel()) { +// m_material.setBlending(!alpha); +// doDirty = true; +// } + + if (doDirty) + markDirty(DirtyMaterial); +} + +inline static bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} + +namespace { + struct X { float x, tx; }; + struct Y { float y, ty; }; +} + +void QSGDefaultImageNode::updateGeometry() +{ + const QSGTexture *t = m_material.texture(); + if (!t) { + m_geometry.allocate(4); + m_geometry.setDrawingMode(GL_TRIANGLE_STRIP); + QSGGeometry::updateTexturedRectGeometry(&m_geometry, QRectF(), QRectF()); + } else { + QRectF textureRect = t->textureSubRect(); + + bool isSubRect = textureRect != QRectF(0, 0, 1, 1); + const int ceilRight = qCeil(m_sourceRect.right()); + const int floorLeft = qFloor(m_sourceRect.left()); + const int ceilBottom = qCeil(m_sourceRect.bottom()); + const int floorTop = qFloor(m_sourceRect.top()); + const int hCells = ceilRight - floorLeft; + const int vCells = ceilBottom - floorTop; + bool isRepeating = hCells > 1 || vCells > 1; + +#ifdef QT_OPENGL_ES_2 + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures); + + QSize size = t->textureSize(); + bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); + + if (isRepeating && (isSubRect || (isNpot && !npotSupported))) { +#else + if (isRepeating && isSubRect) { +#endif + m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6); + m_geometry.setDrawingMode(GL_TRIANGLES); + QVarLengthArray<X, 32> xData(2 * hCells); + QVarLengthArray<Y, 32> yData(2 * vCells); + X *xs = xData.data(); + Y *ys = yData.data(); + + xs->x = m_targetRect.left(); + xs->tx = textureRect.x() + (m_sourceRect.left() - floorLeft) * textureRect.width(); + ++xs; + ys->y = m_targetRect.top(); + ys->ty = textureRect.y() + (m_sourceRect.top() - floorTop) * textureRect.height(); + ++ys; + + float a, b; + b = m_targetRect.width() / m_sourceRect.width(); + a = m_targetRect.x() - m_sourceRect.x() * b; + + float tex_x1 = textureRect.x(); + float tex_x2 = textureRect.right(); + float tex_y1 = textureRect.y(); + float tex_y2 = textureRect.bottom(); + for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) { + xs[0].x = xs[1].x = a + b * i; + xs[0].tx = tex_x2; + xs[1].tx = tex_x1; + xs += 2; + } + b = m_targetRect.height() / m_sourceRect.height(); + a = m_targetRect.y() - m_sourceRect.y() * b; + for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) { + ys[0].y = ys[1].y = a + b * i; + ys[0].ty = tex_y2; + ys[1].ty = tex_y1; + ys += 2; + } + + xs->x = m_targetRect.right(); + xs->tx = textureRect.x() + (m_sourceRect.right() - ceilRight + 1) * textureRect.width(); + + ys->y = m_targetRect.bottom(); + ys->ty = textureRect.y() + (m_sourceRect.bottom() - ceilBottom + 1) * textureRect.height(); + + 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) { + *indices++ = i; + *indices++ = i + 2; + *indices++ = i + 3; + *indices++ = i + 3; + *indices++ = i + 1; + *indices++ = i; + } + } else { + QRectF sr(textureRect.x() + m_sourceRect.x() * textureRect.width(), + textureRect.y() + m_sourceRect.y() * textureRect.height(), + m_sourceRect.width() * textureRect.width(), + m_sourceRect.height() * textureRect.height()); + + m_geometry.allocate(4); + m_geometry.setDrawingMode(GL_TRIANGLE_STRIP); + QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); + } + } + markDirty(DirtyGeometry); + m_dirtyGeometry = false; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultimagenode_p.h new file mode 100644 index 0000000000..f1b416d69c --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultimagenode_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef DEFAULT_PIXMAPNODE_H +#define DEFAULT_PIXMAPNODE_H + +#include <private/qsgadaptationlayer_p.h> + +#include <QtQuick/qsgtexturematerial.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGDefaultImageNode : public QSGImageNode +{ +public: + QSGDefaultImageNode(); + virtual void setTargetRect(const QRectF &rect); + virtual void setSourceRect(const QRectF &rect); + virtual void setTexture(QSGTexture *t); + virtual void update(); + + virtual void setMipmapFiltering(QSGTexture::Filtering filtering); + virtual void setFiltering(QSGTexture::Filtering filtering); + virtual void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode); + virtual void setVerticalWrapMode(QSGTexture::WrapMode wrapMode); + + virtual void preprocess(); + +private: + void updateGeometry(); + + QRectF m_targetRect; + QRectF m_sourceRect; + + QSGOpaqueTextureMaterial m_material; + QSGTextureMaterial m_materialO; + + uint m_dirtyGeometry : 1; + + QSGGeometry m_geometry; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp new file mode 100644 index 0000000000..bb89b4a9f8 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp @@ -0,0 +1,548 @@ + +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + + +#include "qsgdefaultrectanglenode_p.h" + +#include <QtQuick/qsgvertexcolormaterial.h> +#include <QtQuick/qsgtexturematerial.h> + +#include <QtQuick/private/qsgcontext_p.h> + +#include <QtCore/qmath.h> +#include <QtCore/qvarlengtharray.h> + +QT_BEGIN_NAMESPACE + +QSGDefaultRectangleNode::QSGDefaultRectangleNode(QSGContext *context) + : m_border(0) + , m_radius(0) + , m_pen_width(0) + , m_aligned(true) + , m_gradient_is_opaque(true) + , m_dirty_geometry(false) + , m_default_geometry(QSGGeometry::defaultAttributes_Point2D(), 4) + , m_context(context) +{ + setGeometry(&m_default_geometry); + setMaterial(&m_fill_material); + m_border_material.setColor(QColor(0, 0, 0)); + + m_material_type = TypeFlat; + +#ifdef QML_RUNTIME_TESTING + description = QLatin1String("rectangle"); +#endif +} + +QSGDefaultRectangleNode::~QSGDefaultRectangleNode() +{ + if (m_material_type == TypeVertexGradient) + delete material(); + delete m_border; +} + +QSGGeometryNode *QSGDefaultRectangleNode::border() +{ + if (!m_border) { + m_border = new QSGGeometryNode; + m_border->setMaterial(&m_border_material); + QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 0); + m_border->setGeometry(geometry); + m_border->setFlag(QSGNode::OwnsGeometry); + } + return m_border; +} + +void QSGDefaultRectangleNode::setRect(const QRectF &rect) +{ + if (rect == m_rect) + return; + m_rect = rect; + m_dirty_geometry = true; +} + +void QSGDefaultRectangleNode::setColor(const QColor &color) +{ + if (color == m_fill_material.color()) + return; + m_fill_material.setColor(color); + if (m_gradient_stops.isEmpty()) { + Q_ASSERT(m_material_type == TypeFlat); + markDirty(DirtyMaterial); + } +} + +void QSGDefaultRectangleNode::setPenColor(const QColor &color) +{ + if (color == m_border_material.color()) + return; + m_border_material.setColor(color); + if (m_border) + m_border->markDirty(DirtyMaterial); +} + +void QSGDefaultRectangleNode::setPenWidth(qreal width) +{ + if (width == m_pen_width) + return; + m_pen_width = width; + if (m_pen_width <= 0 && m_border && m_border->parent()) + removeChildNode(m_border); + else if (m_pen_width > 0 && !border()->parent()) + appendChildNode(m_border); + m_dirty_geometry = true; +} + + +void QSGDefaultRectangleNode::setGradientStops(const QGradientStops &stops) +{ + if (stops.constData() == m_gradient_stops.constData()) + return; + + m_gradient_stops = stops; + + m_gradient_is_opaque = true; + for (int i = 0; i < stops.size(); ++i) + m_gradient_is_opaque &= stops.at(i).second.alpha() == 0xff; + + if (stops.isEmpty()) { + // No gradient specified, use flat color. + if (m_material_type != TypeFlat) { + delete material(); + + setMaterial(&m_fill_material); + m_material_type = TypeFlat; + + setGeometry(&m_default_geometry); + setFlag(OwnsGeometry, false); + } + } else { + if (m_material_type == TypeFlat) { + QSGVertexColorMaterial *material = new QSGVertexColorMaterial; + setMaterial(material); + m_material_type = TypeVertexGradient; + QSGGeometry *g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0); + setGeometry(g); + setFlag(OwnsGeometry); + } + static_cast<QSGVertexColorMaterial *>(material())->setFlag(QSGMaterial::Blending, !m_gradient_is_opaque); + } + + m_dirty_geometry = true; +} + +void QSGDefaultRectangleNode::setRadius(qreal radius) +{ + if (radius == m_radius) + return; + m_radius = radius; + m_dirty_geometry = true; +} + +void QSGDefaultRectangleNode::setAligned(bool aligned) +{ + if (aligned == m_aligned) + return; + m_aligned = aligned; + m_dirty_geometry = true; +} + +void QSGDefaultRectangleNode::update() +{ + if (m_dirty_geometry) { + updateGeometry(); + m_dirty_geometry = false; + } +} + +struct Color4ub +{ + unsigned char r, g, b, a; +}; + +Color4ub operator *(Color4ub c, float t) { c.a *= t; c.r *= t; c.g *= t; c.b *= t; return c; } +Color4ub operator +(Color4ub a, Color4ub b) { a.a += b.a; a.r += b.r; a.g += b.g; a.b += b.b; return a; } + +static inline Color4ub colorToColor4ub(const QColor &c) +{ + Color4ub color = { uchar(c.redF() * c.alphaF() * 255), + uchar(c.greenF() * c.alphaF() * 255), + uchar(c.blueF() * c.alphaF() * 255), + uchar(c.alphaF() * 255) + }; + return color; +} + +struct Vertex +{ + QVector2D position; +}; + +struct ColorVertex +{ + QVector2D position; + Color4ub color; +}; + +void QSGDefaultRectangleNode::updateGeometry() +{ + qreal penWidth = m_aligned ? qreal(qRound(m_pen_width)) : m_pen_width; + + // fast path for the simple case... + if ((penWidth == 0 || m_border_material.color().alpha() == 0) + && m_radius == 0 + && m_material_type == TypeFlat) { + QSGGeometry::updateRectGeometry(&m_default_geometry, m_rect); + return; + } + + QSGGeometry *fill = geometry(); + + // Check that the vertex type matches the material. + Q_ASSERT(m_material_type != TypeFlat || fill->sizeOfVertex() == sizeof(Vertex)); + Q_ASSERT(m_material_type != TypeVertexGradient || fill->sizeOfVertex() == sizeof(ColorVertex)); + + QSGGeometry *borderGeometry = 0; + if (m_border) { + borderGeometry = m_border->geometry(); + Q_ASSERT(borderGeometry->sizeOfVertex() == sizeof(Vertex)); + } + + int fillVertexCount = 0; + + // Preallocate arrays for a rectangle with 18 segments per corner and 3 gradient stops. + uchar *fillVertices = 0; + Vertex *borderVertices = 0; + + Color4ub fillColor = colorToColor4ub(m_fill_material.color()); + const QGradientStops &stops = m_gradient_stops; + + if (m_radius > 0) { + // Rounded corners. + + // Radius should never exceeds half of the width or half of the height + qreal radius = qMin(qMin(m_rect.width() * qreal(0.5), m_rect.height() * qreal(0.5)), m_radius); + QRectF innerRect = m_rect; + innerRect.adjust(radius, radius, -radius, -radius); + if (m_aligned && (int(penWidth) & 1)) { + // Pen width is odd, so add the offset as documented. + innerRect.moveLeft(innerRect.left() + qreal(0.5)); + innerRect.moveTop(innerRect.top() + qreal(0.5)); + } + + qreal innerRadius = radius - penWidth * qreal(0.5); + qreal outerRadius = radius + penWidth * qreal(0.5); + + // Number of segments per corner, approximately one per 3 pixels. + int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18); + + /* + + --+-__ + | segment + | _+ + --+-__ _- \ + -+ segment + --------+ \ <- gradient line + +-----+ + | | + + */ + + int nextGradientStop = 0; + qreal gradientPos = (radius - innerRadius) / (innerRect.height() + 2 * radius); + while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos) + ++nextGradientStop; + int lastGradientStop = stops.size() - 1; + qreal lastGradientPos = (innerRect.height() + radius + innerRadius) / (innerRect.height() + 2 * radius); + while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos) + --lastGradientStop; + + int borderVertexHead = 0; + int borderVertexTail = 0; + if (penWidth) { + // The reason I add extra vertices where the gradient lines intersect the border is + // to avoid pixel sized gaps between the fill and the border caused by floating point + // inaccuracies. + borderGeometry->allocate((segments + 1) * 2 * 4 + (lastGradientStop - nextGradientStop + 1) * 4 + 2); + borderVertexHead = borderVertexTail = (borderGeometry->vertexCount() >> 1) - 1; + borderVertices = (Vertex *)borderGeometry->vertexData(); + } + + fill->allocate((segments + 1) * 4 + (lastGradientStop - nextGradientStop + 1) * 2); + fillVertices = (uchar *)fill->vertexData(); + + qreal py = 0; // previous inner y-coordinate. + qreal plx = 0; // previous inner left x-coordinate. + qreal prx = 0; // previous inner right x-coordinate. + + qreal angle = qreal(0.5) * M_PI / qreal(segments); + qreal cosStep = qFastCos(angle); + qreal sinStep = qFastSin(angle); + + for (int part = 0; part < 2; ++part) { + qreal c = 1 - part; + qreal s = part; + for (int i = 0; i <= segments; ++i) { + qreal y, lx, rx; + if (innerRadius > 0) { + y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate. + lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate. + rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate. + gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / (innerRect.height() + 2 * radius); + } else { + y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate. + lx = innerRect.left() - innerRadius; // current inner left x-coordinate. + rx = innerRect.right() + innerRadius; // current inner right x-coordinate. + gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / (innerRect.height() + 2 * radius); + } + qreal Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate. + qreal lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate. + qreal rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate. + + while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { + // Insert vertices at gradient stops. + qreal gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * (innerRect.height() + 2 * radius); + Q_ASSERT(fillVertexCount >= 2); + qreal t = (gy - py) / (y - py); + qreal glx = plx * (1 - t) + t * lx; + qreal grx = prx * (1 - t) + t * rx; + + if (penWidth) { + const Vertex &first = borderVertices[borderVertexHead]; + borderVertices[--borderVertexHead].position = QVector2D(glx, gy); + borderVertices[--borderVertexHead] = first; + + const Vertex &last = borderVertices[borderVertexTail - 2]; + borderVertices[borderVertexTail++] = last; + borderVertices[borderVertexTail++].position = QVector2D(grx, gy); + } + + ColorVertex *vertices = (ColorVertex *)fillVertices; + + fillColor = colorToColor4ub(stops.at(nextGradientStop).second); + vertices[fillVertexCount].position = QVector2D(grx, gy); + vertices[fillVertexCount].color = fillColor; + ++fillVertexCount; + vertices[fillVertexCount].position = QVector2D(glx, gy); + vertices[fillVertexCount].color = fillColor; + ++fillVertexCount; + + ++nextGradientStop; + } + + if (penWidth) { + borderVertices[--borderVertexHead].position = QVector2D(lx, y); + borderVertices[--borderVertexHead].position = QVector2D(lX, Y); + borderVertices[borderVertexTail++].position = QVector2D(rX, Y); + borderVertices[borderVertexTail++].position = QVector2D(rx, y); + } + + if (stops.isEmpty()) { + Q_ASSERT(m_material_type == TypeFlat); + Vertex *vertices = (Vertex *)fillVertices; + vertices[fillVertexCount++].position = QVector2D(rx, y); + vertices[fillVertexCount++].position = QVector2D(lx, y); + } else { + if (nextGradientStop == 0) { + fillColor = colorToColor4ub(stops.at(0).second); + } else if (nextGradientStop == stops.size()) { + fillColor = colorToColor4ub(stops.last().second); + } else { + const QGradientStop &prev = stops.at(nextGradientStop - 1); + const QGradientStop &next = stops.at(nextGradientStop); + qreal t = (gradientPos - prev.first) / (next.first - prev.first); + fillColor = (colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t); + } + + ColorVertex *vertices = (ColorVertex *)fillVertices; + vertices[fillVertexCount].position = QVector2D(rx, y); + vertices[fillVertexCount].color = fillColor; + ++fillVertexCount; + vertices[fillVertexCount].position = QVector2D(lx, y); + vertices[fillVertexCount].color = fillColor; + ++fillVertexCount; + } + py = y; + plx = lx; + prx = rx; + + // Rotate + qreal tmp = c; + c = c * cosStep - s * sinStep; + s = s * cosStep + tmp * sinStep; + } + } + + if (penWidth) { + // Close border. + const Vertex &first = borderVertices[borderVertexHead]; + const Vertex &second = borderVertices[borderVertexHead + 1]; + borderVertices[borderVertexTail++] = first; + borderVertices[borderVertexTail++] = second; + + Q_ASSERT(borderVertexHead == 0 && borderVertexTail == borderGeometry->vertexCount()); + } + Q_ASSERT(fillVertexCount == fill->vertexCount()); + + } else { + + // Straight corners. + QRectF innerRect = m_rect; + QRectF outerRect = m_rect; + + qreal halfPenWidth = 0; + if (penWidth) { + if (m_aligned && (int(penWidth) & 1)) { + // Pen width is odd, so add the offset as documented. + innerRect.moveLeft(innerRect.left() + qreal(0.5)); + innerRect.moveTop(innerRect.top() + qreal(0.5)); + outerRect = innerRect; + } + halfPenWidth = penWidth * qreal(0.5); + innerRect.adjust(halfPenWidth, halfPenWidth, -halfPenWidth, -halfPenWidth); + outerRect.adjust(-halfPenWidth, -halfPenWidth, halfPenWidth, halfPenWidth); + } + + int nextGradientStop = 0; + qreal gradientPos = halfPenWidth / m_rect.height(); + while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos) + ++nextGradientStop; + int lastGradientStop = stops.size() - 1; + qreal lastGradientPos = (m_rect.height() - halfPenWidth) / m_rect.height(); + while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos) + --lastGradientStop; + + int borderVertexCount = 0; + if (penWidth) { + borderGeometry->allocate((1 + lastGradientStop - nextGradientStop) * 4 + 10); + borderVertices = (Vertex *)borderGeometry->vertexData(); + } + fill->allocate((3 + lastGradientStop - nextGradientStop) * 2); + fillVertices = (uchar *)fill->vertexData(); + + QVarLengthArray<qreal, 16> ys(3 + lastGradientStop - nextGradientStop); + int yCount = 0; + + for (int part = 0; part < 2; ++part) { + qreal y = (part ? innerRect.bottom() : innerRect.top()); + gradientPos = (y - innerRect.top() + halfPenWidth) / m_rect.height(); + + while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { + // Insert vertices at gradient stops. + qreal gy = (innerRect.top() - halfPenWidth) + stops.at(nextGradientStop).first * m_rect.height(); + Q_ASSERT(fillVertexCount >= 2); + + ColorVertex *vertices = (ColorVertex *)fillVertices; + + fillColor = colorToColor4ub(stops.at(nextGradientStop).second); + vertices[fillVertexCount].position = QVector2D(innerRect.right(), gy); + vertices[fillVertexCount].color = fillColor; + ++fillVertexCount; + vertices[fillVertexCount].position = QVector2D(innerRect.left(), gy); + vertices[fillVertexCount].color = fillColor; + ++fillVertexCount; + + ys[yCount++] = gy; + + ++nextGradientStop; + } + + if (stops.isEmpty()) { + Q_ASSERT(m_material_type == TypeFlat); + Vertex *vertices = (Vertex *)fillVertices; + vertices[fillVertexCount++].position = QVector2D(innerRect.right(), y); + vertices[fillVertexCount++].position = QVector2D(innerRect.left(), y); + } else { + if (nextGradientStop == 0) { + fillColor = colorToColor4ub(stops.at(0).second); + } else if (nextGradientStop == stops.size()) { + fillColor = colorToColor4ub(stops.last().second); + } else { + const QGradientStop &prev = stops.at(nextGradientStop - 1); + const QGradientStop &next = stops.at(nextGradientStop); + qreal t = (gradientPos - prev.first) / (next.first - prev.first); + fillColor = (colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t); + } + + ColorVertex *vertices = (ColorVertex *)fillVertices; + vertices[fillVertexCount].position = QVector2D(innerRect.right(), y); + vertices[fillVertexCount].color = fillColor; + ++fillVertexCount; + vertices[fillVertexCount].position = QVector2D(innerRect.left(), y); + vertices[fillVertexCount].color = fillColor; + ++fillVertexCount; + } + + ys[yCount++] = y; + } + + if (penWidth) { + borderVertices[borderVertexCount++].position = QVector2D(outerRect.right(), outerRect.top()); + borderVertices[borderVertexCount++].position = QVector2D(innerRect.right(), ys[0]); + for (int i = 1; i < fillVertexCount / 2; ++i) { + borderVertices[borderVertexCount++].position = QVector2D(outerRect.right(), outerRect.bottom()); + borderVertices[borderVertexCount++].position = QVector2D(innerRect.right(), ys[i]); + } + + borderVertices[borderVertexCount++].position = QVector2D(outerRect.left(), outerRect.bottom()); + borderVertices[borderVertexCount++].position = QVector2D(innerRect.left(), ys[fillVertexCount / 2 - 1]); + for (int i = fillVertexCount / 2 - 2; i >= 0; --i) { + borderVertices[borderVertexCount++].position = QVector2D(outerRect.left(), outerRect.top()); + borderVertices[borderVertexCount++].position = QVector2D(innerRect.left(), ys[i]); + } + + borderVertices[borderVertexCount++].position = QVector2D(outerRect.right(), outerRect.top()); + borderVertices[borderVertexCount++].position = QVector2D(innerRect.right(), innerRect.top()); + + Q_ASSERT(borderVertexCount == borderGeometry->vertexCount()); + } + Q_ASSERT(fillVertexCount == fill->vertexCount()); + } + + markDirty(DirtyGeometry); +} + + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h new file mode 100644 index 0000000000..b491913428 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef DEFAULT_RECTANGLENODE_H +#define DEFAULT_RECTANGLENODE_H + +#include <private/qsgadaptationlayer_p.h> + +#include <QtQuick/qsgflatcolormaterial.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGMaterial; +class QSGContext; + +class QSGDefaultRectangleNode : public QSGRectangleNode +{ +public: + QSGDefaultRectangleNode(QSGContext *context); + ~QSGDefaultRectangleNode(); + + virtual void setRect(const QRectF &rect); + virtual void setColor(const QColor &color); + virtual void setPenColor(const QColor &color); + virtual void setPenWidth(qreal width); + virtual void setGradientStops(const QGradientStops &stops); + virtual void setRadius(qreal radius); + virtual void setAligned(bool aligned); + virtual void update(); + +private: + enum { + TypeFlat, + TypeVertexGradient + }; + QSGGeometryNode *border(); + + void updateGeometry(); + void updateGradientTexture(); + + QSGGeometryNode *m_border; + QSGFlatColorMaterial m_border_material; + QSGFlatColorMaterial m_fill_material; + + QRectF m_rect; + QGradientStops m_gradient_stops; + qreal m_radius; + qreal m_pen_width; + + uint m_aligned : 1; + uint m_gradient_is_opaque : 1; + uint m_dirty_geometry : 1; + + uint m_material_type : 2; // Only goes up to 3 + + QSGGeometry m_default_geometry; + + QSGContext *m_context; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp new file mode 100644 index 0000000000..d3b90bed60 --- /dev/null +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdistancefieldglyphnode_p.h" +#include "qsgdistancefieldglyphnode_p_p.h" +#include <QtQuick/private/qsgdistancefieldutil_p.h> +#include <QtQuick/private/qsgcontext_p.h> + +QT_BEGIN_NAMESPACE + +QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGDistanceFieldGlyphCacheManager *cacheManager) + : m_material(0) + , m_glyph_cacheManager(cacheManager) + , m_glyph_cache(0) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) + , m_style(QQuickText::Normal) + , m_antialiasingMode(GrayAntialiasing) + , m_dirtyGeometry(false) + , m_dirtyMaterial(false) +{ + m_geometry.setDrawingMode(GL_TRIANGLES); + setGeometry(&m_geometry); + setPreferredAntialiasingMode(cacheManager->defaultAntialiasingMode()); +#ifdef QML_RUNTIME_TESTING + description = QLatin1String("glyphs"); +#endif +} + +QSGDistanceFieldGlyphNode::~QSGDistanceFieldGlyphNode() +{ + delete m_material; + if (m_glyph_cache) { + m_glyph_cache->release(m_glyphs.glyphIndexes()); + m_glyph_cache->unregisterGlyphNode(this); + } +} + +void QSGDistanceFieldGlyphNode::setColor(const QColor &color) +{ + m_color = color; + if (m_material != 0) { + m_material->setColor(color); + markDirty(DirtyMaterial); + } +} + +void QSGDistanceFieldGlyphNode::setPreferredAntialiasingMode(AntialiasingMode mode) +{ + if (mode == m_antialiasingMode) + return; + m_antialiasingMode = mode; + m_dirtyMaterial = true; +} + +void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) +{ + QRawFont font = glyphs.rawFont(); + m_position = QPointF(position.x(), position.y() - font.ascent()); + m_glyphs = glyphs; + + QSGDistanceFieldGlyphCache *oldCache = m_glyph_cache; + m_glyph_cache = m_glyph_cacheManager->cache(m_glyphs.rawFont()); + if (m_glyph_cache != oldCache) { + if (oldCache) + oldCache->unregisterGlyphNode(this); + m_glyph_cache->registerGlyphNode(this); + } + m_glyph_cache->populate(glyphs.glyphIndexes()); + + const QVector<quint32> &glyphIndexes = m_glyphs.glyphIndexes(); + const QVector<QPointF> &glyphPositions = m_glyphs.positions(); + for (int i = 0; i < glyphIndexes.size(); ++i) { + GlyphInfo g; + g.glyphIndex = glyphIndexes.at(i); + g.position = glyphPositions.at(i); + m_glyphsToAdd.append(g); + } + + m_dirtyGeometry = true; + m_dirtyMaterial = true; +} + +void QSGDistanceFieldGlyphNode::setStyle(QQuickText::TextStyle style) +{ + if (m_style == style) + return; + m_style = style; + m_dirtyMaterial = true; +} + +void QSGDistanceFieldGlyphNode::setStyleColor(const QColor &color) +{ + if (m_styleColor == color) + return; + m_styleColor = color; + m_dirtyMaterial = true; +} + +void QSGDistanceFieldGlyphNode::update() +{ + if (m_dirtyMaterial) + updateMaterial(); + if (m_dirtyGeometry) + updateGeometry(); +} + +void QSGDistanceFieldGlyphNode::updateGeometry() +{ + Q_ASSERT(m_glyph_cache); + + if (m_glyphsToAdd.isEmpty()) + return; + + QSGGeometry *g = geometry(); + + Q_ASSERT(g->indexType() == GL_UNSIGNED_SHORT); + + int oldVertexCount = g->vertexCount(); + int oldIndexCount = g->indexCount(); + + QVector<QSGGeometry::TexturedPoint2D> vp; + vp.reserve(m_glyphsToAdd.size() * 4); + QVector<ushort> ip; + ip.reserve(m_glyphsToAdd.size() * 6); + + QPointF margins(2, 2); + QPointF texMargins = margins / m_glyph_cache->fontScale(); + + const QSGDistanceFieldGlyphCache::Texture *textureToUse = 0; + + QLinkedList<GlyphInfo>::iterator it = m_glyphsToAdd.begin(); + while (it != m_glyphsToAdd.end()) { + quint32 glyphIndex = it->glyphIndex; + QSGDistanceFieldGlyphCache::TexCoord c = m_glyph_cache->glyphTexCoord(glyphIndex); + + if (c.isNull()) { + if (!c.isValid()) + ++it; + else + it = m_glyphsToAdd.erase(it); + continue; + } + + const QSGDistanceFieldGlyphCache::Texture *texture = m_glyph_cache->glyphTexture(glyphIndex); + if (!texture->textureId) { + ++it; + continue; + } + + QSGDistanceFieldGlyphCache::Metrics metrics = m_glyph_cache->glyphMetrics(glyphIndex); + + if (!textureToUse) + textureToUse = texture; + + metrics.width += margins.x() * 2; + metrics.height += margins.y() * 2; + metrics.baselineX -= margins.x(); + metrics.baselineY += margins.y(); + c.xMargin -= texMargins.x(); + c.yMargin -= texMargins.y(); + c.width += texMargins.x() * 2; + c.height += texMargins.y() * 2; + + const QPointF &glyphPosition = it->position; + qreal x = glyphPosition.x() + metrics.baselineX + m_position.x(); + qreal y = glyphPosition.y() - metrics.baselineY + m_position.y(); + + m_boundingRect |= QRectF(x, y, metrics.width, metrics.height); + + float cx1 = x; + float cx2 = x + metrics.width; + float cy1 = y; + float cy2 = y + metrics.height; + + float tx1 = c.x + c.xMargin; + float tx2 = tx1 + c.width; + float ty1 = c.y + c.yMargin; + float ty2 = ty1 + c.height; + + if (m_baseLine.isNull()) + m_baseLine = glyphPosition; + + int i = vp.size(); + + QSGGeometry::TexturedPoint2D v1; + v1.set(cx1, cy1, tx1, ty1); + QSGGeometry::TexturedPoint2D v2; + v2.set(cx2, cy1, tx2, ty1); + QSGGeometry::TexturedPoint2D v3; + v3.set(cx1, cy2, tx1, ty2); + QSGGeometry::TexturedPoint2D v4; + v4.set(cx2, cy2, tx2, ty2); + vp.append(v1); + vp.append(v2); + vp.append(v3); + vp.append(v4); + + int o = i + oldVertexCount; + ip.append(o + 0); + ip.append(o + 2); + ip.append(o + 3); + ip.append(o + 3); + ip.append(o + 1); + ip.append(o + 0); + + it = m_glyphsToAdd.erase(it); + } + + if (vp.isEmpty()) + return; + + void *data = 0; + if (oldVertexCount && oldIndexCount) { + int byteSize = oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D) + + oldIndexCount * sizeof(quint16); + data = qMalloc(byteSize); + memcpy(data, g->vertexData(), byteSize); + } + + g->allocate(oldVertexCount + vp.size(), oldIndexCount + ip.size()); + + if (data) { + memcpy(g->vertexData(), data, oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D)); + memcpy(g->indexData(), ((char *) data) + oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D), + oldIndexCount * sizeof(quint16)); + qFree(data); + } + + memcpy(g->vertexDataAsTexturedPoint2D() + oldVertexCount, vp.constData(), vp.size() * sizeof(QSGGeometry::TexturedPoint2D)); + memcpy(g->indexDataAsUShort() + oldIndexCount, ip.constData(), ip.size() * sizeof(quint16)); + + setBoundingRect(m_boundingRect); + markDirty(DirtyGeometry); + m_dirtyGeometry = false; + + m_material->setTexture(textureToUse); +} + +void QSGDistanceFieldGlyphNode::updateMaterial() +{ + delete m_material; + + if (m_style == QQuickText::Normal) { + switch (m_antialiasingMode) { + case HighQualitySubPixelAntialiasing: + m_material = new QSGHiQSubPixelDistanceFieldTextMaterial; + break; + case LowQualitySubPixelAntialiasing: + m_material = new QSGLoQSubPixelDistanceFieldTextMaterial; + break; + case GrayAntialiasing: + default: + m_material = new QSGDistanceFieldTextMaterial; + break; + } + } else { + QSGDistanceFieldStyledTextMaterial *material; + if (m_style == QQuickText::Outline) { + material = new QSGDistanceFieldOutlineTextMaterial; + } else { + QSGDistanceFieldShiftedStyleTextMaterial *sMaterial = new QSGDistanceFieldShiftedStyleTextMaterial; + if (m_style == QQuickText::Raised) + sMaterial->setShift(QPointF(0.0, 1.0)); + else + sMaterial->setShift(QPointF(0.0, -1.0)); + material = sMaterial; + } + material->setStyleColor(m_styleColor); + m_material = material; + } + + m_material->setGlyphCache(m_glyph_cache); + m_material->setColor(m_color); + setMaterial(m_material); + m_dirtyMaterial = false; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp new file mode 100644 index 0000000000..3852b01518 --- /dev/null +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp @@ -0,0 +1,725 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdistancefieldglyphnode_p_p.h" +#include <QtQuick/private/qsgdistancefieldutil_p.h> +#include <QtQuick/private/qsgtexture_p.h> +#include <QtGui/qopenglfunctions.h> +#include <qmath.h> + +QT_BEGIN_NAMESPACE + +class QSGDistanceFieldTextMaterialShader : public QSGMaterialShader +{ +public: + QSGDistanceFieldTextMaterialShader(); + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual char const *const *attributeNames() const; + +protected: + virtual void initialize(); + virtual const char *vertexShader() const; + virtual const char *fragmentShader() const; + + void updateAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc); + + float m_fontScale; + float m_matrixScale; + + int m_matrix_id; + int m_textureScale_id; + int m_alphaMin_id; + int m_alphaMax_id; + int m_color_id; +}; + +const char *QSGDistanceFieldTextMaterialShader::vertexShader() const { + return + "uniform highp mat4 matrix; \n" + "uniform highp vec2 textureScale; \n" + "attribute highp vec4 vCoord; \n" + "attribute highp vec2 tCoord; \n" + "varying highp vec2 sampleCoord; \n" + "void main() { \n" + " sampleCoord = tCoord * textureScale; \n" + " gl_Position = matrix * vCoord; \n" + "}"; +} + +const char *QSGDistanceFieldTextMaterialShader::fragmentShader() const { + return + "varying highp vec2 sampleCoord; \n" + "uniform sampler2D texture; \n" + "uniform lowp vec4 color; \n" + "uniform highp float alphaMin; \n" + "uniform highp float alphaMax; \n" + "void main() { \n" + " gl_FragColor = color * smoothstep(alphaMin, \n" + " alphaMax, \n" + " texture2D(texture, sampleCoord).a); \n" + "}"; +} + +char const *const *QSGDistanceFieldTextMaterialShader::attributeNames() const { + static char const *const attr[] = { "vCoord", "tCoord", 0 }; + return attr; +} + +QSGDistanceFieldTextMaterialShader::QSGDistanceFieldTextMaterialShader() + : m_fontScale(1.0) + , m_matrixScale(1.0) +{ +} + +void QSGDistanceFieldTextMaterialShader::updateAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc) +{ + float combinedScale = m_fontScale * m_matrixScale; + float base = thresholdFunc(combinedScale); + float range = spreadFunc(combinedScale); + + float alphaMin = qMax(0.0f, base - range); + float alphaMax = qMin(base + range, 1.0f); + program()->setUniformValue(m_alphaMin_id, GLfloat(alphaMin)); + program()->setUniformValue(m_alphaMax_id, GLfloat(alphaMax)); +} + +void QSGDistanceFieldTextMaterialShader::initialize() +{ + QSGMaterialShader::initialize(); + m_matrix_id = program()->uniformLocation("matrix"); + m_textureScale_id = program()->uniformLocation("textureScale"); + m_color_id = program()->uniformLocation("color"); + m_alphaMin_id = program()->uniformLocation("alphaMin"); + m_alphaMax_id = program()->uniformLocation("alphaMax"); +} + +void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + QSGDistanceFieldTextMaterial *material = static_cast<QSGDistanceFieldTextMaterial *>(newEffect); + QSGDistanceFieldTextMaterial *oldMaterial = static_cast<QSGDistanceFieldTextMaterial *>(oldEffect); + + bool updated = material->updateCache(); + + if (oldMaterial == 0 + || material->color() != oldMaterial->color() + || state.isOpacityDirty()) { + QVector4D color(material->color().redF(), material->color().greenF(), + material->color().blueF(), material->color().alphaF()); + color *= state.opacity(); + program()->setUniformValue(m_color_id, color); + } + + bool updateRange = false; + if (oldMaterial == 0 + || material->glyphCache()->fontScale() != oldMaterial->glyphCache()->fontScale()) { + m_fontScale = material->glyphCache()->fontScale(); + updateRange = true; + } + if (state.isMatrixDirty()) { + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); + m_matrixScale = qSqrt(qAbs(state.modelViewMatrix().determinant())); + updateRange = true; + } + if (updateRange) { + updateAlphaRange(material->glyphCache()->manager()->thresholdFunc(), + material->glyphCache()->manager()->antialiasingSpreadFunc()); + } + + Q_ASSERT(material->glyphCache()); + + if (updated + || oldMaterial == 0 + || oldMaterial->texture()->textureId != material->texture()->textureId) { + program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->textureSize().width(), + 1.0 / material->textureSize().height())); + glBindTexture(GL_TEXTURE_2D, material->texture()->textureId); + + if (updated) { + // Set the mag/min filters to be linear. We only need to do this when the texture + // has been recreated. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + } +} + +QSGDistanceFieldTextMaterial::QSGDistanceFieldTextMaterial() + : m_glyph_cache(0) + , m_texture(0) +{ + setFlag(Blending, true); +} + +QSGDistanceFieldTextMaterial::~QSGDistanceFieldTextMaterial() +{ +} + +QSGMaterialType *QSGDistanceFieldTextMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +QSGMaterialShader *QSGDistanceFieldTextMaterial::createShader() const +{ + return new QSGDistanceFieldTextMaterialShader; +} + +bool QSGDistanceFieldTextMaterial::updateCache() +{ + m_glyph_cache->update(); + if (!m_texture) + m_texture = m_glyph_cache->glyphTexture(-1); // invalid texture + QSize glyphCacheSize = m_texture->size; + if (glyphCacheSize != m_size) { + m_size = glyphCacheSize; + + return true; + } else { + return false; + } +} + +int QSGDistanceFieldTextMaterial::compare(const QSGMaterial *o) const +{ + Q_ASSERT(o && type() == o->type()); + const QSGDistanceFieldTextMaterial *other = static_cast<const QSGDistanceFieldTextMaterial *>(o); + if (m_glyph_cache != other->m_glyph_cache) + return m_glyph_cache - other->m_glyph_cache; + if (m_glyph_cache->fontScale() != other->m_glyph_cache->fontScale()) { + qreal s1 = m_glyph_cache->fontScale(); + qreal s2 = other->m_glyph_cache->fontScale(); + return int(s2 < s1) - int(s1 < s2); + } + QRgb c1 = m_color.rgba(); + QRgb c2 = other->m_color.rgba(); + return int(c2 < c1) - int(c1 < c2); +} + + +class DistanceFieldStyledTextMaterialShader : public QSGDistanceFieldTextMaterialShader +{ +public: + DistanceFieldStyledTextMaterialShader(); + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + +protected: + virtual void initialize(); + virtual const char *fragmentShader() const = 0; + + int m_styleColor_id; +}; + +DistanceFieldStyledTextMaterialShader::DistanceFieldStyledTextMaterialShader() + : QSGDistanceFieldTextMaterialShader() +{ +} + +void DistanceFieldStyledTextMaterialShader::initialize() +{ + QSGDistanceFieldTextMaterialShader::initialize(); + m_styleColor_id = program()->uniformLocation("styleColor"); +} + +void DistanceFieldStyledTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + QSGDistanceFieldTextMaterialShader::updateState(state, newEffect, oldEffect); + + QSGDistanceFieldStyledTextMaterial *material = static_cast<QSGDistanceFieldStyledTextMaterial *>(newEffect); + QSGDistanceFieldStyledTextMaterial *oldMaterial = static_cast<QSGDistanceFieldStyledTextMaterial *>(oldEffect); + + if (oldMaterial == 0 + || material->styleColor() != oldMaterial->styleColor() + || (state.isOpacityDirty())) { + QVector4D color(material->styleColor().redF(), material->styleColor().greenF(), + material->styleColor().blueF(), material->styleColor().alphaF()); + color *= state.opacity(); + program()->setUniformValue(m_styleColor_id, color); + } +} + +QSGDistanceFieldStyledTextMaterial::QSGDistanceFieldStyledTextMaterial() + : QSGDistanceFieldTextMaterial() +{ +} + +QSGDistanceFieldStyledTextMaterial::~QSGDistanceFieldStyledTextMaterial() +{ +} + +int QSGDistanceFieldStyledTextMaterial::compare(const QSGMaterial *o) const +{ + Q_ASSERT(o && type() == o->type()); + const QSGDistanceFieldStyledTextMaterial *other = static_cast<const QSGDistanceFieldStyledTextMaterial *>(o); + if (m_styleColor != other->m_styleColor) { + QRgb c1 = m_styleColor.rgba(); + QRgb c2 = other->m_styleColor.rgba(); + return int(c2 < c1) - int(c1 < c2); + } + return QSGDistanceFieldTextMaterial::compare(o); +} + + +class DistanceFieldOutlineTextMaterialShader : public DistanceFieldStyledTextMaterialShader +{ +public: + DistanceFieldOutlineTextMaterialShader(); + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + +protected: + virtual void initialize(); + virtual const char *fragmentShader() const; + + void updateOutlineAlphaRange(int dfRadius); + + int m_outlineAlphaMax0_id; + int m_outlineAlphaMax1_id; +}; + +const char *DistanceFieldOutlineTextMaterialShader::fragmentShader() const { + return + "varying highp vec2 sampleCoord; \n" + "uniform sampler2D texture; \n" + "uniform lowp vec4 color; \n" + "uniform lowp vec4 styleColor; \n" + "uniform highp float alphaMin; \n" + "uniform highp float alphaMax; \n" + "uniform highp float outlineAlphaMax0; \n" + "uniform highp float outlineAlphaMax1; \n" + "void main() { \n" + " mediump float d = texture2D(texture, sampleCoord).a; \n" + " gl_FragColor = mix(styleColor, color, smoothstep(alphaMin, alphaMax, d)) \n" + " * smoothstep(outlineAlphaMax0, outlineAlphaMax1, d); \n" + "}"; +} + +DistanceFieldOutlineTextMaterialShader::DistanceFieldOutlineTextMaterialShader() + : DistanceFieldStyledTextMaterialShader() +{ +} + +void DistanceFieldOutlineTextMaterialShader::initialize() +{ + DistanceFieldStyledTextMaterialShader::initialize(); + m_outlineAlphaMax0_id = program()->uniformLocation("outlineAlphaMax0"); + m_outlineAlphaMax1_id = program()->uniformLocation("outlineAlphaMax1"); +} + +void DistanceFieldOutlineTextMaterialShader::updateOutlineAlphaRange(int dfRadius) +{ + qreal outlineLimit = qMax(qreal(0.2), qreal(0.5 - 0.5 / dfRadius / m_fontScale)); + + qreal combinedScale = m_fontScale * m_matrixScale; + qreal alphaMin = qMax(0.0, 0.5 - 0.07 / combinedScale); + qreal styleAlphaMin0 = qMax(0.0, outlineLimit - 0.07 / combinedScale); + qreal styleAlphaMin1 = qMin(qreal(outlineLimit + 0.07 / combinedScale), alphaMin); + program()->setUniformValue(m_outlineAlphaMax0_id, GLfloat(styleAlphaMin0)); + program()->setUniformValue(m_outlineAlphaMax1_id, GLfloat(styleAlphaMin1)); +} + +void DistanceFieldOutlineTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + DistanceFieldStyledTextMaterialShader::updateState(state, newEffect, oldEffect); + + QSGDistanceFieldOutlineTextMaterial *material = static_cast<QSGDistanceFieldOutlineTextMaterial *>(newEffect); + QSGDistanceFieldOutlineTextMaterial *oldMaterial = static_cast<QSGDistanceFieldOutlineTextMaterial *>(oldEffect); + + if (oldMaterial == 0 + || material->glyphCache()->fontScale() != oldMaterial->glyphCache()->fontScale() + || state.isMatrixDirty()) + updateOutlineAlphaRange(material->glyphCache()->distanceFieldRadius()); +} + + +QSGDistanceFieldOutlineTextMaterial::QSGDistanceFieldOutlineTextMaterial() + : QSGDistanceFieldStyledTextMaterial() +{ +} + +QSGDistanceFieldOutlineTextMaterial::~QSGDistanceFieldOutlineTextMaterial() +{ +} + +QSGMaterialType *QSGDistanceFieldOutlineTextMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +QSGMaterialShader *QSGDistanceFieldOutlineTextMaterial::createShader() const +{ + return new DistanceFieldOutlineTextMaterialShader; +} + + +class DistanceFieldShiftedStyleTextMaterialShader : public DistanceFieldStyledTextMaterialShader +{ +public: + DistanceFieldShiftedStyleTextMaterialShader(); + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + +protected: + virtual void initialize(); + virtual const char *vertexShader() const; + virtual const char *fragmentShader() const; + + void updateShift(const QSGDistanceFieldGlyphCache *cache, const QPointF& shift); + + int m_shift_id; +}; + +DistanceFieldShiftedStyleTextMaterialShader::DistanceFieldShiftedStyleTextMaterialShader() + : DistanceFieldStyledTextMaterialShader() +{ +} + +void DistanceFieldShiftedStyleTextMaterialShader::initialize() +{ + DistanceFieldStyledTextMaterialShader::initialize(); + m_shift_id = program()->uniformLocation("shift"); +} + +void DistanceFieldShiftedStyleTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + DistanceFieldStyledTextMaterialShader::updateState(state, newEffect, oldEffect); + + QSGDistanceFieldShiftedStyleTextMaterial *material = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(newEffect); + QSGDistanceFieldShiftedStyleTextMaterial *oldMaterial = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(oldEffect); + + if (oldMaterial == 0 + || oldMaterial->glyphCache()->fontScale() != material->glyphCache()->fontScale() + || oldMaterial->shift() != material->shift() + || oldMaterial->textureSize() != material->textureSize()) { + updateShift(material->glyphCache(), material->shift()); + } +} + +void DistanceFieldShiftedStyleTextMaterialShader::updateShift(const QSGDistanceFieldGlyphCache *cache, const QPointF &shift) +{ + QPointF texel(1.0 / cache->fontScale() * shift.x(), + 1.0 / cache->fontScale() * shift.y()); + program()->setUniformValue(m_shift_id, texel); +} + +const char *DistanceFieldShiftedStyleTextMaterialShader::vertexShader() const +{ + return + "uniform highp mat4 matrix; \n" + "uniform highp vec2 textureScale; \n" + "attribute highp vec4 vCoord; \n" + "attribute highp vec2 tCoord; \n" + "uniform highp vec2 shift; \n" + "varying highp vec2 sampleCoord; \n" + "varying highp vec2 shiftedSampleCoord; \n" + "void main() { \n" + " sampleCoord = tCoord * textureScale; \n" + " shiftedSampleCoord = (tCoord - shift) * textureScale; \n" + " gl_Position = matrix * vCoord; \n" + "}"; +} + +const char *DistanceFieldShiftedStyleTextMaterialShader::fragmentShader() const { + return + "varying highp vec2 sampleCoord; \n" + "varying highp vec2 shiftedSampleCoord; \n" + "uniform sampler2D texture; \n" + "uniform lowp vec4 color; \n" + "uniform lowp vec4 styleColor; \n" + "uniform highp float alphaMin; \n" + "uniform highp float alphaMax; \n" + "void main() { \n" + " highp float a = smoothstep(alphaMin, alphaMax, texture2D(texture, sampleCoord).a);\n" + " highp vec4 shifted = styleColor * smoothstep(alphaMin, \n" + " alphaMax, \n" + " texture2D(texture, shiftedSampleCoord).a); \n" + " gl_FragColor = mix(shifted, color, a); \n" + "}"; +} + +QSGDistanceFieldShiftedStyleTextMaterial::QSGDistanceFieldShiftedStyleTextMaterial() + : QSGDistanceFieldStyledTextMaterial() +{ +} + +QSGDistanceFieldShiftedStyleTextMaterial::~QSGDistanceFieldShiftedStyleTextMaterial() +{ +} + +QSGMaterialType *QSGDistanceFieldShiftedStyleTextMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +QSGMaterialShader *QSGDistanceFieldShiftedStyleTextMaterial::createShader() const +{ + return new DistanceFieldShiftedStyleTextMaterialShader; +} + + +class QSGHiQSubPixelDistanceFieldTextMaterialShader : public QSGDistanceFieldTextMaterialShader +{ +public: + virtual void initialize(); + virtual void activate(); + virtual void deactivate(); + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + +protected: + virtual const char *vertexShader() const; + virtual const char *fragmentShader() const; + +private: + int m_fontScale_id; + int m_vecDelta_id; +}; + +const char *QSGHiQSubPixelDistanceFieldTextMaterialShader::vertexShader() const { + return + "uniform highp mat4 matrix; \n" + "uniform highp vec2 textureScale; \n" + "uniform highp float fontScale; \n" + "uniform highp vec4 vecDelta; \n" + "attribute highp vec4 vCoord; \n" + "attribute highp vec2 tCoord; \n" + "varying highp vec2 sampleCoord; \n" + "varying highp vec3 sampleFarLeft; \n" + "varying highp vec3 sampleNearLeft; \n" + "varying highp vec3 sampleNearRight; \n" + "varying highp vec3 sampleFarRight; \n" + "void main() { \n" + " sampleCoord = tCoord * textureScale; \n" + " gl_Position = matrix * vCoord; \n" + // Calculate neighbour pixel position in item space. + " highp vec3 wDelta = gl_Position.w * vecDelta.xyw; \n" + " highp vec3 farLeft = vCoord.xyw - 0.667 * wDelta; \n" + " highp vec3 nearLeft = vCoord.xyw - 0.333 * wDelta; \n" + " highp vec3 nearRight = vCoord.xyw + 0.333 * wDelta; \n" + " highp vec3 farRight = vCoord.xyw + 0.667 * wDelta; \n" + // Calculate neighbour texture coordinate. + " highp vec2 scale = textureScale / fontScale; \n" + " highp vec2 base = sampleCoord - scale * vCoord.xy; \n" + " sampleFarLeft = vec3(base * farLeft.z + scale * farLeft.xy, farLeft.z); \n" + " sampleNearLeft = vec3(base * nearLeft.z + scale * nearLeft.xy, nearLeft.z); \n" + " sampleNearRight = vec3(base * nearRight.z + scale * nearRight.xy, nearRight.z); \n" + " sampleFarRight = vec3(base * farRight.z + scale * farRight.xy, farRight.z); \n" + "}"; +} + +const char *QSGHiQSubPixelDistanceFieldTextMaterialShader::fragmentShader() const { + return + "varying highp vec2 sampleCoord; \n" + "varying highp vec3 sampleFarLeft; \n" + "varying highp vec3 sampleNearLeft; \n" + "varying highp vec3 sampleNearRight; \n" + "varying highp vec3 sampleFarRight; \n" + "uniform sampler2D texture; \n" + "uniform lowp vec4 color; \n" + "uniform highp float alphaMin; \n" + "uniform highp float alphaMax; \n" + "void main() { \n" + " highp vec4 n; \n" + " n.x = texture2DProj(texture, sampleFarLeft).a; \n" + " n.y = texture2DProj(texture, sampleNearLeft).a; \n" + " highp float c = texture2D(texture, sampleCoord).a; \n" + " n.z = texture2DProj(texture, sampleNearRight).a; \n" + " n.w = texture2DProj(texture, sampleFarRight).a; \n" +#if 0 + // Blurrier, faster. + " n = smoothstep(alphaMin, alphaMax, n); \n" + " c = smoothstep(alphaMin, alphaMax, c); \n" +#else + // Sharper, slower. + " highp vec2 d = min(abs(n.yw - n.xz) * 2., 0.67); \n" + " highp vec2 lo = mix(vec2(alphaMin), vec2(0.5), d); \n" + " highp vec2 hi = mix(vec2(alphaMax), vec2(0.5), d); \n" + " n = smoothstep(lo.xxyy, hi.xxyy, n); \n" + " c = smoothstep(lo.x + lo.y, hi.x + hi.y, 2. * c); \n" +#endif + " gl_FragColor = vec4(0.333 * (n.xyz + n.yzw + c), c) * color.w; \n" + "}"; +} + +//const char *QSGHiQSubPixelDistanceFieldTextMaterialShader::fragmentShader() const { +// return +// "#extension GL_OES_standard_derivatives: enable \n" +// "varying highp vec2 sampleCoord; \n" +// "uniform sampler2D texture; \n" +// "uniform lowp vec4 color; \n" +// "uniform highp float alphaMin; \n" +// "uniform highp float alphaMax; \n" +// "void main() { \n" +// " highp vec2 delta = dFdx(sampleCoord); \n" +// " highp vec4 n; \n" +// " n.x = texture2D(texture, sampleCoord - 0.667 * delta).a; \n" +// " n.y = texture2D(texture, sampleCoord - 0.333 * delta).a; \n" +// " highp float c = texture2D(texture, sampleCoord).a; \n" +// " n.z = texture2D(texture, sampleCoord + 0.333 * delta).a; \n" +// " n.w = texture2D(texture, sampleCoord + 0.667 * delta).a; \n" +// " n = smoothstep(alphaMin, alphaMax, n); \n" +// " c = smoothstep(alphaMin, alphaMax, c); \n" +// " gl_FragColor = vec4(0.333 * (n.xyz + n.yzw + c), c) * color.w; \n" +// "}"; +//} + +void QSGHiQSubPixelDistanceFieldTextMaterialShader::initialize() +{ + QSGDistanceFieldTextMaterialShader::initialize(); + m_fontScale_id = program()->uniformLocation("fontScale"); + m_vecDelta_id = program()->uniformLocation("vecDelta"); +} + +void QSGHiQSubPixelDistanceFieldTextMaterialShader::activate() +{ + QSGDistanceFieldTextMaterialShader::activate(); + glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR); +} + +void QSGHiQSubPixelDistanceFieldTextMaterialShader::deactivate() +{ + QSGDistanceFieldTextMaterialShader::deactivate(); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); +} + +void QSGHiQSubPixelDistanceFieldTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + QSGDistanceFieldTextMaterial *material = static_cast<QSGDistanceFieldTextMaterial *>(newEffect); + QSGDistanceFieldTextMaterial *oldMaterial = static_cast<QSGDistanceFieldTextMaterial *>(oldEffect); + + if (oldMaterial == 0 || material->color() != oldMaterial->color()) { + QColor c = material->color(); + state.context()->functions()->glBlendColor(c.redF(), c.greenF(), c.blueF(), 1.0f); + } + + if (oldMaterial == 0 || material->glyphCache()->fontScale() != oldMaterial->glyphCache()->fontScale()) + program()->setUniformValue(m_fontScale_id, GLfloat(material->glyphCache()->fontScale())); + + if (oldMaterial == 0 || state.isMatrixDirty()) { + int viewportWidth = state.viewportRect().width(); + QMatrix4x4 mat = state.combinedMatrix().inverted(); + program()->setUniformValue(m_vecDelta_id, mat.column(0) * (qreal(2) / viewportWidth)); + } + + QSGDistanceFieldTextMaterialShader::updateState(state, newEffect, oldEffect); +} + +QSGMaterialType *QSGHiQSubPixelDistanceFieldTextMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +QSGMaterialShader *QSGHiQSubPixelDistanceFieldTextMaterial::createShader() const +{ + return new QSGHiQSubPixelDistanceFieldTextMaterialShader; +} + + +class QSGLoQSubPixelDistanceFieldTextMaterialShader : public QSGHiQSubPixelDistanceFieldTextMaterialShader +{ +protected: + virtual const char *vertexShader() const; + virtual const char *fragmentShader() const; +}; + +const char *QSGLoQSubPixelDistanceFieldTextMaterialShader::vertexShader() const { + return + "uniform highp mat4 matrix; \n" + "uniform highp vec2 textureScale; \n" + "uniform highp float fontScale; \n" + "uniform highp vec4 vecDelta; \n" + "attribute highp vec4 vCoord; \n" + "attribute highp vec2 tCoord; \n" + "varying highp vec3 sampleNearLeft; \n" + "varying highp vec3 sampleNearRight; \n" + "void main() { \n" + " highp vec2 sampleCoord = tCoord * textureScale; \n" + " gl_Position = matrix * vCoord; \n" + // Calculate neighbour pixel position in item space. + " highp vec3 wDelta = gl_Position.w * vecDelta.xyw; \n" + " highp vec3 nearLeft = vCoord.xyw - 0.25 * wDelta; \n" + " highp vec3 nearRight = vCoord.xyw + 0.25 * wDelta; \n" + // Calculate neighbour texture coordinate. + " highp vec2 scale = textureScale / fontScale; \n" + " highp vec2 base = sampleCoord - scale * vCoord.xy; \n" + " sampleNearLeft = vec3(base * nearLeft.z + scale * nearLeft.xy, nearLeft.z); \n" + " sampleNearRight = vec3(base * nearRight.z + scale * nearRight.xy, nearRight.z); \n" + "}"; +} + +const char *QSGLoQSubPixelDistanceFieldTextMaterialShader::fragmentShader() const { + return + "varying highp vec3 sampleNearLeft; \n" + "varying highp vec3 sampleNearRight; \n" + "uniform sampler2D texture; \n" + "uniform lowp vec4 color; \n" + "uniform highp float alphaMin; \n" + "uniform highp float alphaMax; \n" + "void main() { \n" + " highp vec2 n; \n" + " n.x = texture2DProj(texture, sampleNearLeft).a; \n" + " n.y = texture2DProj(texture, sampleNearRight).a; \n" + " n = smoothstep(alphaMin, alphaMax, n); \n" + " highp float c = 0.5 * (n.x + n.y); \n" + " gl_FragColor = vec4(n.x, c, n.y, c) * color.w; \n" + "}"; +} + +QSGMaterialType *QSGLoQSubPixelDistanceFieldTextMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +QSGMaterialShader *QSGLoQSubPixelDistanceFieldTextMaterial::createShader() const +{ + return new QSGLoQSubPixelDistanceFieldTextMaterialShader; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h new file mode 100644 index 0000000000..8096d4ae70 --- /dev/null +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DISTANCEFIELD_GLYPHNODE_H +#define DISTANCEFIELD_GLYPHNODE_H + +#include <private/qsgadaptationlayer_p.h> +#include <QtQuick/qsgtexture.h> + +#include <QtQuick/private/qquicktext_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGDistanceFieldGlyphCacheManager; +class QSGDistanceFieldTextMaterial; +class QSGDistanceFieldGlyphNode: public QSGGlyphNode +{ +public: + QSGDistanceFieldGlyphNode(QSGDistanceFieldGlyphCacheManager *cacheManager); + ~QSGDistanceFieldGlyphNode(); + + virtual QPointF baseLine() const { return m_baseLine; } + virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs); + virtual void setColor(const QColor &color); + + virtual void setPreferredAntialiasingMode(AntialiasingMode mode); + + virtual void setStyle(QQuickText::TextStyle style); + virtual void setStyleColor(const QColor &color); + + virtual void update(); + + void updateGeometry(); + +private: + void updateMaterial(); + + QColor m_color; + QPointF m_baseLine; + QSGDistanceFieldTextMaterial *m_material; + QPointF m_position; + QGlyphRun m_glyphs; + QSGDistanceFieldGlyphCacheManager *m_glyph_cacheManager; + QSGDistanceFieldGlyphCache *m_glyph_cache; + QSGGeometry m_geometry; + QQuickText::TextStyle m_style; + QColor m_styleColor; + AntialiasingMode m_antialiasingMode; + QRectF m_boundingRect; + + struct GlyphInfo { + quint32 glyphIndex; + QPointF position; + }; + QLinkedList<GlyphInfo> m_glyphsToAdd; + + uint m_dirtyGeometry: 1; + uint m_dirtyMaterial: 1; +}; + +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // DISTANCEFIELD_GLYPHNODE_H diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h new file mode 100644 index 0000000000..dbdc6e2498 --- /dev/null +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DISTANCEFIELDTEXTMATERIAL_H +#define DISTANCEFIELDTEXTMATERIAL_H + +#include <QtQuick/qsgmaterial.h> +#include "qsgdistancefieldglyphnode_p.h" +#include "qsgadaptationlayer_p.h" + +QT_BEGIN_NAMESPACE + +class QSGDistanceFieldTextMaterial: public QSGMaterial +{ +public: + QSGDistanceFieldTextMaterial(); + ~QSGDistanceFieldTextMaterial(); + + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; + virtual int compare(const QSGMaterial *other) const; + + void setColor(const QColor &color) { m_color = color; } + const QColor &color() const { return m_color; } + + void setGlyphCache(QSGDistanceFieldGlyphCache *a) { m_glyph_cache = a; } + QSGDistanceFieldGlyphCache *glyphCache() const { return m_glyph_cache; } + + void setTexture(const QSGDistanceFieldGlyphCache::Texture * tex) { m_texture = tex; } + const QSGDistanceFieldGlyphCache::Texture * texture() const { return m_texture; } + + QSize textureSize() const { return m_size; } + + bool updateCache(); + +protected: + QSize m_size; + QColor m_color; + QSGDistanceFieldGlyphCache *m_glyph_cache; + const QSGDistanceFieldGlyphCache::Texture *m_texture; +}; + +class QSGDistanceFieldStyledTextMaterial : public QSGDistanceFieldTextMaterial +{ +public: + QSGDistanceFieldStyledTextMaterial(); + ~QSGDistanceFieldStyledTextMaterial(); + + virtual QSGMaterialType *type() const = 0; + virtual QSGMaterialShader *createShader() const = 0; + virtual int compare(const QSGMaterial *other) const; + + void setStyleColor(const QColor &color) { m_styleColor = color; } + const QColor &styleColor() const { return m_styleColor; } + +protected: + QColor m_styleColor; +}; + +class QSGDistanceFieldOutlineTextMaterial : public QSGDistanceFieldStyledTextMaterial +{ +public: + QSGDistanceFieldOutlineTextMaterial(); + ~QSGDistanceFieldOutlineTextMaterial(); + + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; +}; + +class QSGDistanceFieldShiftedStyleTextMaterial : public QSGDistanceFieldStyledTextMaterial +{ +public: + QSGDistanceFieldShiftedStyleTextMaterial(); + ~QSGDistanceFieldShiftedStyleTextMaterial(); + + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; + + void setShift(const QPointF &shift) { m_shift = shift; } + const QPointF &shift() const { return m_shift; } + +protected: + QPointF m_shift; +}; + +class QSGHiQSubPixelDistanceFieldTextMaterial : public QSGDistanceFieldTextMaterial +{ +public: + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; +}; + +class QSGLoQSubPixelDistanceFieldTextMaterial : public QSGDistanceFieldTextMaterial +{ +public: + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; +}; + +QT_END_NAMESPACE + +#endif // DISTANCEFIELDTEXTMATERIAL_H diff --git a/src/quick/scenegraph/qsgflashnode.cpp b/src/quick/scenegraph/qsgflashnode.cpp new file mode 100644 index 0000000000..9546e91ee1 --- /dev/null +++ b/src/quick/scenegraph/qsgflashnode.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgflashnode_p.h" + +QT_BEGIN_NAMESPACE + +QSGFlashNode::QSGFlashNode() + : m_counter(1) +{ + setFlag(UsePreprocess); + setColor(QColor(rand()%56 + 200, rand()%56 + 200, rand()%156 + 100)); // A random, mostly yellow, color +} + +void QSGFlashNode::preprocess() +{ + if (m_counter) { + --m_counter; + } else { + delete this; + } +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgflashnode_p.h b/src/quick/scenegraph/qsgflashnode_p.h new file mode 100644 index 0000000000..71ac22d648 --- /dev/null +++ b/src/quick/scenegraph/qsgflashnode_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGFLASHNODE_H +#define QSGFLASHNODE_H + +#include <QtQuick/QSGSimpleRectNode> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGFlashNode : public QSGSimpleRectNode +{ +public: + QSGFlashNode(); + + void preprocess(); + +private: + int m_counter; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGFLASHNODE_H + diff --git a/src/quick/scenegraph/qsgpathsimplifier.cpp b/src/quick/scenegraph/qsgpathsimplifier.cpp new file mode 100644 index 0000000000..9e851bf434 --- /dev/null +++ b/src/quick/scenegraph/qsgpathsimplifier.cpp @@ -0,0 +1,1673 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgpathsimplifier_p.h" + +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qglobal.h> +#include <QtCore/qpoint.h> +#include <QtCore/qalgorithms.h> + +#include <math.h> + +#include <private/qopengl_p.h> +#include <private/qrbtree_p.h> + +QT_BEGIN_NAMESPACE + +#define Q_FIXED_POINT_SCALE 256 +#define Q_TRIANGULATE_END_OF_POLYGON quint32(-1) + + +namespace { + +//============================================================================// +// QPoint // +//============================================================================// + +inline bool operator < (const QPoint &a, const QPoint &b) +{ + return a.y() < b.y() || (a.y() == b.y() && a.x() < b.x()); +} + +inline bool operator > (const QPoint &a, const QPoint &b) +{ + return b < a; +} + +inline bool operator <= (const QPoint &a, const QPoint &b) +{ + return !(a > b); +} + +inline bool operator >= (const QPoint &a, const QPoint &b) +{ + return !(a < b); +} + +inline int cross(const QPoint &u, const QPoint &v) +{ + return u.x() * v.y() - u.y() * v.x(); +} + +inline int dot(const QPoint &u, const QPoint &v) +{ + return u.x() * v.x() + u.y() * v.y(); +} + +//============================================================================// +// Fraction // +//============================================================================// + +// Fraction must be in the range [0, 1) +struct Fraction +{ + bool isValid() const { return denominator != 0; } + + // numerator and denominator must not have common denominators. + unsigned int numerator, denominator; +}; + +inline unsigned int gcd(unsigned int x, unsigned int y) +{ + while (y != 0) { + unsigned int z = y; + y = x % y; + x = z; + } + return x; +} + +// Fraction must be in the range [0, 1) +// Assume input is valid. +Fraction fraction(unsigned int n, unsigned int d) { + Fraction result; + if (n == 0) { + result.numerator = 0; + result.denominator = 1; + } else { + unsigned int g = gcd(n, d); + result.numerator = n / g; + result.denominator = d / g; + } + return result; +} + +//============================================================================// +// Rational // +//============================================================================// + +struct Rational +{ + bool isValid() const { return fraction.isValid(); } + int integer; + Fraction fraction; +}; + +//============================================================================// +// IntersectionPoint // +//============================================================================// + +struct IntersectionPoint +{ + bool isValid() const { return x.fraction.isValid() && y.fraction.isValid(); } + QPoint round() const; + bool isAccurate() const { return x.fraction.numerator == 0 && y.fraction.numerator == 0; } + + Rational x; // 8:8 signed, 32/32 + Rational y; // 8:8 signed, 32/32 +}; + +QPoint IntersectionPoint::round() const +{ + QPoint result(x.integer, y.integer); + if (2 * x.fraction.numerator >= x.fraction.denominator) + ++result.rx(); + if (2 * y.fraction.numerator >= y.fraction.denominator) + ++result.ry(); + return result; +} + +// Return positive value if 'p' is to the right of the line 'v1'->'v2', negative if left of the +// line and zero if exactly on the line. +// The returned value is the z-component of the qCross product between 'v2-v1' and 'p-v1', +// which is twice the signed area of the triangle 'p'->'v1'->'v2' (positive for CW order). +inline int pointDistanceFromLine(const QPoint &p, const QPoint &v1, const QPoint &v2) +{ + return cross(v2 - v1, p - v1); +} + +IntersectionPoint intersectionPoint(const QPoint &u1, const QPoint &u2, + const QPoint &v1, const QPoint &v2) +{ + IntersectionPoint result = {{0, {0, 0}}, {0, {0, 0}}}; + + QPoint u = u2 - u1; + QPoint v = v2 - v1; + int d1 = cross(u, v1 - u1); + int d2 = cross(u, v2 - u1); + int det = d2 - d1; + int d3 = cross(v, u1 - v1); + int d4 = d3 - det; //qCross(v, u2 - v1); + + // Check that the math is correct. + Q_ASSERT(d4 == cross(v, u2 - v1)); + + // The intersection point can be expressed as: + // v1 - v * d1/det + // v2 - v * d2/det + // u1 + u * d3/det + // u2 + u * d4/det + + // I'm only interested in lines that are crossing, so ignore parallel lines even if they overlap. + if (det == 0) + return result; + + if (det < 0) { + det = -det; + d1 = -d1; + d2 = -d2; + d3 = -d3; + d4 = -d4; + } + + // I'm only interested in lines intersecting at their interior, not at their end points. + // The lines intersect at their interior if and only if 'd1 < 0', 'd2 > 0', 'd3 < 0' and 'd4 > 0'. + if (d1 >= 0 || d2 <= 0 || d3 <= 0 || d4 >= 0) + return result; + + // Calculate the intersection point as follows: + // v1 - v * d1/det | v1 <= v2 (component-wise) + // v2 - v * d2/det | v2 < v1 (component-wise) + + // Assuming 16 bits per vector component. + if (v.x() >= 0) { + result.x.integer = v1.x() + int(qint64(-v.x()) * d1 / det); + result.x.fraction = fraction((unsigned int)(qint64(-v.x()) * d1 % det), (unsigned int)det); + } else { + result.x.integer = v2.x() + int(qint64(-v.x()) * d2 / det); + result.x.fraction = fraction((unsigned int)(qint64(-v.x()) * d2 % det), (unsigned int)det); + } + + if (v.y() >= 0) { + result.y.integer = v1.y() + int(qint64(-v.y()) * d1 / det); + result.y.fraction = fraction((unsigned int)(qint64(-v.y()) * d1 % det), (unsigned int)det); + } else { + result.y.integer = v2.y() + int(qint64(-v.y()) * d2 / det); + result.y.fraction = fraction((unsigned int)(qint64(-v.y()) * d2 % det), (unsigned int)det); + } + + Q_ASSERT(result.x.fraction.isValid()); + Q_ASSERT(result.y.fraction.isValid()); + return result; +} + +//============================================================================// +// PathSimplifier // +//============================================================================// + +class PathSimplifier +{ +public: + PathSimplifier(const QVectorPath &path, QDataBuffer<QPoint> &vertices, + QDataBuffer<quint32> &indices, const QTransform &matrix); + +private: + struct Element; + + class BoundingVolumeHierarchy + { + public: + struct Node + { + enum Type + { + Leaf, + Split + }; + Type type; + QPoint minimum; + QPoint maximum; + union { + Element *element; // type == Leaf + Node *left; // type == Split + }; + Node *right; + }; + + BoundingVolumeHierarchy(); + ~BoundingVolumeHierarchy(); + void allocate(int nodeCount); + void free(); + Node *newNode(); + + Node *root; + private: + void freeNode(Node *n); + + Node *nodeBlock; + int blockSize; + int firstFree; + }; + + struct Element + { + enum Degree + { + Line = 1, + Quadratic = 2, + Cubic = 3 + }; + + quint32 &upperIndex() { return indices[pointingUp ? degree : 0]; } + quint32 &lowerIndex() { return indices[pointingUp ? 0 : degree]; } + quint32 upperIndex() const { return indices[pointingUp ? degree : 0]; } + quint32 lowerIndex() const { return indices[pointingUp ? 0 : degree]; } + void flip(); + + QPoint middle; + quint32 indices[4]; // index to points + Element *next, *previous; // used in connectElements() + int winding; // used in connectElements() + union { + QRBTree<Element *>::Node *edgeNode; // used in connectElements() + BoundingVolumeHierarchy::Node *bvhNode; + }; + Degree degree : 8; + uint processed : 1; // initially false, true when the element has been checked for intersections. + uint pointingUp : 1; // used in connectElements() + uint originallyPointingUp : 1; // used in connectElements() + }; + + class ElementAllocator + { + public: + ElementAllocator(); + ~ElementAllocator(); + void allocate(int count); + Element *newElement(); + private: + struct ElementBlock + { + ElementBlock *next; + int blockSize; + int firstFree; + Element elements[1]; + } *blocks; + }; + + struct Event + { + enum Type { Upper, Lower }; + bool operator < (const Event &other) const; + + QPoint point; + Type type; + Element *element; + }; + + typedef QRBTree<Element *>::Node RBNode; + typedef BoundingVolumeHierarchy::Node BVHNode; + + void initElements(const QVectorPath &path, const QTransform &matrix); + void removeIntersections(); + void connectElements(); + void fillIndices(); + BVHNode *buildTree(Element **elements, int elementCount); + bool intersectNodes(QDataBuffer<Element *> &elements, BVHNode *elementNode, BVHNode *treeNode); + bool equalElements(const Element *e1, const Element *e2); + bool splitLineAt(QDataBuffer<Element *> &elements, BVHNode *node, quint32 pointIndex, bool processAgain); + void appendSeparatingAxes(QVarLengthArray<QPoint, 12> &axes, Element *element); + QPair<int, int> calculateSeparatingAxisRange(const QPoint &axis, Element *element); + void splitCurve(QDataBuffer<Element *> &elements, BVHNode *node); + bool setElementToQuadratic(Element *element, quint32 pointIndex1, const QPoint &ctrl, quint32 pointIndex2); + bool setElementToCubic(Element *element, quint32 pointIndex1, const QPoint &ctrl1, const QPoint &ctrl2, quint32 pointIndex2); + void setElementToCubicAndSimplify(Element *element, quint32 pointIndex1, const QPoint &ctrl1, const QPoint &ctrl2, quint32 pointIndex2); + RBNode *findElementLeftOf(const Element *element, const QPair<RBNode *, RBNode *> &bounds); + bool elementIsLeftOf(const Element *left, const Element *right); + QPair<RBNode *, RBNode *> outerBounds(const QPoint &point); + static bool flattenQuadratic(const QPoint &u, const QPoint &v, const QPoint &w); + static bool flattenCubic(const QPoint &u, const QPoint &v, const QPoint &w, const QPoint &q); + static bool splitQuadratic(const QPoint &u, const QPoint &v, const QPoint &w, QPoint *result); + static bool splitCubic(const QPoint &u, const QPoint &v, const QPoint &w, const QPoint &q, QPoint *result); + void subDivQuadratic(const QPoint &u, const QPoint &v, const QPoint &w); + void subDivCubic(const QPoint &u, const QPoint &v, const QPoint &w, const QPoint &q); + static void sortEvents(Event *events, int count); + + ElementAllocator m_elementAllocator; + QDataBuffer<Element *> m_elements; + QDataBuffer<QPoint> *m_points; + BoundingVolumeHierarchy m_bvh; + QDataBuffer<quint32> *m_indices; + QRBTree<Element *> m_elementList; + uint m_hints; +}; + +inline PathSimplifier::BoundingVolumeHierarchy::BoundingVolumeHierarchy() + : root(0) + , nodeBlock(0) + , blockSize(0) + , firstFree(0) +{ +} + +inline PathSimplifier::BoundingVolumeHierarchy::~BoundingVolumeHierarchy() +{ + free(); +} + +inline void PathSimplifier::BoundingVolumeHierarchy::allocate(int nodeCount) +{ + Q_ASSERT(nodeBlock == 0); + Q_ASSERT(firstFree == 0); + nodeBlock = new Node[blockSize = nodeCount]; +} + +inline void PathSimplifier::BoundingVolumeHierarchy::free() +{ + freeNode(root); + delete[] nodeBlock; + nodeBlock = 0; + firstFree = blockSize = 0; + root = 0; +} + +inline PathSimplifier::BVHNode *PathSimplifier::BoundingVolumeHierarchy::newNode() +{ + if (firstFree < blockSize) + return &nodeBlock[firstFree++]; + return new Node; +} + +inline void PathSimplifier::BoundingVolumeHierarchy::freeNode(Node *n) +{ + if (!n) + return; + Q_ASSERT(n->type == Node::Split || n->type == Node::Leaf); + if (n->type == Node::Split) { + freeNode(n->left); + freeNode(n->right); + } + if (!(n >= nodeBlock && n < nodeBlock + blockSize)) + delete n; +} + +inline PathSimplifier::ElementAllocator::ElementAllocator() + : blocks(0) +{ +} + +inline PathSimplifier::ElementAllocator::~ElementAllocator() +{ + while (blocks) { + ElementBlock *block = blocks; + blocks = blocks->next; + qFree(block); + } +} + +inline void PathSimplifier::ElementAllocator::allocate(int count) +{ + Q_ASSERT(blocks == 0); + Q_ASSERT(count > 0); + blocks = (ElementBlock *)qMalloc(sizeof(ElementBlock) + (count - 1) * sizeof(Element)); + blocks->blockSize = count; + blocks->next = 0; + blocks->firstFree = 0; +} + +inline PathSimplifier::Element *PathSimplifier::ElementAllocator::newElement() +{ + Q_ASSERT(blocks); + if (blocks->firstFree < blocks->blockSize) + return &blocks->elements[blocks->firstFree++]; + ElementBlock *oldBlock = blocks; + blocks = (ElementBlock *)qMalloc(sizeof(ElementBlock) + (oldBlock->blockSize - 1) * sizeof(Element)); + blocks->blockSize = oldBlock->blockSize; + blocks->next = oldBlock; + blocks->firstFree = 0; + return &blocks->elements[blocks->firstFree++]; +} + + +inline bool PathSimplifier::Event::operator < (const Event &other) const +{ + if (point == other.point) + return type < other.type; + return other.point < point; +} + +inline void PathSimplifier::Element::flip() +{ + for (int i = 0; i < (degree + 1) >> 1; ++i) { + Q_ASSERT(degree >= Line && degree <= Cubic); + Q_ASSERT(i >= 0 && i < degree); + qSwap(indices[i], indices[degree - i]); + } + pointingUp = !pointingUp; + Q_ASSERT(next == 0 && previous == 0); +} + +PathSimplifier::PathSimplifier(const QVectorPath &path, QDataBuffer<QPoint> &vertices, + QDataBuffer<quint32> &indices, const QTransform &matrix) + : m_elements(0) + , m_points(&vertices) + , m_indices(&indices) +{ + m_points->reset(); + m_indices->reset(); + initElements(path, matrix); + if (!m_elements.isEmpty()) { + removeIntersections(); + connectElements(); + fillIndices(); + } +} + +void PathSimplifier::initElements(const QVectorPath &path, const QTransform &matrix) +{ + m_hints = path.hints(); + int pathElementCount = path.elementCount(); + if (pathElementCount == 0) + return; + m_elements.reserve(2 * pathElementCount); + m_elementAllocator.allocate(2 * pathElementCount); + m_points->reserve(2 * pathElementCount); + const QPainterPath::ElementType *e = path.elements(); + const qreal *p = path.points(); + if (e) { + qreal x, y; + quint32 moveToIndex = 0; + quint32 previousIndex = 0; + for (int i = 0; i < pathElementCount; ++i, ++e, p += 2) { + switch (*e) { + case QPainterPath::MoveToElement: + { + if (!m_points->isEmpty()) { + const QPoint &from = m_points->at(previousIndex); + const QPoint &to = m_points->at(moveToIndex); + if (from != to) { + Element *element = m_elementAllocator.newElement(); + element->degree = Element::Line; + element->indices[0] = previousIndex; + element->indices[1] = moveToIndex; + element->middle.rx() = (from.x() + to.x()) >> 1; + element->middle.ry() = (from.y() + to.y()) >> 1; + m_elements.add(element); + } + } + previousIndex = moveToIndex = m_points->size(); + matrix.map(p[0], p[1], &x, &y); + QPoint to(qRound(x * Q_FIXED_POINT_SCALE), qRound(y * Q_FIXED_POINT_SCALE)); + m_points->add(to); + } + break; + case QPainterPath::LineToElement: + Q_ASSERT(!m_points->isEmpty()); + { + matrix.map(p[0], p[1], &x, &y); + QPoint to(qRound(x * Q_FIXED_POINT_SCALE), qRound(y * Q_FIXED_POINT_SCALE)); + const QPoint &from = m_points->last(); + if (to != from) { + Element *element = m_elementAllocator.newElement(); + element->degree = Element::Line; + element->indices[0] = previousIndex; + element->indices[1] = quint32(m_points->size()); + element->middle.rx() = (from.x() + to.x()) >> 1; + element->middle.ry() = (from.y() + to.y()) >> 1; + m_elements.add(element); + previousIndex = m_points->size(); + m_points->add(to); + } + } + break; + case QPainterPath::CurveToElement: + Q_ASSERT(i + 2 < pathElementCount); + Q_ASSERT(!m_points->isEmpty()); + Q_ASSERT(e[1] == QPainterPath::CurveToDataElement); + Q_ASSERT(e[2] == QPainterPath::CurveToDataElement); + { + quint32 startPointIndex = previousIndex; + matrix.map(p[4], p[5], &x, &y); + QPoint end(qRound(x * Q_FIXED_POINT_SCALE), qRound(y * Q_FIXED_POINT_SCALE)); + previousIndex = m_points->size(); + m_points->add(end); + + // See if this cubic bezier is really quadratic. + qreal x1 = p[-2] + qreal(1.5) * (p[0] - p[-2]); + qreal y1 = p[-1] + qreal(1.5) * (p[1] - p[-1]); + qreal x2 = p[4] + qreal(1.5) * (p[2] - p[4]); + qreal y2 = p[5] + qreal(1.5) * (p[3] - p[5]); + + Element *element = m_elementAllocator.newElement(); + if (qAbs(x1 - x2) < qreal(1e-3) && qAbs(y1 - y2) < qreal(1e-3)) { + // The bezier curve is quadratic. + matrix.map(x1, y1, &x, &y); + QPoint ctrl(qRound(x * Q_FIXED_POINT_SCALE), + qRound(y * Q_FIXED_POINT_SCALE)); + setElementToQuadratic(element, startPointIndex, ctrl, previousIndex); + } else { + // The bezier curve is cubic. + matrix.map(p[0], p[1], &x, &y); + QPoint ctrl1(qRound(x * Q_FIXED_POINT_SCALE), + qRound(y * Q_FIXED_POINT_SCALE)); + matrix.map(p[2], p[3], &x, &y); + QPoint ctrl2(qRound(x * Q_FIXED_POINT_SCALE), + qRound(y * Q_FIXED_POINT_SCALE)); + setElementToCubicAndSimplify(element, startPointIndex, ctrl1, ctrl2, + previousIndex); + } + m_elements.add(element); + } + i += 2; + e += 2; + p += 4; + + break; + default: + Q_ASSERT_X(0, "QSGPathSimplifier::initialize", "Unexpected element type."); + break; + } + } + if (!m_points->isEmpty()) { + const QPoint &from = m_points->at(previousIndex); + const QPoint &to = m_points->at(moveToIndex); + if (from != to) { + Element *element = m_elementAllocator.newElement(); + element->degree = Element::Line; + element->indices[0] = previousIndex; + element->indices[1] = moveToIndex; + element->middle.rx() = (from.x() + to.x()) >> 1; + element->middle.ry() = (from.y() + to.y()) >> 1; + m_elements.add(element); + } + } + } else { + qreal x, y; + + for (int i = 0; i < pathElementCount; ++i, p += 2) { + matrix.map(p[0], p[1], &x, &y); + QPoint to(qRound(x * Q_FIXED_POINT_SCALE), qRound(y * Q_FIXED_POINT_SCALE)); + if (to != m_points->last()) + m_points->add(to); + } + + while (!m_points->isEmpty() && m_points->last() == m_points->first()) + m_points->pop_back(); + + if (m_points->isEmpty()) + return; + + quint32 prev = quint32(m_points->size() - 1); + for (int i = 0; i < m_points->size(); ++i) { + QPoint &to = m_points->at(i); + QPoint &from = m_points->at(prev); + Element *element = m_elementAllocator.newElement(); + element->degree = Element::Line; + element->indices[0] = prev; + element->indices[1] = quint32(i); + element->middle.rx() = (from.x() + to.x()) >> 1; + element->middle.ry() = (from.y() + to.y()) >> 1; + m_elements.add(element); + prev = i; + } + } + + for (int i = 0; i < m_elements.size(); ++i) + m_elements.at(i)->processed = false; +} + +void PathSimplifier::removeIntersections() +{ + Q_ASSERT(!m_elements.isEmpty()); + QDataBuffer<Element *> elements(m_elements.size()); + for (int i = 0; i < m_elements.size(); ++i) + elements.add(m_elements.at(i)); + m_bvh.allocate(2 * m_elements.size()); + m_bvh.root = buildTree(elements.data(), elements.size()); + + elements.reset(); + for (int i = 0; i < m_elements.size(); ++i) + elements.add(m_elements.at(i)); + + while (!elements.isEmpty()) { + Element *element = elements.last(); + elements.pop_back(); + BVHNode *node = element->bvhNode; + Q_ASSERT(node->type == BVHNode::Leaf); + Q_ASSERT(node->element == element); + if (!element->processed) { + if (!intersectNodes(elements, node, m_bvh.root)) + element->processed = true; + } + } + + m_bvh.free(); // The bounding volume hierarchy is not needed anymore. +} + +void PathSimplifier::connectElements() +{ + Q_ASSERT(!m_elements.isEmpty()); + QDataBuffer<Event> events(m_elements.size() * 2); + for (int i = 0; i < m_elements.size(); ++i) { + Element *element = m_elements.at(i); + element->next = element->previous = 0; + element->winding = 0; + element->edgeNode = 0; + const QPoint &u = m_points->at(element->indices[0]); + const QPoint &v = m_points->at(element->indices[element->degree]); + if (u != v) { + element->pointingUp = element->originallyPointingUp = v < u; + + Event event; + event.element = element; + event.point = u; + event.type = element->pointingUp ? Event::Lower : Event::Upper; + events.add(event); + event.point = v; + event.type = element->pointingUp ? Event::Upper : Event::Lower; + events.add(event); + } + } + QVarLengthArray<Element *, 8> orderedElements; + if (!events.isEmpty()) + sortEvents(events.data(), events.size()); + while (!events.isEmpty()) { + const Event *event = &events.last(); + QPoint eventPoint = event->point; + + // Find all elements passing through the event point. + QPair<RBNode *, RBNode *> bounds = outerBounds(eventPoint); + + // Special case: single element above and single element below event point. + int eventCount = events.size(); + if (event->type == Event::Lower && eventCount > 2) { + QPair<RBNode *, RBNode *> range; + range.first = bounds.first ? m_elementList.next(bounds.first) + : m_elementList.front(m_elementList.root); + range.second = bounds.second ? m_elementList.previous(bounds.second) + : m_elementList.back(m_elementList.root); + + const Event *event2 = &events.at(eventCount - 2); + const Event *event3 = &events.at(eventCount - 3); + Q_ASSERT(event2->point == eventPoint); // There are always at least two events at a point. + if (range.first == range.second && event2->type == Event::Upper && event3->point != eventPoint) { + Element *element = event->element; + Element *element2 = event2->element; + element->edgeNode->data = event2->element; + element2->edgeNode = element->edgeNode; + element->edgeNode = 0; + + events.pop_back(); + events.pop_back(); + + if (element2->pointingUp != element->pointingUp) + element2->flip(); + element2->winding = element->winding; + int winding = element->winding; + if (element->originallyPointingUp) + ++winding; + if (winding == 0 || winding == 1) { + if (element->pointingUp) { + element->previous = event2->element; + element2->next = event->element; + } else { + element->next = event2->element; + element2->previous = event->element; + } + } + continue; + } + } + orderedElements.clear(); + + // First, find the ones above the event point. + if (m_elementList.root) { + RBNode *current = bounds.first ? m_elementList.next(bounds.first) + : m_elementList.front(m_elementList.root); + while (current != bounds.second) { + Element *element = current->data; + Q_ASSERT(element->edgeNode == current); + int winding = element->winding; + if (element->originallyPointingUp) + ++winding; + const QPoint &lower = m_points->at(element->lowerIndex()); + if (lower == eventPoint) { + if (winding == 0 || winding == 1) + orderedElements.append(current->data); + } else { + // The element is passing through 'event.point'. + Q_ASSERT(m_points->at(element->upperIndex()) != eventPoint); + Q_ASSERT(element->degree == Element::Line); + // Split the line. + Element *eventElement = event->element; + int indexIndex = (event->type == Event::Upper) == eventElement->pointingUp + ? eventElement->degree : 0; + quint32 pointIndex = eventElement->indices[indexIndex]; + Q_ASSERT(eventPoint == m_points->at(pointIndex)); + + Element *upperElement = m_elementAllocator.newElement(); + *upperElement = *element; + upperElement->lowerIndex() = element->upperIndex() = pointIndex; + upperElement->edgeNode = 0; + element->next = element->previous = 0; + if (upperElement->next) + upperElement->next->previous = upperElement; + else if (upperElement->previous) + upperElement->previous->next = upperElement; + if (element->pointingUp != element->originallyPointingUp) + element->flip(); + if (winding == 0 || winding == 1) + orderedElements.append(upperElement); + m_elements.add(upperElement); + } + current = m_elementList.next(current); + } + } + while (!events.isEmpty() && events.last().point == eventPoint) { + event = &events.last(); + if (event->type == Event::Upper) { + Q_ASSERT(event->point == m_points->at(event->element->upperIndex())); + RBNode *left = findElementLeftOf(event->element, bounds); + RBNode *node = m_elementList.newNode(); + node->data = event->element; + Q_ASSERT(event->element->edgeNode == 0); + event->element->edgeNode = node; + m_elementList.attachAfter(left, node); + } else { + Q_ASSERT(event->type == Event::Lower); + Q_ASSERT(event->point == m_points->at(event->element->lowerIndex())); + Element *element = event->element; + Q_ASSERT(element->edgeNode); + m_elementList.deleteNode(element->edgeNode); + Q_ASSERT(element->edgeNode == 0); + } + events.pop_back(); + } + + if (m_elementList.root) { + RBNode *current = bounds.first ? m_elementList.next(bounds.first) + : m_elementList.front(m_elementList.root); + int winding = bounds.first ? bounds.first->data->winding : 0; + + // Calculate winding numbers and flip elements if necessary. + while (current != bounds.second) { + Element *element = current->data; + Q_ASSERT(element->edgeNode == current); + int ccw = winding & 1; + Q_ASSERT(element->pointingUp == element->originallyPointingUp); + if (element->originallyPointingUp) { + --winding; + } else { + ++winding; + ccw ^= 1; + } + element->winding = winding; + if (ccw == 0) + element->flip(); + current = m_elementList.next(current); + } + + // Pick elements with correct winding number. + current = bounds.second ? m_elementList.previous(bounds.second) + : m_elementList.back(m_elementList.root); + while (current != bounds.first) { + Element *element = current->data; + Q_ASSERT(element->edgeNode == current); + Q_ASSERT(m_points->at(element->upperIndex()) == eventPoint); + int winding = element->winding; + if (element->originallyPointingUp) + ++winding; + if (winding == 0 || winding == 1) + orderedElements.append(current->data); + current = m_elementList.previous(current); + } + } + + if (!orderedElements.isEmpty()) { + Q_ASSERT((orderedElements.size() & 1) == 0); + int i = 0; + Element *firstElement = orderedElements.at(0); + if (m_points->at(firstElement->indices[0]) != eventPoint) { + orderedElements.append(firstElement); + i = 1; + } + for (; i < orderedElements.size(); i += 2) { + Q_ASSERT(i + 1 < orderedElements.size()); + Element *next = orderedElements.at(i); + Element *previous = orderedElements.at(i + 1); + Q_ASSERT(next->previous == 0); + Q_ASSERT(previous->next == 0); + next->previous = previous; + previous->next = next; + } + } + } +#ifndef QT_NO_DEBUG + for (int i = 0; i < m_elements.size(); ++i) { + const Element *element = m_elements.at(i); + Q_ASSERT(element->next == 0 || element->next->previous == element); + Q_ASSERT(element->previous == 0 || element->previous->next == element); + Q_ASSERT((element->next == 0) == (element->previous == 0)); + } +#endif +} + +void PathSimplifier::fillIndices() +{ + for (int i = 0; i < m_elements.size(); ++i) + m_elements.at(i)->processed = false; + for (int i = 0; i < m_elements.size(); ++i) { + Element *element = m_elements.at(i); + if (element->processed || element->next == 0) + continue; + do { + m_indices->add(element->indices[0]); + switch (element->degree) { + case Element::Quadratic: + { + QPoint pts[] = { + m_points->at(element->indices[0]), + m_points->at(element->indices[1]), + m_points->at(element->indices[2]) + }; + subDivQuadratic(pts[0], pts[1], pts[2]); + } + break; + case Element::Cubic: + { + QPoint pts[] = { + m_points->at(element->indices[0]), + m_points->at(element->indices[1]), + m_points->at(element->indices[2]), + m_points->at(element->indices[3]) + }; + subDivCubic(pts[0], pts[1], pts[2], pts[3]); + } + break; + default: + break; + } + Q_ASSERT(element->next); + element->processed = true; + element = element->next; + } while (element != m_elements.at(i)); + m_indices->add(Q_TRIANGULATE_END_OF_POLYGON); + } +} + +PathSimplifier::BVHNode *PathSimplifier::buildTree(Element **elements, int elementCount) +{ + Q_ASSERT(elementCount > 0); + BVHNode *node = m_bvh.newNode(); + if (elementCount == 1) { + Element *element = *elements; + element->bvhNode = node; + node->type = BVHNode::Leaf; + node->element = element; + node->minimum = node->maximum = m_points->at(element->indices[0]); + for (int i = 1; i <= element->degree; ++i) { + const QPoint &p = m_points->at(element->indices[i]); + node->minimum.rx() = qMin(node->minimum.x(), p.x()); + node->minimum.ry() = qMin(node->minimum.y(), p.y()); + node->maximum.rx() = qMax(node->maximum.x(), p.x()); + node->maximum.ry() = qMax(node->maximum.y(), p.y()); + } + return node; + } + + node->type = BVHNode::Split; + + QPoint minimum, maximum; + minimum = maximum = elements[0]->middle; + + for (int i = 1; i < elementCount; ++i) { + const QPoint &p = elements[i]->middle; + minimum.rx() = qMin(minimum.x(), p.x()); + minimum.ry() = qMin(minimum.y(), p.y()); + maximum.rx() = qMax(maximum.x(), p.x()); + maximum.ry() = qMax(maximum.y(), p.y()); + } + + int comp, pivot; + if (maximum.x() - minimum.x() > maximum.y() - minimum.y()) { + comp = 0; + pivot = (maximum.x() + minimum.x()) >> 1; + } else { + comp = 1; + pivot = (maximum.y() + minimum.y()) >> 1; + } + + int lo = 0; + int hi = elementCount - 1; + while (lo < hi) { + while (lo < hi && (&elements[lo]->middle.rx())[comp] <= pivot) + ++lo; + while (lo < hi && (&elements[hi]->middle.rx())[comp] > pivot) + --hi; + if (lo < hi) + qSwap(elements[lo], elements[hi]); + } + + if (lo == elementCount) { + // All points are the same. + Q_ASSERT(minimum.x() == maximum.x() && minimum.y() == maximum.y()); + lo = elementCount >> 1; + } + + node->left = buildTree(elements, lo); + node->right = buildTree(elements + lo, elementCount - lo); + + const BVHNode *left = node->left; + const BVHNode *right = node->right; + node->minimum.rx() = qMin(left->minimum.x(), right->minimum.x()); + node->minimum.ry() = qMin(left->minimum.y(), right->minimum.y()); + node->maximum.rx() = qMax(left->maximum.x(), right->maximum.x()); + node->maximum.ry() = qMax(left->maximum.y(), right->maximum.y()); + + return node; +} + +bool PathSimplifier::intersectNodes(QDataBuffer<Element *> &elements, BVHNode *elementNode, + BVHNode *treeNode) +{ + if (elementNode->minimum.x() >= treeNode->maximum.x() + || elementNode->minimum.y() >= treeNode->maximum.y() + || elementNode->maximum.x() <= treeNode->minimum.x() + || elementNode->maximum.y() <= treeNode->minimum.y()) + { + return false; + } + + Q_ASSERT(elementNode->type == BVHNode::Leaf); + Element *element = elementNode->element; + Q_ASSERT(!element->processed); + + if (treeNode->type == BVHNode::Leaf) { + Element *nodeElement = treeNode->element; + if (!nodeElement->processed) + return false; + + if (treeNode->element == elementNode->element) + return false; + + if (equalElements(treeNode->element, elementNode->element)) + return false; // element doesn't split itself. + + if (element->degree == Element::Line && nodeElement->degree == Element::Line) { + const QPoint &u1 = m_points->at(element->indices[0]); + const QPoint &u2 = m_points->at(element->indices[1]); + const QPoint &v1 = m_points->at(nodeElement->indices[0]); + const QPoint &v2 = m_points->at(nodeElement->indices[1]); + IntersectionPoint intersection = intersectionPoint(u1, u2, v1, v2); + if (!intersection.isValid()) + return false; + + Q_ASSERT(intersection.x.integer >= qMin(u1.x(), u2.x())); + Q_ASSERT(intersection.y.integer >= qMin(u1.y(), u2.y())); + Q_ASSERT(intersection.x.integer >= qMin(v1.x(), v2.x())); + Q_ASSERT(intersection.y.integer >= qMin(v1.y(), v2.y())); + + Q_ASSERT(intersection.x.integer <= qMax(u1.x(), u2.x())); + Q_ASSERT(intersection.y.integer <= qMax(u1.y(), u2.y())); + Q_ASSERT(intersection.x.integer <= qMax(v1.x(), v2.x())); + Q_ASSERT(intersection.y.integer <= qMax(v1.y(), v2.y())); + + m_points->add(intersection.round()); + splitLineAt(elements, treeNode, m_points->size() - 1, !intersection.isAccurate()); + return splitLineAt(elements, elementNode, m_points->size() - 1, false); + } else { + QVarLengthArray<QPoint, 12> axes; + appendSeparatingAxes(axes, elementNode->element); + appendSeparatingAxes(axes, treeNode->element); + for (int i = 0; i < axes.size(); ++i) { + QPair<int, int> range1 = calculateSeparatingAxisRange(axes.at(i), elementNode->element); + QPair<int, int> range2 = calculateSeparatingAxisRange(axes.at(i), treeNode->element); + if (range1.first >= range2.second || range1.second <= range2.first) { + return false; // Separating axis found. + } + } + // Bounding areas overlap. + if (nodeElement->degree > Element::Line) + splitCurve(elements, treeNode); + if (element->degree > Element::Line) { + splitCurve(elements, elementNode); + } else { + // The element was not split, so it can be processed further. + if (intersectNodes(elements, elementNode, treeNode->left)) + return true; + if (intersectNodes(elements, elementNode, treeNode->right)) + return true; + return false; + } + return true; + } + } else { + if (intersectNodes(elements, elementNode, treeNode->left)) + return true; + if (intersectNodes(elements, elementNode, treeNode->right)) + return true; + return false; + } +} + +bool PathSimplifier::equalElements(const Element *e1, const Element *e2) +{ + Q_ASSERT(e1 != e2); + if (e1->degree != e2->degree) + return false; + + // Possibly equal and in the same direction. + bool equalSame = true; + for (int i = 0; i <= e1->degree; ++i) + equalSame &= m_points->at(e1->indices[i]) == m_points->at(e2->indices[i]); + + // Possibly equal and in opposite directions. + bool equalOpposite = true; + for (int i = 0; i <= e1->degree; ++i) + equalOpposite &= m_points->at(e1->indices[e1->degree - i]) == m_points->at(e2->indices[i]); + + return equalSame || equalOpposite; +} + +bool PathSimplifier::splitLineAt(QDataBuffer<Element *> &elements, BVHNode *node, + quint32 pointIndex, bool processAgain) +{ + Q_ASSERT(node->type == BVHNode::Leaf); + Element *element = node->element; + Q_ASSERT(element->degree == Element::Line); + const QPoint &u = m_points->at(element->indices[0]); + const QPoint &v = m_points->at(element->indices[1]); + const QPoint &p = m_points->at(pointIndex); + if (u == p || v == p) + return false; // No split needed. + + if (processAgain) + element->processed = false; // Needs to be processed again. + + Element *first = node->element; + Element *second = m_elementAllocator.newElement(); + *second = *first; + first->indices[1] = second->indices[0] = pointIndex; + first->middle.rx() = (u.x() + p.x()) >> 1; + first->middle.ry() = (u.y() + p.y()) >> 1; + second->middle.rx() = (v.x() + p.x()) >> 1; + second->middle.ry() = (v.y() + p.y()) >> 1; + m_elements.add(second); + + BVHNode *left = m_bvh.newNode(); + BVHNode *right = m_bvh.newNode(); + left->type = right->type = BVHNode::Leaf; + left->element = first; + right->element = second; + left->minimum = right->minimum = node->minimum; + left->maximum = right->maximum = node->maximum; + if (u.x() < v.x()) + left->maximum.rx() = right->minimum.rx() = p.x(); + else + left->minimum.rx() = right->maximum.rx() = p.x(); + if (u.y() < v.y()) + left->maximum.ry() = right->minimum.ry() = p.y(); + else + left->minimum.ry() = right->maximum.ry() = p.y(); + left->element->bvhNode = left; + right->element->bvhNode = right; + + node->type = BVHNode::Split; + node->left = left; + node->right = right; + + if (!first->processed) { + elements.add(left->element); + elements.add(right->element); + } + return true; +} + +void PathSimplifier::appendSeparatingAxes(QVarLengthArray<QPoint, 12> &axes, Element *element) +{ + switch (element->degree) { + case Element::Cubic: + { + const QPoint &u = m_points->at(element->indices[0]); + const QPoint &v = m_points->at(element->indices[1]); + const QPoint &w = m_points->at(element->indices[2]); + const QPoint &q = m_points->at(element->indices[3]); + QPoint ns[] = { + QPoint(u.y() - v.y(), v.x() - u.x()), + QPoint(v.y() - w.y(), w.x() - v.x()), + QPoint(w.y() - q.y(), q.x() - w.x()), + QPoint(q.y() - u.y(), u.x() - q.x()), + QPoint(u.y() - w.y(), w.x() - u.x()), + QPoint(v.y() - q.y(), q.x() - v.x()) + }; + for (int i = 0; i < 6; ++i) { + if (ns[i].x() || ns[i].y()) + axes.append(ns[i]); + } + } + break; + case Element::Quadratic: + { + const QPoint &u = m_points->at(element->indices[0]); + const QPoint &v = m_points->at(element->indices[1]); + const QPoint &w = m_points->at(element->indices[2]); + QPoint ns[] = { + QPoint(u.y() - v.y(), v.x() - u.x()), + QPoint(v.y() - w.y(), w.x() - v.x()), + QPoint(w.y() - u.y(), u.x() - w.x()) + }; + for (int i = 0; i < 3; ++i) { + if (ns[i].x() || ns[i].y()) + axes.append(ns[i]); + } + } + break; + case Element::Line: + { + const QPoint &u = m_points->at(element->indices[0]); + const QPoint &v = m_points->at(element->indices[1]); + QPoint n(u.y() - v.y(), v.x() - u.x()); + if (n.x() || n.y()) + axes.append(n); + } + break; + default: + Q_ASSERT_X(0, "QSGPathSimplifier::appendSeparatingAxes", "Unexpected element type."); + break; + } +} + +QPair<int, int> PathSimplifier::calculateSeparatingAxisRange(const QPoint &axis, Element *element) +{ + QPair<int, int> range(0x7fffffff, -0x7fffffff); + for (int i = 0; i <= element->degree; ++i) { + const QPoint &p = m_points->at(element->indices[i]); + int dist = dot(axis, p); + range.first = qMin(range.first, dist); + range.second = qMax(range.second, dist); + } + return range; +} + +void PathSimplifier::splitCurve(QDataBuffer<Element *> &elements, BVHNode *node) +{ + Q_ASSERT(node->type == BVHNode::Leaf); + + Element *first = node->element; + Element *second = m_elementAllocator.newElement(); + *second = *first; + m_elements.add(second); + Q_ASSERT(first->degree > Element::Line); + + bool accurate = true; + const QPoint &u = m_points->at(first->indices[0]); + const QPoint &v = m_points->at(first->indices[1]); + const QPoint &w = m_points->at(first->indices[2]); + + if (first->degree == Element::Quadratic) { + QPoint pts[3]; + accurate = splitQuadratic(u, v, w, pts); + int pointIndex = m_points->size(); + m_points->add(pts[1]); + accurate &= setElementToQuadratic(first, first->indices[0], pts[0], pointIndex); + accurate &= setElementToQuadratic(second, pointIndex, pts[2], second->indices[2]); + } else { + Q_ASSERT(first->degree == Element::Cubic); + const QPoint &q = m_points->at(first->indices[3]); + QPoint pts[5]; + accurate = splitCubic(u, v, w, q, pts); + int pointIndex = m_points->size(); + m_points->add(pts[2]); + accurate &= setElementToCubic(first, first->indices[0], pts[0], pts[1], pointIndex); + accurate &= setElementToCubic(second, pointIndex, pts[3], pts[4], second->indices[3]); + } + + if (!accurate) + first->processed = second->processed = false; // Needs to be processed again. + + BVHNode *left = m_bvh.newNode(); + BVHNode *right = m_bvh.newNode(); + left->type = right->type = BVHNode::Leaf; + left->element = first; + right->element = second; + + left->minimum.rx() = left->minimum.ry() = right->minimum.rx() = right->minimum.ry() = INT_MAX; + left->maximum.rx() = left->maximum.ry() = right->maximum.rx() = right->maximum.ry() = INT_MIN; + + for (int i = 0; i <= first->degree; ++i) { + QPoint &p = m_points->at(first->indices[i]); + left->minimum.rx() = qMin(left->minimum.x(), p.x()); + left->minimum.ry() = qMin(left->minimum.y(), p.y()); + left->maximum.rx() = qMax(left->maximum.x(), p.x()); + left->maximum.ry() = qMax(left->maximum.y(), p.y()); + } + for (int i = 0; i <= second->degree; ++i) { + QPoint &p = m_points->at(second->indices[i]); + right->minimum.rx() = qMin(right->minimum.x(), p.x()); + right->minimum.ry() = qMin(right->minimum.y(), p.y()); + right->maximum.rx() = qMax(right->maximum.x(), p.x()); + right->maximum.ry() = qMax(right->maximum.y(), p.y()); + } + left->element->bvhNode = left; + right->element->bvhNode = right; + + node->type = BVHNode::Split; + node->left = left; + node->right = right; + + if (!first->processed) { + elements.add(left->element); + elements.add(right->element); + } +} + +bool PathSimplifier::setElementToQuadratic(Element *element, quint32 pointIndex1, + const QPoint &ctrl, quint32 pointIndex2) +{ + const QPoint &p1 = m_points->at(pointIndex1); + const QPoint &p2 = m_points->at(pointIndex2); + if (flattenQuadratic(p1, ctrl, p2)) { + // Insert line. + element->degree = Element::Line; + element->indices[0] = pointIndex1; + element->indices[1] = pointIndex2; + element->middle.rx() = (p1.x() + p2.x()) >> 1; + element->middle.ry() = (p1.y() + p2.y()) >> 1; + return false; + } else { + // Insert bezier. + element->degree = Element::Quadratic; + element->indices[0] = pointIndex1; + element->indices[1] = m_points->size(); + element->indices[2] = pointIndex2; + element->middle.rx() = (p1.x() + ctrl.x() + p2.x()) / 3; + element->middle.ry() = (p1.y() + ctrl.y() + p2.y()) / 3; + m_points->add(ctrl); + return true; + } +} + +bool PathSimplifier::setElementToCubic(Element *element, quint32 pointIndex1, const QPoint &v, + const QPoint &w, quint32 pointIndex2) +{ + const QPoint &u = m_points->at(pointIndex1); + const QPoint &q = m_points->at(pointIndex2); + if (flattenCubic(u, v, w, q)) { + // Insert line. + element->degree = Element::Line; + element->indices[0] = pointIndex1; + element->indices[1] = pointIndex2; + element->middle.rx() = (u.x() + q.x()) >> 1; + element->middle.ry() = (u.y() + q.y()) >> 1; + return false; + } else { + // Insert bezier. + element->degree = Element::Cubic; + element->indices[0] = pointIndex1; + element->indices[1] = m_points->size(); + element->indices[2] = m_points->size() + 1; + element->indices[3] = pointIndex2; + element->middle.rx() = (u.x() + v.x() + w.x() + q.x()) >> 2; + element->middle.ry() = (u.y() + v.y() + w.y() + q.y()) >> 2; + m_points->add(v); + m_points->add(w); + return true; + } +} + +void PathSimplifier::setElementToCubicAndSimplify(Element *element, quint32 pointIndex1, + const QPoint &v, const QPoint &w, + quint32 pointIndex2) +{ + const QPoint &u = m_points->at(pointIndex1); + const QPoint &q = m_points->at(pointIndex2); + if (flattenCubic(u, v, w, q)) { + // Insert line. + element->degree = Element::Line; + element->indices[0] = pointIndex1; + element->indices[1] = pointIndex2; + element->middle.rx() = (u.x() + q.x()) >> 1; + element->middle.ry() = (u.y() + q.y()) >> 1; + return; + } + + bool intersecting = (u == q) || intersectionPoint(u, v, w, q).isValid(); + if (!intersecting) { + // Insert bezier. + element->degree = Element::Cubic; + element->indices[0] = pointIndex1; + element->indices[1] = m_points->size(); + element->indices[2] = m_points->size() + 1; + element->indices[3] = pointIndex2; + element->middle.rx() = (u.x() + v.x() + w.x() + q.x()) >> 2; + element->middle.ry() = (u.y() + v.y() + w.y() + q.y()) >> 2; + m_points->add(v); + m_points->add(w); + return; + } + + QPoint pts[5]; + splitCubic(u, v, w, q, pts); + int pointIndex = m_points->size(); + m_points->add(pts[2]); + Element *element2 = m_elementAllocator.newElement(); + m_elements.add(element2); + setElementToCubicAndSimplify(element, pointIndex1, pts[0], pts[1], pointIndex); + setElementToCubicAndSimplify(element2, pointIndex, pts[3], pts[4], pointIndex2); +} + +PathSimplifier::RBNode *PathSimplifier::findElementLeftOf(const Element *element, + const QPair<RBNode *, RBNode *> &bounds) +{ + if (!m_elementList.root) + return 0; + RBNode *current = bounds.first; + Q_ASSERT(!current || !elementIsLeftOf(element, current->data)); + if (!current) + current = m_elementList.front(m_elementList.root); + Q_ASSERT(current); + RBNode *result = 0; + while (current != bounds.second && !elementIsLeftOf(element, current->data)) { + result = current; + current = m_elementList.next(current); + } + return result; +} + +bool PathSimplifier::elementIsLeftOf(const Element *left, const Element *right) +{ + const QPoint &leftU = m_points->at(left->upperIndex()); + const QPoint &leftL = m_points->at(left->lowerIndex()); + const QPoint &rightU = m_points->at(right->upperIndex()); + const QPoint &rightL = m_points->at(right->lowerIndex()); + Q_ASSERT(leftL >= rightU && rightL >= leftU); + if (leftU.x() < qMin(rightL.x(), rightU.x())) + return true; + if (leftU.x() > qMax(rightL.x(), rightU.x())) + return false; + int d = pointDistanceFromLine(leftU, rightL, rightU); + // d < 0: left, d > 0: right, d == 0: on top + if (d == 0) { + d = pointDistanceFromLine(leftL, rightL, rightU); + if (d == 0) { + if (right->degree > Element::Line) { + d = pointDistanceFromLine(leftL, rightL, m_points->at(right->indices[1])); + if (d == 0) + d = pointDistanceFromLine(leftL, rightL, m_points->at(right->indices[2])); + } else if (left->degree > Element::Line) { + d = pointDistanceFromLine(m_points->at(left->indices[1]), rightL, rightU); + if (d == 0) + d = pointDistanceFromLine(m_points->at(left->indices[2]), rightL, rightU); + } + } + } + return d < 0; +} + +QPair<PathSimplifier::RBNode *, PathSimplifier::RBNode *> PathSimplifier::outerBounds(const QPoint &point) +{ + RBNode *current = m_elementList.root; + QPair<RBNode *, RBNode *> result(0, 0); + + while (current) { + const Element *element = current->data; + Q_ASSERT(element->edgeNode == current); + const QPoint &v1 = m_points->at(element->lowerIndex()); + const QPoint &v2 = m_points->at(element->upperIndex()); + Q_ASSERT(point >= v2 && point <= v1); + if (point == v1 || point == v2) + break; + int d = pointDistanceFromLine(point, v1, v2); + if (d == 0) { + if (element->degree == Element::Line) + break; + d = pointDistanceFromLine(point, v1, m_points->at(element->indices[1])); + if (d == 0) + d = pointDistanceFromLine(point, v1, m_points->at(element->indices[2])); + Q_ASSERT(d != 0); + } + if (d < 0) { + result.second = current; + current = current->left; + } else { + result.first = current; + current = current->right; + } + } + + if (!current) + return result; + + RBNode *mid = current; + + current = mid->left; + while (current) { + const Element *element = current->data; + Q_ASSERT(element->edgeNode == current); + const QPoint &v1 = m_points->at(element->lowerIndex()); + const QPoint &v2 = m_points->at(element->upperIndex()); + Q_ASSERT(point >= v2 && point <= v1); + bool equal = (point == v1 || point == v2); + if (!equal) { + int d = pointDistanceFromLine(point, v1, v2); + Q_ASSERT(d >= 0); + equal = (d == 0 && element->degree == Element::Line); + } + if (equal) { + current = current->left; + } else { + result.first = current; + current = current->right; + } + } + + current = mid->right; + while (current) { + const Element *element = current->data; + Q_ASSERT(element->edgeNode == current); + const QPoint &v1 = m_points->at(element->lowerIndex()); + const QPoint &v2 = m_points->at(element->upperIndex()); + Q_ASSERT(point >= v2 && point <= v1); + bool equal = (point == v1 || point == v2); + if (!equal) { + int d = pointDistanceFromLine(point, v1, v2); + Q_ASSERT(d <= 0); + equal = (d == 0 && element->degree == Element::Line); + } + if (equal) { + current = current->right; + } else { + result.second = current; + current = current->left; + } + } + + return result; +} + +inline bool PathSimplifier::flattenQuadratic(const QPoint &u, const QPoint &v, const QPoint &w) +{ + QPoint deltas[2] = { v - u, w - v }; + int d = qAbs(cross(deltas[0], deltas[1])); + int l = qAbs(deltas[0].x()) + qAbs(deltas[0].y()) + qAbs(deltas[1].x()) + qAbs(deltas[1].y()); + return d < (Q_FIXED_POINT_SCALE * Q_FIXED_POINT_SCALE * 3 / 2) || l <= Q_FIXED_POINT_SCALE * 2; +} + +inline bool PathSimplifier::flattenCubic(const QPoint &u, const QPoint &v, + const QPoint &w, const QPoint &q) +{ + QPoint deltas[] = { v - u, w - v, q - w, q - u }; + int d = qAbs(cross(deltas[0], deltas[1])) + qAbs(cross(deltas[1], deltas[2])) + + qAbs(cross(deltas[0], deltas[3])) + qAbs(cross(deltas[3], deltas[2])); + int l = qAbs(deltas[0].x()) + qAbs(deltas[0].y()) + qAbs(deltas[1].x()) + qAbs(deltas[1].y()) + + qAbs(deltas[2].x()) + qAbs(deltas[2].y()); + return d < (Q_FIXED_POINT_SCALE * Q_FIXED_POINT_SCALE * 3) || l <= Q_FIXED_POINT_SCALE * 2; +} + +inline bool PathSimplifier::splitQuadratic(const QPoint &u, const QPoint &v, + const QPoint &w, QPoint *result) +{ + result[0] = u + v; + result[2] = v + w; + result[1] = result[0] + result[2]; + bool accurate = ((result[0].x() | result[0].y() | result[2].x() | result[2].y()) & 1) == 0 + && ((result[1].x() | result[1].y()) & 3) == 0; + result[0].rx() >>= 1; + result[0].ry() >>= 1; + result[1].rx() >>= 2; + result[1].ry() >>= 2; + result[2].rx() >>= 1; + result[2].ry() >>= 1; + return accurate; +} + +inline bool PathSimplifier::splitCubic(const QPoint &u, const QPoint &v, + const QPoint &w, const QPoint &q, QPoint *result) +{ + result[0] = u + v; + result[2] = v + w; + result[4] = w + q; + result[1] = result[0] + result[2]; + result[3] = result[2] + result[4]; + result[2] = result[1] + result[3]; + bool accurate = ((result[0].x() | result[0].y() | result[4].x() | result[4].y()) & 1) == 0 + && ((result[1].x() | result[1].y() | result[3].x() | result[3].y()) & 3) == 0 + && ((result[2].x() | result[2].y()) & 7) == 0; + result[0].rx() >>= 1; + result[0].ry() >>= 1; + result[1].rx() >>= 2; + result[1].ry() >>= 2; + result[2].rx() >>= 3; + result[2].ry() >>= 3; + result[3].rx() >>= 2; + result[3].ry() >>= 2; + result[4].rx() >>= 1; + result[4].ry() >>= 1; + return accurate; +} + +inline void PathSimplifier::subDivQuadratic(const QPoint &u, const QPoint &v, const QPoint &w) +{ + if (flattenQuadratic(u, v, w)) + return; + QPoint pts[3]; + splitQuadratic(u, v, w, pts); + subDivQuadratic(u, pts[0], pts[1]); + m_indices->add(m_points->size()); + m_points->add(pts[1]); + subDivQuadratic(pts[1], pts[2], w); +} + +inline void PathSimplifier::subDivCubic(const QPoint &u, const QPoint &v, + const QPoint &w, const QPoint &q) +{ + if (flattenCubic(u, v, w, q)) + return; + QPoint pts[5]; + splitCubic(u, v, w, q, pts); + subDivCubic(u, pts[0], pts[1], pts[2]); + m_indices->add(m_points->size()); + m_points->add(pts[2]); + subDivCubic(pts[2], pts[3], pts[4], q); +} + +void PathSimplifier::sortEvents(Event *events, int count) +{ + // Bucket sort + insertion sort. + Q_ASSERT(count > 0); + QDataBuffer<Event> buffer(count); + buffer.resize(count); + QScopedArrayPointer<int> bins(new int[count]); + int counts[0x101]; + memset(counts, 0, sizeof(counts)); + + int minimum, maximum; + minimum = maximum = events[0].point.y(); + for (int i = 1; i < count; ++i) { + minimum = qMin(minimum, events[i].point.y()); + maximum = qMax(maximum, events[i].point.y()); + } + + for (int i = 0; i < count; ++i) { + bins[i] = ((maximum - events[i].point.y()) << 8) / (maximum - minimum + 1); + Q_ASSERT(bins[i] >= 0 && bins[i] < 0x100); + ++counts[bins[i]]; + } + + for (int i = 1; i < 0x100; ++i) + counts[i] += counts[i - 1]; + counts[0x100] = counts[0xff]; + Q_ASSERT(counts[0x100] == count); + + for (int i = 0; i < count; ++i) + buffer.at(--counts[bins[i]]) = events[i]; + + int j = 0; + for (int i = 0; i < 0x100; ++i) { + for (; j < counts[i + 1]; ++j) { + int k = j; + while (k > 0 && (buffer.at(j) < events[k - 1])) { + events[k] = events[k - 1]; + --k; + } + events[k] = buffer.at(j); + } + } +} + +} // end anonymous namespace + + +void qSimplifyPath(const QVectorPath &path, QDataBuffer<QPoint> &vertices, + QDataBuffer<quint32> &indices, const QTransform &matrix) +{ + PathSimplifier(path, vertices, indices, matrix); +} + +void qSimplifyPath(const QPainterPath &path, QDataBuffer<QPoint> &vertices, + QDataBuffer<quint32> &indices, const QTransform &matrix) +{ + qSimplifyPath(qtVectorPathForPath(path), vertices, indices, matrix); +} + + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgpathsimplifier_p.h b/src/quick/scenegraph/qsgpathsimplifier_p.h new file mode 100644 index 0000000000..0639c4f622 --- /dev/null +++ b/src/quick/scenegraph/qsgpathsimplifier_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGPATHSIMPLIFIER_P_H +#define QSGPATHSIMPLIFIER_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. +// + +#include <QtGui/qpainterpath.h> +#include <QtGui/private/qdatabuffer_p.h> +#include <QtGui/private/qvectorpath_p.h> + +QT_BEGIN_NAMESPACE + +// The returned vertices are in 8:8 fixed point format. The path is assumed to be in the range (-128, 128)x(-128, 128). +void qSimplifyPath(const QVectorPath &path, QDataBuffer<QPoint> &vertices, QDataBuffer<quint32> &indices, const QTransform &matrix = QTransform()); +void qSimplifyPath(const QPainterPath &path, QDataBuffer<QPoint> &vertices, QDataBuffer<quint32> &indices, const QTransform &matrix = QTransform()); + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri new file mode 100644 index 0000000000..0adc20502d --- /dev/null +++ b/src/quick/scenegraph/scenegraph.pri @@ -0,0 +1,86 @@ +!contains(QT_CONFIG, egl):DEFINES += QT_NO_EGL + +# Core API +HEADERS += \ + $$PWD/coreapi/qsgdefaultrenderer_p.h \ + $$PWD/coreapi/qsggeometry.h \ + $$PWD/coreapi/qsgmaterial.h \ + $$PWD/coreapi/qsgnode.h \ + $$PWD/coreapi/qsgnodeupdater_p.h \ + $$PWD/coreapi/qsgrenderer_p.h \ + $$PWD/coreapi/qsggeometry_p.h + +SOURCES += \ + $$PWD/coreapi/qsgdefaultrenderer.cpp \ + $$PWD/coreapi/qsggeometry.cpp \ + $$PWD/coreapi/qsgmaterial.cpp \ + $$PWD/coreapi/qsgnode.cpp \ + $$PWD/coreapi/qsgnodeupdater.cpp \ + $$PWD/coreapi/qsgrenderer.cpp + + +# Util API +HEADERS += \ + $$PWD/util/qsgareaallocator_p.h \ + $$PWD/util/qsgengine.h \ + $$PWD/util/qsgflatcolormaterial.h \ + $$PWD/util/qsgsimplematerial.h \ + $$PWD/util/qsgsimplerectnode.h \ + $$PWD/util/qsgsimpletexturenode.h \ + $$PWD/util/qsgtexturematerial.h \ + $$PWD/util/qsgtexturematerial_p.h \ + $$PWD/util/qsgvertexcolormaterial.h \ + $$PWD/util/qsgtexture.h \ + $$PWD/util/qsgtexture_p.h \ + $$PWD/util/qsgtextureprovider_p.h \ + $$PWD/util/qsgpainternode_p.h \ + $$PWD/util/qsgdistancefieldutil_p.h + +SOURCES += \ + $$PWD/util/qsgareaallocator.cpp \ + $$PWD/util/qsgengine.cpp \ + $$PWD/util/qsgflatcolormaterial.cpp \ + $$PWD/util/qsgsimplerectnode.cpp \ + $$PWD/util/qsgsimpletexturenode.cpp \ + $$PWD/util/qsgtexturematerial.cpp \ + $$PWD/util/qsgvertexcolormaterial.cpp \ + $$PWD/util/qsgtexture.cpp \ + $$PWD/util/qsgtextureprovider.cpp \ + $$PWD/util/qsgpainternode.cpp \ + $$PWD/util/qsgdistancefieldutil.cpp + + +# QML / Adaptations API +HEADERS += \ + $$PWD/qsgadaptationlayer_p.h \ + $$PWD/qsgcontext_p.h \ + $$PWD/qsgcontextplugin_p.h \ + $$PWD/qsgdefaultglyphnode_p.h \ + $$PWD/qsgdefaultdistancefieldglyphcache_p.h \ + $$PWD/qsgdistancefieldglyphnode_p.h \ + $$PWD/qsgdistancefieldglyphnode_p_p.h \ + $$PWD/qsgdefaultglyphnode_p_p.h \ + $$PWD/qsgdefaultimagenode_p.h \ + $$PWD/qsgdefaultrectanglenode_p.h \ + $$PWD/qsgflashnode_p.h \ + $$PWD/qsgpathsimplifier_p.h + +SOURCES += \ + $$PWD/qsgadaptationlayer.cpp \ + $$PWD/qsgcontext.cpp \ + $$PWD/qsgcontextplugin.cpp \ + $$PWD/qsgdefaultglyphnode.cpp \ + $$PWD/qsgdefaultglyphnode_p.cpp \ + $$PWD/qsgdefaultdistancefieldglyphcache.cpp \ + $$PWD/qsgdistancefieldglyphnode.cpp \ + $$PWD/qsgdistancefieldglyphnode_p.cpp \ + $$PWD/qsgdefaultimagenode.cpp \ + $$PWD/qsgdefaultrectanglenode.cpp \ + $$PWD/qsgflashnode.cpp \ + $$PWD/qsgpathsimplifier.cpp + + + + + + diff --git a/src/quick/scenegraph/util/qsgareaallocator.cpp b/src/quick/scenegraph/util/qsgareaallocator.cpp new file mode 100644 index 0000000000..c5171f1c93 --- /dev/null +++ b/src/quick/scenegraph/util/qsgareaallocator.cpp @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgareaallocator_p.h" + +#include <QtCore/qglobal.h> +#include <QtCore/qrect.h> +#include <QtCore/qpoint.h> + +QT_BEGIN_NAMESPACE + +namespace +{ + enum SplitType + { + VerticalSplit, + HorizontalSplit + }; + + static const int maxMargin = 2; +} + +struct QSGAreaAllocatorNode +{ + QSGAreaAllocatorNode(QSGAreaAllocatorNode *parent); + ~QSGAreaAllocatorNode(); + inline bool isLeaf(); + + QSGAreaAllocatorNode *parent; + QSGAreaAllocatorNode *left; + QSGAreaAllocatorNode *right; + int split; // only valid for inner nodes. + SplitType splitType; + bool isOccupied; // only valid for leaf nodes. +}; + +QSGAreaAllocatorNode::QSGAreaAllocatorNode(QSGAreaAllocatorNode *parent) + : parent(parent) + , left(0) + , right(0) + , isOccupied(false) +{ +} + +QSGAreaAllocatorNode::~QSGAreaAllocatorNode() +{ + delete left; + delete right; +} + +bool QSGAreaAllocatorNode::isLeaf() +{ + Q_ASSERT((left != 0) == (right != 0)); + return !left; +} + + +QSGAreaAllocator::QSGAreaAllocator(const QSize &size) : m_size(size) +{ + m_root = new QSGAreaAllocatorNode(0); +} + +QSGAreaAllocator::~QSGAreaAllocator() +{ + delete m_root; +} + +QRect QSGAreaAllocator::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 QSGAreaAllocator::deallocate(const QRect &rect) +{ + return deallocateInNode(rect.topLeft(), m_root); +} + +bool QSGAreaAllocator::allocateInNode(const QSize &size, QPoint &result, const QRect ¤tRect, QSGAreaAllocatorNode *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 QSGAreaAllocatorNode(node); + node->right = new QSGAreaAllocatorNode(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 QSGAreaAllocator::deallocateInNode(const QPoint &pos, QSGAreaAllocatorNode *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 QSGAreaAllocator::mergeNodeWithNeighbors(QSGAreaAllocatorNode *node) +{ + bool done = false; + QSGAreaAllocatorNode *parent = 0; + QSGAreaAllocatorNode *current = 0; + QSGAreaAllocatorNode *sibling; + while (!done) { + Q_ASSERT(node->isLeaf()); + Q_ASSERT(!node->isOccupied); + if (node->parent == 0) + return; // No neighbours. + + 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 neighbour. + 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); + + QSGAreaAllocatorNode *neighbor = parent->left; + while (neighbor->right && neighbor->splitType == splitType) + neighbor = neighbor->right; + + if (neighbor->isLeaf() && neighbor->parent->splitType == splitType && !neighbor->isOccupied) { + // Left neighbour can be merged. + parent->split = neighbor->parent->split; + + parent = neighbor->parent; + sibling = neighbor == parent->left ? parent->right : parent->left; + QSGAreaAllocatorNode **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 neighbour. + 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); + + QSGAreaAllocatorNode *neighbor = parent->right; + while (neighbor->left && neighbor->splitType == splitType) + neighbor = neighbor->left; + + if (neighbor->isLeaf() && neighbor->parent->splitType == splitType && !neighbor->isOccupied) { + // Right neighbour can be merged. + parent->split = neighbor->parent->split; + + parent = neighbor->parent; + sibling = neighbor == parent->left ? parent->right : parent->left; + QSGAreaAllocatorNode **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) +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgareaallocator_p.h b/src/quick/scenegraph/util/qsgareaallocator_p.h new file mode 100644 index 0000000000..be26046865 --- /dev/null +++ b/src/quick/scenegraph/util/qsgareaallocator_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AREAALLOCATOR_H +#define AREAALLOCATOR_H + +#include <QtQuick/qtquickglobal.h> +#include <QtCore/qsize.h> + +QT_BEGIN_NAMESPACE + +class QRect; +class QPoint; +struct QSGAreaAllocatorNode; +class Q_QUICK_EXPORT QSGAreaAllocator +{ +public: + QSGAreaAllocator(const QSize &size); + ~QSGAreaAllocator(); + + 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, QSGAreaAllocatorNode *node); + bool deallocateInNode(const QPoint &pos, QSGAreaAllocatorNode *node); + void mergeNodeWithNeighbors(QSGAreaAllocatorNode *node); + + QSGAreaAllocatorNode *m_root; + QSize m_size; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp new file mode 100644 index 0000000000..d1b0445ee0 --- /dev/null +++ b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp @@ -0,0 +1,805 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdistancefieldutil_p.h" + +#include <qmath.h> +#include <private/qsgpathsimplifier_p.h> +#include <private/qsgadaptationlayer_p.h> +#include <QtGui/private/qopenglengineshadersource_p.h> +#include <QtQuick/private/qsgcontext_p.h> + +QT_BEGIN_NAMESPACE + +static float defaultThresholdFunc(float glyphScale) +{ + static float base = qgetenv("QT_DF_BASE").isEmpty() ? 0.5f : qgetenv("QT_DF_BASE").toFloat(); + static float baseDev = qgetenv("QT_DF_BASEDEVIATION").isEmpty() ? 0.065f : qgetenv("QT_DF_BASEDEVIATION").toFloat(); + static float devScaleMin = qgetenv("QT_DF_SCALEFORMAXDEV").isEmpty() ? 0.15f : qgetenv("QT_DF_SCALEFORMAXDEV").toFloat(); + static float devScaleMax = qgetenv("QT_DF_SCALEFORNODEV").isEmpty() ? 0.3f : qgetenv("QT_DF_SCALEFORNODEV").toFloat(); + return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev); +} + +static float defaultAntialiasingSpreadFunc(float glyphScale) +{ + static float range = qgetenv("QT_DF_RANGE").isEmpty() ? 0.06f : qgetenv("QT_DF_RANGE").toFloat(); + return range / glyphScale; +} + +namespace +{ + enum FillHDir + { + LeftToRight, + RightToLeft + }; + + enum FillVDir + { + TopDown, + BottomUp + }; + + enum FillClip + { + NoClip, + Clip + }; +} + +template <FillClip clip, FillHDir dir> +inline void fillLine(qint32 *, int, int, int, qint32, qint32) +{ +} + +template <> +inline void fillLine<Clip, LeftToRight>(qint32 *line, int width, int lx, int rx, qint32 d, qint32 dd) +{ + int fromX = qMax(0, lx >> 8); + int toX = qMin(width, rx >> 8); + int x = toX - fromX; + if (x <= 0) + return; + qint32 val = d + (((fromX << 8) + 0xff - lx) * dd >> 8); + line += fromX; + do { + *line = abs(val) < abs(*line) ? val : *line; + val += dd; + ++line; + } while (--x); +} + +template <> +inline void fillLine<Clip, RightToLeft>(qint32 *line, int width, int lx, int rx, qint32 d, qint32 dd) +{ + int fromX = qMax(0, lx >> 8); + int toX = qMin(width, rx >> 8); + int x = toX - fromX; + if (x <= 0) + return; + qint32 val = d + (((toX << 8) + 0xff - rx) * dd >> 8); + line += toX; + do { + val -= dd; + --line; + *line = abs(val) < abs(*line) ? val : *line; + } while (--x); +} + +template <> +inline void fillLine<NoClip, LeftToRight>(qint32 *line, int, int lx, int rx, qint32 d, qint32 dd) +{ + int fromX = lx >> 8; + int toX = rx >> 8; + int x = toX - fromX; + if (x <= 0) + return; + qint32 val = d + ((~lx & 0xff) * dd >> 8); + line += fromX; + do { + *line = abs(val) < abs(*line) ? val : *line; + val += dd; + ++line; + } while (--x); +} + +template <> +inline void fillLine<NoClip, RightToLeft>(qint32 *line, int, int lx, int rx, qint32 d, qint32 dd) +{ + int fromX = lx >> 8; + int toX = rx >> 8; + int x = toX - fromX; + if (x <= 0) + return; + qint32 val = d + ((~rx & 0xff) * dd >> 8); + line += toX; + do { + val -= dd; + --line; + *line = abs(val) < abs(*line) ? val : *line; + } while (--x); +} + +template <FillClip clip, FillVDir vDir, FillHDir hDir> +inline void fillLines(qint32 *bits, int width, int height, int upperY, int lowerY, + int &lx, int ldx, int &rx, int rdx, qint32 &d, qint32 ddy, qint32 ddx) +{ + Q_UNUSED(height); + Q_ASSERT(upperY < lowerY); + int y = lowerY - upperY; + if (vDir == TopDown) { + qint32 *line = bits + upperY * width; + do { + fillLine<clip, hDir>(line, width, lx, rx, d, ddx); + lx += ldx; + d += ddy; + rx += rdx; + line += width; + } while (--y); + } else { + qint32 *line = bits + lowerY * width; + do { + lx -= ldx; + d -= ddy; + rx -= rdx; + line -= width; + fillLine<clip, hDir>(line, width, lx, rx, d, ddx); + } while (--y); + } +} + +template <FillClip clip> +void drawTriangle(qint32 *bits, int width, int height, const QPoint *center, + const QPoint *v1, const QPoint *v2, qint32 value) +{ + const int y1 = clip == Clip ? qBound(0, v1->y() >> 8, height) : v1->y() >> 8; + const int y2 = clip == Clip ? qBound(0, v2->y() >> 8, height) : v2->y() >> 8; + const int yC = clip == Clip ? qBound(0, center->y() >> 8, height) : center->y() >> 8; + + const int v1Frac = clip == Clip ? (y1 << 8) + 0xff - v1->y() : ~v2->y() & 0xff; + const int v2Frac = clip == Clip ? (y2 << 8) + 0xff - v2->y() : ~v1->y() & 0xff; + const int centerFrac = clip == Clip ? (yC << 8) + 0xff - center->y() : ~center->y() & 0xff; + + int dx1 = 0, x1 = 0, dx2 = 0, x2 = 0; + qint32 dd1, d1, dd2, d2; + if (v1->y() != center->y()) { + dx1 = ((v1->x() - center->x()) << 8) / (v1->y() - center->y()); + x1 = center->x() + centerFrac * (v1->x() - center->x()) / (v1->y() - center->y()); + } + if (v2->y() != center->y()) { + dx2 = ((v2->x() - center->x()) << 8) / (v2->y() - center->y()); + x2 = center->x() + centerFrac * (v2->x() - center->x()) / (v2->y() - center->y()); + } + + const qint32 div = (v2->x() - center->x()) * (v1->y() - center->y()) + - (v2->y() - center->y()) * (v1->x() - center->x()); + const qint32 dd = div ? qint32((qint64(value * (v1->y() - v2->y())) << 8) / div) : 0; + + if (y2 < yC) { + if (y1 < yC) { + // Center at the bottom. + if (y2 < y1) { + // y2 < y1 < yC + // Long right edge. + d1 = centerFrac * value / (v1->y() - center->y()); + dd1 = ((value << 8) / (v1->y() - center->y())); + fillLines<clip, BottomUp, LeftToRight>(bits, width, height, y1, yC, x1, dx1, + x2, dx2, d1, dd1, dd); + dx1 = ((v1->x() - v2->x()) << 8) / (v1->y() - v2->y()); + x1 = v1->x() + v1Frac * (v1->x() - v2->x()) / (v1->y() - v2->y()); + fillLines<clip, BottomUp, LeftToRight>(bits, width, height, y2, y1, x1, dx1, + x2, dx2, value, 0, dd); + } else { + // y1 <= y2 < yC + // Long left edge. + d2 = centerFrac * value / (v2->y() - center->y()); + dd2 = ((value << 8) / (v2->y() - center->y())); + fillLines<clip, BottomUp, RightToLeft>(bits, width, height, y2, yC, x1, dx1, + x2, dx2, d2, dd2, dd); + if (y1 != y2) { + dx2 = ((v1->x() - v2->x()) << 8) / (v1->y() - v2->y()); + x2 = v2->x() + v2Frac * (v1->x() - v2->x()) / (v1->y() - v2->y()); + fillLines<clip, BottomUp, RightToLeft>(bits, width, height, y1, y2, x1, dx1, + x2, dx2, value, 0, dd); + } + } + } else { + // y2 < yC <= y1 + // Center to the right. + int dx = ((v1->x() - v2->x()) << 8) / (v1->y() - v2->y()); + int xUp, xDn; + xUp = xDn = v2->x() + (clip == Clip ? (yC << 8) + 0xff - v2->y() + : (center->y() | 0xff) - v2->y()) + * (v1->x() - v2->x()) / (v1->y() - v2->y()); + fillLines<clip, BottomUp, LeftToRight>(bits, width, height, y2, yC, xUp, dx, + x2, dx2, value, 0, dd); + if (yC != y1) + fillLines<clip, TopDown, LeftToRight>(bits, width, height, yC, y1, xDn, dx, + x1, dx1, value, 0, dd); + } + } else { + if (y1 < yC) { + // y1 < yC <= y2 + // Center to the left. + int dx = ((v1->x() - v2->x()) << 8) / (v1->y() - v2->y()); + int xUp, xDn; + xUp = xDn = v1->x() + (clip == Clip ? (yC << 8) + 0xff - v1->y() + : (center->y() | 0xff) - v1->y()) + * (v1->x() - v2->x()) / (v1->y() - v2->y()); + fillLines<clip, BottomUp, RightToLeft>(bits, width, height, y1, yC, x1, dx1, + xUp, dx, value, 0, dd); + if (yC != y2) + fillLines<clip, TopDown, RightToLeft>(bits, width, height, yC, y2, x2, dx2, + xDn, dx, value, 0, dd); + } else { + // Center at the top. + if (y2 < y1) { + // yC <= y2 < y1 + // Long right edge. + if (yC != y2) { + d2 = centerFrac * value / (v2->y() - center->y()); + dd2 = ((value << 8) / (v2->y() - center->y())); + fillLines<clip, TopDown, LeftToRight>(bits, width, height, yC, y2, x2, dx2, + x1, dx1, d2, dd2, dd); + } + dx2 = ((v1->x() - v2->x()) << 8) / (v1->y() - v2->y()); + x2 = v2->x() + v2Frac * (v1->x() - v2->x()) / (v1->y() - v2->y()); + fillLines<clip, TopDown, LeftToRight>(bits, width, height, y2, y1, x2, dx2, + x1, dx1, value, 0, dd); + } else { + // Long left edge. + // yC <= y1 <= y2 + if (yC != y1) { + d1 = centerFrac * value / (v1->y() - center->y()); + dd1 = ((value << 8) / (v1->y() - center->y())); + fillLines<clip, TopDown, RightToLeft>(bits, width, height, yC, y1, x2, dx2, + x1, dx1, d1, dd1, dd); + } + if (y1 != y2) { + dx1 = ((v1->x() - v2->x()) << 8) / (v1->y() - v2->y()); + x1 = v1->x() + v1Frac * (v1->x() - v2->x()) / (v1->y() - v2->y()); + fillLines<clip, TopDown, RightToLeft>(bits, width, height, y1, y2, x2, dx2, + x1, dx1, value, 0, dd); + } + } + } + } +} + +template <FillClip clip> +void drawRectangle(qint32 *bits, int width, int height, + const QPoint *int1, const QPoint *center1, const QPoint *ext1, + const QPoint *int2, const QPoint *center2, const QPoint *ext2, + qint32 extValue) +{ + if (center1->y() > center2->y()) { + qSwap(center1, center2); + qSwap(int1, ext2); + qSwap(ext1, int2); + extValue = -extValue; + } + + Q_ASSERT(ext1->x() - center1->x() == center1->x() - int1->x()); + Q_ASSERT(ext1->y() - center1->y() == center1->y() - int1->y()); + Q_ASSERT(ext2->x() - center2->x() == center2->x() - int2->x()); + Q_ASSERT(ext2->y() - center2->y() == center2->y() - int2->y()); + + const int yc1 = clip == Clip ? qBound(0, center1->y() >> 8, height) : center1->y() >> 8; + const int yc2 = clip == Clip ? qBound(0, center2->y() >> 8, height) : center2->y() >> 8; + const int yi1 = clip == Clip ? qBound(0, int1->y() >> 8, height) : int1->y() >> 8; + const int yi2 = clip == Clip ? qBound(0, int2->y() >> 8, height) : int2->y() >> 8; + const int ye1 = clip == Clip ? qBound(0, ext1->y() >> 8, height) : ext1->y() >> 8; + const int ye2 = clip == Clip ? qBound(0, ext2->y() >> 8, height) : ext2->y() >> 8; + + const int center1Frac = clip == Clip ? (yc1 << 8) + 0xff - center1->y() : ~center1->y() & 0xff; + const int center2Frac = clip == Clip ? (yc2 << 8) + 0xff - center2->y() : ~center2->y() & 0xff; + const int int1Frac = clip == Clip ? (yi1 << 8) + 0xff - int1->y() : ~int1->y() & 0xff; + const int ext1Frac = clip == Clip ? (ye1 << 8) + 0xff - ext1->y() : ~ext1->y() & 0xff; + + int dxC = 0, dxE = 0; // cap slope, edge slope + qint32 ddC = 0; + if (ext1->y() != int1->y()) { + dxC = ((ext1->x() - int1->x()) << 8) / (ext1->y() - int1->y()); + ddC = (extValue << 9) / (ext1->y() - int1->y()); + } + if (ext1->y() != ext2->y()) + dxE = ((ext1->x() - ext2->x()) << 8) / (ext1->y() - ext2->y()); + + const qint32 div = (ext1->x() - int1->x()) * (ext2->y() - int1->y()) + - (ext1->y() - int1->y()) * (ext2->x() - int1->x()); + const qint32 dd = div ? qint32((qint64(extValue * (ext2->y() - ext1->y())) << 9) / div) : 0; + + int xe1, xe2, xc1, xc2; + qint32 d; + + qint32 intValue = -extValue; + + if (center2->x() < center1->x()) { + // Leaning to the right. '/' + if (int1->y() < ext2->y()) { + // Mostly vertical. + Q_ASSERT(ext1->y() != ext2->y()); + xe1 = ext1->x() + ext1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y()); + xe2 = int1->x() + int1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y()); + if (ye1 != yi1) { + xc2 = center1->x() + center1Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y()); + xc2 += (ye1 - yc1) * dxC; + fillLines<clip, TopDown, LeftToRight>(bits, width, height, ye1, yi1, xe1, dxE, + xc2, dxC, extValue, 0, dd); + } + if (yi1 != ye2) + fillLines<clip, TopDown, LeftToRight>(bits, width, height, yi1, ye2, xe1, dxE, + xe2, dxE, extValue, 0, dd); + if (ye2 != yi2) { + xc1 = center2->x() + center2Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y()); + xc1 += (ye2 - yc2) * dxC; + fillLines<clip, TopDown, RightToLeft>(bits, width, height, ye2, yi2, xc1, dxC, + xe2, dxE, intValue, 0, dd); + } + } else { + // Mostly horizontal. + Q_ASSERT(ext1->y() != int1->y()); + xc1 = center2->x() + center2Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y()); + xc2 = center1->x() + center1Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y()); + xc1 += (ye2 - yc2) * dxC; + xc2 += (ye1 - yc1) * dxC; + if (ye1 != ye2) { + xe1 = ext1->x() + ext1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y()); + fillLines<clip, TopDown, LeftToRight>(bits, width, height, ye1, ye2, xe1, dxE, + xc2, dxC, extValue, 0, dd); + } + if (ye2 != yi1) { + d = (clip == Clip ? (ye2 << 8) + 0xff - center2->y() + : (ext2->y() | 0xff) - center2->y()) + * 2 * extValue / (ext1->y() - int1->y()); + fillLines<clip, TopDown, LeftToRight>(bits, width, height, ye2, yi1, xc1, dxC, + xc2, dxC, d, ddC, dd); + } + if (yi1 != yi2) { + xe2 = int1->x() + int1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y()); + fillLines<clip, TopDown, RightToLeft>(bits, width, height, yi1, yi2, xc1, dxC, + xe2, dxE, intValue, 0, dd); + } + } + } else { + // Leaning to the left. '\' + if (ext1->y() < int2->y()) { + // Mostly vertical. + Q_ASSERT(ext1->y() != ext2->y()); + xe1 = ext1->x() + ext1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y()); + xe2 = int1->x() + int1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y()); + if (yi1 != ye1) { + xc1 = center1->x() + center1Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y()); + xc1 += (yi1 - yc1) * dxC; + fillLines<clip, TopDown, RightToLeft>(bits, width, height, yi1, ye1, xc1, dxC, + xe2, dxE, intValue, 0, dd); + } + if (ye1 != yi2) + fillLines<clip, TopDown, RightToLeft>(bits, width, height, ye1, yi2, xe1, dxE, + xe2, dxE, intValue, 0, dd); + if (yi2 != ye2) { + xc2 = center2->x() + center2Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y()); + xc2 += (yi2 - yc2) * dxC; + fillLines<clip, TopDown, LeftToRight>(bits, width, height, yi2, ye2, xe1, dxE, + xc2, dxC, extValue, 0, dd); + } + } else { + // Mostly horizontal. + Q_ASSERT(ext1->y() != int1->y()); + xc1 = center1->x() + center1Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y()); + xc2 = center2->x() + center2Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y()); + xc1 += (yi1 - yc1) * dxC; + xc2 += (yi2 - yc2) * dxC; + if (yi1 != yi2) { + xe2 = int1->x() + int1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y()); + fillLines<clip, TopDown, RightToLeft>(bits, width, height, yi1, yi2, xc1, dxC, + xe2, dxE, intValue, 0, dd); + } + if (yi2 != ye1) { + d = (clip == Clip ? (yi2 << 8) + 0xff - center2->y() + : (int2->y() | 0xff) - center2->y()) + * 2 * extValue / (ext1->y() - int1->y()); + fillLines<clip, TopDown, RightToLeft>(bits, width, height, yi2, ye1, xc1, dxC, + xc2, dxC, d, ddC, dd); + } + if (ye1 != ye2) { + xe1 = ext1->x() + ext1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y()); + fillLines<clip, TopDown, LeftToRight>(bits, width, height, ye1, ye2, xe1, dxE, + xc2, dxC, extValue, 0, dd); + } + } + } +} + +static void drawPolygons(qint32 *bits, int width, int height, const QPoint *vertices, + const quint32 *indices, int indexCount, qint32 value) +{ + Q_ASSERT(indexCount != 0); + Q_ASSERT(height <= 128); + QVarLengthArray<quint8, 16> scans[128]; + int first = 0; + for (int i = 1; i < indexCount; ++i) { + quint32 idx1 = indices[i - 1]; + quint32 idx2 = indices[i]; + Q_ASSERT(idx1 != quint32(-1)); + if (idx2 == quint32(-1)) { + idx2 = indices[first]; + Q_ASSERT(idx2 != quint32(-1)); + first = ++i; + } + const QPoint *v1 = &vertices[idx1]; + const QPoint *v2 = &vertices[idx2]; + if (v2->y() < v1->y()) + qSwap(v1, v2); + int fromY = qMax(0, v1->y() >> 8); + int toY = qMin(height, v2->y() >> 8); + if (fromY >= toY) + continue; + int dx = ((v2->x() - v1->x()) << 8) / (v2->y() - v1->y()); + int x = v1->x() + ((fromY << 8) + 0xff - v1->y()) * (v2->x() - v1->x()) / (v2->y() - v1->y()); + for (int y = fromY; y < toY; ++y) { + quint32 c = quint32(x >> 8); + if (c < quint32(width)) + scans[y].append(quint8(c)); + x += dx; + } + } + for (int i = 0; i < height; ++i) { + quint8 *scanline = scans[i].data(); + int size = scans[i].size(); + for (int j = 1; j < size; ++j) { + int k = j; + quint8 value = scanline[k]; + for (; k != 0 && value < scanline[k - 1]; --k) + scanline[k] = scanline[k - 1]; + scanline[k] = value; + } + qint32 *line = bits + i * width; + int j = 0; + for (; j + 1 < size; j += 2) { + for (quint8 x = scanline[j]; x < scanline[j + 1]; ++x) + line[x] = value; + } + if (j < size) { + for (int x = scanline[j]; x < width; ++x) + line[x] = value; + } + } +} + +static QImage makeDistanceField(int imgSize, const QPainterPath &path, int dfScale, int offs) +{ + QImage image(imgSize, imgSize, QImage::Format_Indexed8); + + if (path.isEmpty()) { + image.fill(0); + return image; + } + + QTransform transform; + transform.translate(offs, offs); + transform.scale(qreal(1) / dfScale, qreal(1) / dfScale); + + QDataBuffer<quint32> pathIndices(0); + QDataBuffer<QPoint> pathVertices(0); + qSimplifyPath(path, pathVertices, pathIndices, transform); + + const qint32 interiorColor = -0x7f80; // 8:8 signed format, -127.5 + const qint32 exteriorColor = 0x7f80; // 8:8 signed format, 127.5 + + QScopedArrayPointer<qint32> bits(new qint32[imgSize * imgSize]); + for (int i = 0; i < imgSize * imgSize; ++i) + bits[i] = exteriorColor; + + const qreal angleStep = qreal(15 * 3.141592653589793238 / 180); + const QPoint rotation(qRound(cos(angleStep) * 0x4000), + qRound(sin(angleStep) * 0x4000)); // 2:14 signed + + const quint32 *indices = pathIndices.data(); + QVarLengthArray<QPoint> normals; + QVarLengthArray<QPoint> vertices; + QVarLengthArray<bool> isConvex; + QVarLengthArray<bool> needsClipping; + + drawPolygons(bits.data(), imgSize, imgSize, pathVertices.data(), indices, pathIndices.size(), + interiorColor); + + int index = 0; + + while (index < pathIndices.size()) { + normals.clear(); + vertices.clear(); + needsClipping.clear(); + + // Find end of polygon. + int end = index; + while (indices[end] != quint32(-1)) + ++end; + + // Calculate vertex normals. + for (int next = index, prev = end - 1; next < end; prev = next++) { + quint32 fromVertexIndex = indices[prev]; + quint32 toVertexIndex = indices[next]; + + const QPoint &from = pathVertices.at(fromVertexIndex); + const QPoint &to = pathVertices.at(toVertexIndex); + + QPoint n(to.y() - from.y(), from.x() - to.x()); + if (n.x() == 0 && n.y() == 0) + continue; + int scale = qRound((offs << 16) / sqrt(qreal(n.x() * n.x() + n.y() * n.y()))); // 8:16 + n.rx() = n.x() * scale >> 8; + n.ry() = n.y() * scale >> 8; + normals.append(n); + QPoint v(to.x() + 0x7f, to.y() + 0x7f); + vertices.append(v); + needsClipping.append((to.x() < offs << 8) || (to.x() >= (imgSize - offs) << 8) + || (to.y() < offs << 8) || (to.y() >= (imgSize - offs) << 8)); + } + + isConvex.resize(normals.count()); + for (int next = 0, prev = normals.count() - 1; next < normals.count(); prev = next++) { + isConvex[prev] = normals.at(prev).x() * normals.at(next).y() + - normals.at(prev).y() * normals.at(next).x() < 0; + } + + // Draw quads. + for (int next = 0, prev = normals.count() - 1; next < normals.count(); prev = next++) { + QPoint n = normals.at(next); + QPoint intPrev = vertices.at(prev); + QPoint extPrev = vertices.at(prev); + QPoint intNext = vertices.at(next); + QPoint extNext = vertices.at(next); + + extPrev.rx() -= n.x(); + extPrev.ry() -= n.y(); + intPrev.rx() += n.x(); + intPrev.ry() += n.y(); + extNext.rx() -= n.x(); + extNext.ry() -= n.y(); + intNext.rx() += n.x(); + intNext.ry() += n.y(); + + if (needsClipping[prev] || needsClipping[next]) { + drawRectangle<Clip>(bits.data(), imgSize, imgSize, + &intPrev, &vertices.at(prev), &extPrev, + &intNext, &vertices.at(next), &extNext, + exteriorColor); + } else { + drawRectangle<NoClip>(bits.data(), imgSize, imgSize, + &intPrev, &vertices.at(prev), &extPrev, + &intNext, &vertices.at(next), &extNext, + exteriorColor); + } + + if (isConvex.at(prev)) { + QPoint p = extPrev; + if (needsClipping[prev]) { + for (;;) { + QPoint rn((n.x() * rotation.x() - n.y() * rotation.y()) >> 14, + (n.y() * rotation.x() + n.x() * rotation.y()) >> 14); + n = rn; + if (n.x() * normals.at(prev).y() - n.y() * normals.at(prev).x() <= 0) { + p.rx() = vertices.at(prev).x() - normals.at(prev).x(); + p.ry() = vertices.at(prev).y() - normals.at(prev).y(); + drawTriangle<Clip>(bits.data(), imgSize, imgSize, &vertices.at(prev), + &extPrev, &p, exteriorColor); + break; + } + + p.rx() = vertices.at(prev).x() - n.x(); + p.ry() = vertices.at(prev).y() - n.y(); + drawTriangle<Clip>(bits.data(), imgSize, imgSize, &vertices.at(prev), + &extPrev, &p, exteriorColor); + extPrev = p; + } + } else { + for (;;) { + QPoint rn((n.x() * rotation.x() - n.y() * rotation.y()) >> 14, + (n.y() * rotation.x() + n.x() * rotation.y()) >> 14); + n = rn; + if (n.x() * normals.at(prev).y() - n.y() * normals.at(prev).x() <= 0) { + p.rx() = vertices.at(prev).x() - normals.at(prev).x(); + p.ry() = vertices.at(prev).y() - normals.at(prev).y(); + drawTriangle<NoClip>(bits.data(), imgSize, imgSize, &vertices.at(prev), + &extPrev, &p, exteriorColor); + break; + } + + p.rx() = vertices.at(prev).x() - n.x(); + p.ry() = vertices.at(prev).y() - n.y(); + drawTriangle<NoClip>(bits.data(), imgSize, imgSize, &vertices.at(prev), + &extPrev, &p, exteriorColor); + extPrev = p; + } + } + } else { + QPoint p = intPrev; + if (needsClipping[prev]) { + for (;;) { + QPoint rn((n.x() * rotation.x() + n.y() * rotation.y()) >> 14, + (n.y() * rotation.x() - n.x() * rotation.y()) >> 14); + n = rn; + if (n.x() * normals.at(prev).y() - n.y() * normals.at(prev).x() >= 0) { + p.rx() = vertices.at(prev).x() + normals.at(prev).x(); + p.ry() = vertices.at(prev).y() + normals.at(prev).y(); + drawTriangle<Clip>(bits.data(), imgSize, imgSize, &vertices.at(prev), + &p, &intPrev, interiorColor); + break; + } + + p.rx() = vertices.at(prev).x() + n.x(); + p.ry() = vertices.at(prev).y() + n.y(); + drawTriangle<Clip>(bits.data(), imgSize, imgSize, &vertices.at(prev), + &p, &intPrev, interiorColor); + intPrev = p; + } + } else { + for (;;) { + QPoint rn((n.x() * rotation.x() + n.y() * rotation.y()) >> 14, + (n.y() * rotation.x() - n.x() * rotation.y()) >> 14); + n = rn; + if (n.x() * normals.at(prev).y() - n.y() * normals.at(prev).x() >= 0) { + p.rx() = vertices.at(prev).x() + normals.at(prev).x(); + p.ry() = vertices.at(prev).y() + normals.at(prev).y(); + drawTriangle<NoClip>(bits.data(), imgSize, imgSize, &vertices.at(prev), + &p, &intPrev, interiorColor); + break; + } + + p.rx() = vertices.at(prev).x() + n.x(); + p.ry() = vertices.at(prev).y() + n.y(); + drawTriangle<NoClip>(bits.data(), imgSize, imgSize, &vertices.at(prev), + &p, &intPrev, interiorColor); + intPrev = p; + } + } + } + } + + index = end + 1; + } + + const qint32 *inLine = bits.data(); + uchar *outLine = image.bits(); + int padding = image.bytesPerLine() - image.width(); + for (int y = 0; y < imgSize; ++y) { + for (int x = 0; x < imgSize; ++x, ++inLine, ++outLine) + *outLine = uchar((0x7f80 - *inLine) >> 8); + outLine += padding; + } + + return image; +} + +bool qt_fontHasNarrowOutlines(const QRawFont &f) +{ + QRawFont font = f; + font.setPixelSize(QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE); + Q_ASSERT(font.isValid()); + + QVector<quint32> glyphIndices = font.glyphIndexesForString(QLatin1String("O")); + if (glyphIndices.size() < 1) + return false; + + QImage im = font.alphaMapForGlyph(glyphIndices.at(0), QRawFont::PixelAntialiasing); + if (im.isNull()) + return false; + + int minHThick = 999; + int minVThick = 999; + + int thick = 0; + bool in = false; + int y = (im.height() + 1) / 2; + for (int x = 0; x < im.width(); ++x) { + int a = qAlpha(im.pixel(x, y)); + if (a > 127) { + in = true; + ++thick; + } else if (in) { + in = false; + minHThick = qMin(minHThick, thick); + thick = 0; + } + } + + thick = 0; + in = false; + int x = (im.width() + 1) / 2; + for (int y = 0; y < im.height(); ++y) { + int a = qAlpha(im.pixel(x, y)); + if (a > 127) { + in = true; + ++thick; + } else if (in) { + in = false; + minVThick = qMin(minVThick, thick); + thick = 0; + } + } + + return minHThick == 1 || minVThick == 1; +} + +QImage qt_renderDistanceFieldGlyph(const QRawFont &font, glyph_t glyph, bool doubleResolution) +{ + QRawFont renderFont = font; + renderFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(doubleResolution) * QT_DISTANCEFIELD_SCALE(doubleResolution)); + + QPainterPath path = renderFont.pathForGlyph(glyph); + path.translate(-path.boundingRect().topLeft()); + path.setFillRule(Qt::WindingFill); + + QImage im = makeDistanceField(QT_DISTANCEFIELD_TILESIZE(doubleResolution), + path, + QT_DISTANCEFIELD_SCALE(doubleResolution), + QT_DISTANCEFIELD_RADIUS(doubleResolution) / QT_DISTANCEFIELD_SCALE(doubleResolution)); + return im; +} + +QSGDistanceFieldGlyphCacheManager::QSGDistanceFieldGlyphCacheManager(QSGContext *c) + : sgCtx(c) + , m_threshold_func(defaultThresholdFunc) + , m_antialiasingSpread_func(defaultAntialiasingSpreadFunc) +{ +#ifndef QT_OPENGL_ES + m_defaultAntialiasingMode = QSGGlyphNode::HighQualitySubPixelAntialiasing; +#else + m_defaultAntialiasingMode = QSGGlyphNode::GrayAntialiasing; +#endif +} + +QSGDistanceFieldGlyphCacheManager::~QSGDistanceFieldGlyphCacheManager() +{ + qDeleteAll(m_caches.values()); +} + +QSGDistanceFieldGlyphCache *QSGDistanceFieldGlyphCacheManager::cache(const QRawFont &font) +{ + QRawFontPrivate *fontD = QRawFontPrivate::get(font); + QHash<QFontEngine *, QSGDistanceFieldGlyphCache *>::iterator cache = m_caches.find(fontD->fontEngine); + if (cache == m_caches.end()) + cache = m_caches.insert(fontD->fontEngine, sgCtx->createDistanceFieldGlyphCache(font)); + return cache.value(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil_p.h b/src/quick/scenegraph/util/qsgdistancefieldutil_p.h new file mode 100644 index 0000000000..bc28a4d9e1 --- /dev/null +++ b/src/quick/scenegraph/util/qsgdistancefieldutil_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGDISTANCEFIELDUTIL_H +#define QSGDISTANCEFIELDUTIL_H + +#include <qrawfont.h> +#include <private/qfontengine_p.h> +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +#define QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE 54 +#define QT_DISTANCEFIELD_DEFAULT_TILESIZE 64 +#define QT_DISTANCEFIELD_DEFAULT_SCALE 16 +#define QT_DISTANCEFIELD_DEFAULT_RADIUS 80 +#define QT_DISTANCEFIELD_HIGHGLYPHCOUNT 2000 + +#define QT_DISTANCEFIELD_BASEFONTSIZE(NarrowOutlineFont) \ + (NarrowOutlineFont ? QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE * 2 : \ + QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE) +#define QT_DISTANCEFIELD_TILESIZE(NarrowOutlineFont) \ + (NarrowOutlineFont ? QT_DISTANCEFIELD_DEFAULT_TILESIZE * 2 : \ + QT_DISTANCEFIELD_DEFAULT_TILESIZE) +#define QT_DISTANCEFIELD_SCALE(NarrowOutlineFont) \ + (NarrowOutlineFont ? QT_DISTANCEFIELD_DEFAULT_SCALE / 2 : \ + QT_DISTANCEFIELD_DEFAULT_SCALE) +#define QT_DISTANCEFIELD_RADIUS(NarrowOutlineFont) \ + (NarrowOutlineFont ? QT_DISTANCEFIELD_DEFAULT_RADIUS / 2 : \ + QT_DISTANCEFIELD_DEFAULT_RADIUS) + + +typedef float (*ThresholdFunc)(float glyphScale); +typedef float (*AntialiasingSpreadFunc)(float glyphScale); + +bool qt_fontHasNarrowOutlines(const QRawFont &f); +QImage qt_renderDistanceFieldGlyph(const QRawFont &font, glyph_t glyph, bool doubleResolution); + + +class QOpenGLShaderProgram; +class QSGDistanceFieldGlyphCache; +class QSGContext; + +class Q_QUICK_EXPORT QSGDistanceFieldGlyphCacheManager +{ +public: + QSGDistanceFieldGlyphCacheManager(QSGContext *c); + ~QSGDistanceFieldGlyphCacheManager(); + + QSGDistanceFieldGlyphCache *cache(const QRawFont &font); + + QSGGlyphNode::AntialiasingMode defaultAntialiasingMode() const { return m_defaultAntialiasingMode; } + void setDefaultAntialiasingMode(QSGGlyphNode::AntialiasingMode mode) { m_defaultAntialiasingMode = mode; } + + ThresholdFunc thresholdFunc() const { return m_threshold_func; } + void setThresholdFunc(ThresholdFunc func) { m_threshold_func = func; } + + AntialiasingSpreadFunc antialiasingSpreadFunc() const { return m_antialiasingSpread_func; } + void setAntialiasingSpreadFunc(AntialiasingSpreadFunc func) { m_antialiasingSpread_func = func; } + +private: + QHash<QFontEngine *, QSGDistanceFieldGlyphCache *> m_caches; + + QSGContext *sgCtx; + + QSGGlyphNode::AntialiasingMode m_defaultAntialiasingMode; + ThresholdFunc m_threshold_func; + AntialiasingSpreadFunc m_antialiasingSpread_func; +}; + +QT_END_NAMESPACE + +#endif // QSGDISTANCEFIELDUTIL_H diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp new file mode 100644 index 0000000000..b8c93bab8b --- /dev/null +++ b/src/quick/scenegraph/util/qsgengine.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgengine.h" + +#include <QtQuick/qquickcanvas.h> + +#include <private/qobject_p.h> +#include <QtGui/QColor> + +QT_BEGIN_NAMESPACE + +class QSGEnginePrivate : public QObjectPrivate +{ +public: + QSGEnginePrivate() + : canvas(0) + { + } + + QQuickCanvas *canvas; +}; + +/*! + \class QSGEngine + \deprecated + */ + +QSGEngine::QSGEngine(QObject *parent) : + QObject(*(new QSGEnginePrivate), parent) +{ +} + + +QSGEngine::~QSGEngine() +{ +} + + +void QSGEngine::setCanvas(QQuickCanvas *canvas) +{ + d_func()->canvas = canvas; + connect(canvas, SIGNAL(afterRendering()), this, SIGNAL(afterRendering())); + connect(canvas, SIGNAL(beforeRendering()), this, SIGNAL(beforeRendering())); +} + +void QSGEngine::setClearBeforeRendering(bool enabled) +{ + d_func()->canvas->setClearBeforeRendering(enabled); +} + +bool QSGEngine::clearBeforeRendering() const +{ + return d_func()->canvas->clearBeforeRendering(); +} + +QSGTexture *QSGEngine::createTextureFromImage(const QImage &image) const +{ + return d_func()->canvas->createTextureFromImage(image); +} + +QSGTexture *QSGEngine::createTextureFromId(uint id, const QSize &size, TextureOptions options) const +{ + return d_func()->canvas->createTextureFromId(id, size, QQuickCanvas::CreateTextureOptions((int) options)); +} + +void QSGEngine::setClearColor(const QColor &color) +{ + d_func()->canvas->setClearColor(color); +} + +QColor QSGEngine::clearColor() const +{ + return d_func()->canvas->clearColor(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgengine.h b/src/quick/scenegraph/util/qsgengine.h new file mode 100644 index 0000000000..6b7ceb939f --- /dev/null +++ b/src/quick/scenegraph/util/qsgengine.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGENGINE_H +#define QSGENGINE_H + +#include <QObject> + +#include <QtQuick/qsgtexture.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGEnginePrivate; + +class QQuickCanvas; + +class Q_QUICK_EXPORT QSGEngine : public QObject +{ + Q_OBJECT + + Q_DECLARE_PRIVATE(QSGEngine) + +public: + + enum TextureOption { + TextureHasAlphaChannel = 0x0001, + TextureHasMipmaps = 0x0002, + TextureOwnsGLTexture = 0x0004 + }; + Q_DECLARE_FLAGS(TextureOptions, TextureOption) + + QSGTexture *createTextureFromImage(const QImage &image) const; + QSGTexture *createTextureFromId(uint id, const QSize &size, TextureOptions options = TextureOption(0)) const; + + void setClearBeforeRendering(bool enabled); + bool clearBeforeRendering() const; + + void setClearColor(const QColor &color); + QColor clearColor() const; + +Q_SIGNALS: + void beforeRendering(); + void afterRendering(); + +private: + QSGEngine(QObject *parent = 0); + ~QSGEngine(); + + friend class QSGContext; + friend class QSGContextPrivate; + friend class QQuickCanvasPrivate; + void setCanvas(QQuickCanvas *canvas); + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGENGINE_H diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp new file mode 100644 index 0000000000..cf5c7869ea --- /dev/null +++ b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgflatcolormaterial.h" + +#include <qopenglshaderprogram.h> + +QT_BEGIN_NAMESPACE + +class FlatColorMaterialShader : public QSGMaterialShader +{ +public: + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual char const *const *attributeNames() const; + + static QSGMaterialType type; + +private: + virtual void initialize(); + virtual const char *vertexShader() const; + virtual const char *fragmentShader() const; + + int m_matrix_id; + int m_color_id; +}; + +QSGMaterialType FlatColorMaterialShader::type; + +void FlatColorMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + + QSGFlatColorMaterial *oldMaterial = static_cast<QSGFlatColorMaterial *>(oldEffect); + QSGFlatColorMaterial *newMaterial = static_cast<QSGFlatColorMaterial *>(newEffect); + + const QColor &c = newMaterial->color(); + + if (oldMaterial == 0 || c != oldMaterial->color() || state.isOpacityDirty()) { + float opacity = state.opacity(); + QVector4D v(c.redF() * c.alphaF() * opacity, + c.greenF() * c.alphaF() * opacity, + c.blueF() * c.alphaF() * opacity, + c.alphaF() * opacity); + program()->setUniformValue(m_color_id, v); + } + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); +} + +char const *const *FlatColorMaterialShader::attributeNames() const +{ + static char const *const attr[] = { "vCoord", 0 }; + return attr; +} + +void FlatColorMaterialShader::initialize() +{ + m_matrix_id = program()->uniformLocation("matrix"); + m_color_id = program()->uniformLocation("color"); +} + +const char *FlatColorMaterialShader::vertexShader() const { + return + "attribute highp vec4 vCoord; \n" + "uniform highp mat4 matrix; \n" + "void main() { \n" + " gl_Position = matrix * vCoord; \n" + "}"; +} + +const char *FlatColorMaterialShader::fragmentShader() const { + return + "uniform lowp vec4 color; \n" + "void main() { \n" + " gl_FragColor = color; \n" + "}"; +} + + + +/*! + \class QSGFlatColorMaterial + \brief The QSGFlatColorMaterial class provides a convenient way of rendering + solid colored geometry in the scene graph. + + \inmodule QtQuick + + The flat color material will fill every pixel in a geometry using + a solid color. The color can contain transparency. + + The geometry to be rendered with a flat color material requires + vertices in attribute location 0 in the QSGGeometry object to render + correctly. The QSGGeometry::defaultAttributes_Point2D() returns an attribute + set compatible with this material. + + The flat color material respects both current opacity and current matrix + when updating it's rendering state. + */ + + +/*! + Constructs a new flat color material. + + The default color is white. + */ + +QSGFlatColorMaterial::QSGFlatColorMaterial() : m_color(QColor(255, 255, 255)) +{ +} + + + +/*! + \fn QColor QSGFlatColorMaterial::color() const + + Returns this flat color material's color. + + The default color is white. + */ + + + +/*! + Sets this flat color material's color to \a color. + */ + +void QSGFlatColorMaterial::setColor(const QColor &color) +{ + m_color = color; + setFlag(Blending, m_color.alpha() != 0xff); +} + + + +/*! + \internal + */ + +QSGMaterialType *QSGFlatColorMaterial::type() const +{ + return &FlatColorMaterialShader::type; +} + + + +/*! + \internal + */ + +QSGMaterialShader *QSGFlatColorMaterial::createShader() const +{ + return new FlatColorMaterialShader; +} + + +int QSGFlatColorMaterial::compare(const QSGMaterial *other) const +{ + const QSGFlatColorMaterial *flat = static_cast<const QSGFlatColorMaterial *>(other); + return m_color.rgba() - flat->color().rgba(); + +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.h b/src/quick/scenegraph/util/qsgflatcolormaterial.h new file mode 100644 index 0000000000..d788901b8a --- /dev/null +++ b/src/quick/scenegraph/util/qsgflatcolormaterial.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FLATCOLORMATERIAL_H +#define FLATCOLORMATERIAL_H + +#include <QtQuick/qsgmaterial.h> +#include <qcolor.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGFlatColorMaterial : public QSGMaterial +{ +public: + QSGFlatColorMaterial(); + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; + + void setColor(const QColor &color); + const QColor &color() const { return m_color; } + + int compare(const QSGMaterial *other) const; + +private: + QColor m_color; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // FLATCOLORMATERIAL_H diff --git a/src/quick/scenegraph/util/qsgpainternode.cpp b/src/quick/scenegraph/util/qsgpainternode.cpp new file mode 100644 index 0000000000..640aab967e --- /dev/null +++ b/src/quick/scenegraph/util/qsgpainternode.cpp @@ -0,0 +1,464 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgpainternode_p.h" + +#include <QtQuick/private/qquickpainteditem_p.h> + +#include <QtQuick/private/qsgcontext_p.h> +#include <private/qopenglextensions_p.h> +#include <qopenglframebufferobject.h> +#include <qopenglfunctions.h> +#include <qopenglpaintdevice.h> +#include <qmath.h> +#include <qpainter.h> + +QT_BEGIN_NAMESPACE + +#define QT_MINIMUM_DYNAMIC_FBO_SIZE 64 + +static inline int qt_next_power_of_two(int v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + ++v; + return v; +} + +QSGPainterTexture::QSGPainterTexture() + : QSGPlainTexture() +{ + +} + +void QSGPainterTexture::bind() +{ + if (m_dirty_rect.isNull()) { + QSGPlainTexture::bind(); + return; + } + + bool oldMipmapsGenerated = m_mipmaps_generated; + m_mipmaps_generated = true; + QSGPlainTexture::bind(); + m_mipmaps_generated = oldMipmapsGenerated; + + QImage subImage = m_image.copy(m_dirty_rect); + + int w = m_dirty_rect.width(); + int h = m_dirty_rect.height(); + +#ifdef QT_OPENGL_ES + glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_rect.x(), m_dirty_rect.y(), w, h, + GL_RGBA, GL_UNSIGNED_BYTE, subImage.constBits()); +#else + glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_rect.x(), m_dirty_rect.y(), w, h, + GL_BGRA, GL_UNSIGNED_BYTE, subImage.constBits()); +#endif + + if (m_has_mipmaps && !m_mipmaps_generated) { + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D); + m_mipmaps_generated = true; + } + + m_dirty_texture = false; + m_dirty_bind_options = false; + + m_dirty_rect = QRect(); +} + +QSGPainterNode::QSGPainterNode(QQuickPaintedItem *item) + : QSGGeometryNode() + , m_preferredRenderTarget(QQuickPaintedItem::Image) + , m_actualRenderTarget(QQuickPaintedItem::Image) + , m_item(item) + , m_fbo(0) + , m_multisampledFbo(0) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) + , m_texture(0) + , m_gl_device(0) + , m_size(1, 1) + , m_dirtyContents(false) + , m_opaquePainting(false) + , m_linear_filtering(false) + , m_mipmapping(false) + , m_smoothPainting(false) + , m_extensionsChecked(false) + , m_multisamplingSupported(false) + , m_fastFBOResizing(false) + , m_fillColor(Qt::transparent) + , m_contentsScale(1.0) + , m_dirtyGeometry(false) + , m_dirtyRenderTarget(false) + , m_dirtyTexture(false) +{ + m_context = static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphContext(); + + setMaterial(&m_materialO); + setOpaqueMaterial(&m_material); + setGeometry(&m_geometry); +} + +QSGPainterNode::~QSGPainterNode() +{ + delete m_texture; + delete m_fbo; + delete m_multisampledFbo; + delete m_gl_device; +} + +void QSGPainterNode::paint() +{ + QRect dirtyRect = m_dirtyRect.isNull() ? QRect(0, 0, m_size.width(), m_size.height()) : m_dirtyRect; + + QPainter painter; + if (m_actualRenderTarget == QQuickPaintedItem::Image) + painter.begin(&m_image); + else { + if (!m_gl_device) { + m_gl_device = new QOpenGLPaintDevice(m_fboSize); + m_gl_device->setPaintFlipped(true); + } + + if (m_multisampledFbo) + m_multisampledFbo->bind(); + else + m_fbo->bind(); + + painter.begin(m_gl_device); + } + + if (m_smoothPainting) { + painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing + | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + } + + painter.scale(m_contentsScale, m_contentsScale); + + QRect sclip(qFloor(dirtyRect.x()/m_contentsScale), + qFloor(dirtyRect.y()/m_contentsScale), + qCeil(dirtyRect.width()/m_contentsScale+dirtyRect.x()/m_contentsScale-qFloor(dirtyRect.x()/m_contentsScale)), + qCeil(dirtyRect.height()/m_contentsScale+dirtyRect.y()/m_contentsScale-qFloor(dirtyRect.y()/m_contentsScale))); + + if (!m_dirtyRect.isNull()) + painter.setClipRect(sclip); + + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.fillRect(sclip, m_fillColor); + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + + m_item->paint(&painter); + painter.end(); + + if (m_actualRenderTarget == QQuickPaintedItem::Image) { + m_texture->setImage(m_image); + m_texture->setDirtyRect(dirtyRect); + } else if (m_multisampledFbo) { + QOpenGLFramebufferObject::blitFramebuffer(m_fbo, dirtyRect, m_multisampledFbo, dirtyRect); + } + + if (m_multisampledFbo) + m_multisampledFbo->release(); + else if (m_fbo) + m_fbo->release(); + + m_dirtyRect = QRect(); +} + +void QSGPainterNode::update() +{ + if (m_dirtyRenderTarget) + updateRenderTarget(); + if (m_dirtyGeometry) + updateGeometry(); + if (m_dirtyTexture) + updateTexture(); + + if (m_dirtyContents) + paint(); + + m_dirtyGeometry = false; + m_dirtyRenderTarget = false; + m_dirtyTexture = false; + m_dirtyContents = false; +} + +void QSGPainterNode::updateTexture() +{ + m_texture->setHasMipmaps(m_mipmapping); + m_texture->setHasAlphaChannel(!m_opaquePainting); + m_material.setTexture(m_texture); + m_materialO.setTexture(m_texture); + + markDirty(DirtyMaterial); +} + +void QSGPainterNode::updateGeometry() +{ + QRectF source; + if (m_actualRenderTarget == QQuickPaintedItem::Image) + source = QRectF(0, 0, 1, 1); + else + source = QRectF(0, 0, qreal(m_size.width()) / m_fboSize.width(), qreal(m_size.height()) / m_fboSize.height()); + QRectF dest(0, 0, m_size.width(), m_size.height()); + if (m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject) + dest = QRectF(QPointF(0, m_size.height()), QPointF(m_size.width(), 0)); + QSGGeometry::updateTexturedRectGeometry(&m_geometry, + dest, + source); + markDirty(DirtyGeometry); +} + +void QSGPainterNode::updateRenderTarget() +{ + if (!m_extensionsChecked) { + QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' '); + m_multisamplingSupported = extensions.contains("GL_EXT_framebuffer_multisample") + && extensions.contains("GL_EXT_framebuffer_blit"); + m_extensionsChecked = true; + } + + m_dirtyContents = true; + + QQuickPaintedItem::RenderTarget oldTarget = m_actualRenderTarget; + if (m_preferredRenderTarget == QQuickPaintedItem::Image) { + m_actualRenderTarget = QQuickPaintedItem::Image; + } else { + if (!m_multisamplingSupported && m_smoothPainting) + m_actualRenderTarget = QQuickPaintedItem::Image; + else + m_actualRenderTarget = m_preferredRenderTarget; + } + if (oldTarget != m_actualRenderTarget) { + m_image = QImage(); + delete m_fbo; + delete m_multisampledFbo; + m_fbo = m_multisampledFbo = 0; + } + + if (m_actualRenderTarget == QQuickPaintedItem::FramebufferObject || + m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject) { + const QOpenGLContext *ctx = m_context->glContext(); + if (m_fbo && !m_dirtyGeometry && (!ctx->format().samples() || !m_multisamplingSupported)) + return; + + if (m_fboSize.isEmpty()) + updateFBOSize(); + + delete m_fbo; + delete m_multisampledFbo; + m_fbo = m_multisampledFbo = 0; + + if (m_smoothPainting && ctx->format().samples() && m_multisamplingSupported) { + { + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + format.setSamples(8); + m_multisampledFbo = new QOpenGLFramebufferObject(m_fboSize, format); + } + { + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::NoAttachment); + m_fbo = new QOpenGLFramebufferObject(m_fboSize, format); + } + } else { + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + m_fbo = new QOpenGLFramebufferObject(m_fboSize, format); + } + } else { + if (!m_image.isNull() && !m_dirtyGeometry) + return; + + m_image = QImage(m_size, QImage::Format_ARGB32_Premultiplied); + m_image.fill(Qt::transparent); + } + + QSGPainterTexture *texture = new QSGPainterTexture; + if (m_actualRenderTarget == QQuickPaintedItem::Image) { + texture->setOwnsTexture(true); + texture->setTextureSize(m_size); + } else { + texture->setTextureId(m_fbo->texture()); + texture->setOwnsTexture(false); + texture->setTextureSize(m_fboSize); + } + + if (m_texture) + delete m_texture; + + texture->setTextureSize(m_size); + m_texture = texture; +} + +void QSGPainterNode::updateFBOSize() +{ + int fboWidth; + int fboHeight; + if (m_fastFBOResizing) { + fboWidth = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qt_next_power_of_two(m_size.width())); + fboHeight = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qt_next_power_of_two(m_size.height())); + } else { + QSize minimumFBOSize = m_context->minimumFBOSize(); + fboWidth = qMax(minimumFBOSize.width(), m_size.width()); + fboHeight = qMax(minimumFBOSize.height(), m_size.height()); + } + + m_fboSize = QSize(fboWidth, fboHeight); +} + +void QSGPainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target) +{ + if (m_preferredRenderTarget == target) + return; + + m_preferredRenderTarget = target; + + m_dirtyRenderTarget = true; + m_dirtyGeometry = true; + m_dirtyTexture = true; +} + +void QSGPainterNode::setSize(const QSize &size) +{ + if (size == m_size) + return; + + m_size = size; + updateFBOSize(); + + if (m_fbo) + m_dirtyRenderTarget = m_fbo->size() != m_fboSize || m_dirtyRenderTarget; + else + m_dirtyRenderTarget = true; + m_dirtyGeometry = true; + m_dirtyTexture = true; +} + +void QSGPainterNode::setDirty(const QRect &dirtyRect) +{ + m_dirtyContents = true; + m_dirtyRect = dirtyRect; + + if (m_mipmapping) + m_dirtyTexture = true; + + markDirty(DirtyMaterial); +} + +void QSGPainterNode::setOpaquePainting(bool opaque) +{ + if (opaque == m_opaquePainting) + return; + + m_opaquePainting = opaque; + m_dirtyTexture = true; +} + +void QSGPainterNode::setLinearFiltering(bool linearFiltering) +{ + if (linearFiltering == m_linear_filtering) + return; + + m_linear_filtering = linearFiltering; + + m_material.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest); + m_materialO.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest); + markDirty(DirtyMaterial); +} + +void QSGPainterNode::setMipmapping(bool mipmapping) +{ + if (mipmapping == m_mipmapping) + return; + + m_mipmapping = mipmapping; + m_material.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None); + m_materialO.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None); + m_dirtyTexture = true; +} + +void QSGPainterNode::setSmoothPainting(bool s) +{ + if (s == m_smoothPainting) + return; + + m_smoothPainting = s; + m_dirtyRenderTarget = true; +} + +void QSGPainterNode::setFillColor(const QColor &c) +{ + if (c == m_fillColor) + return; + + m_fillColor = c; + markDirty(DirtyMaterial); +} + +void QSGPainterNode::setContentsScale(qreal s) +{ + if (s == m_contentsScale) + return; + + m_contentsScale = s; + markDirty(DirtyMaterial); +} + +void QSGPainterNode::setFastFBOResizing(bool dynamic) +{ + m_fastFBOResizing = dynamic; +} + +QImage QSGPainterNode::toImage() const +{ + if (m_actualRenderTarget == QQuickPaintedItem::Image) + return m_image; + else + return m_fbo->toImage(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgpainternode_p.h b/src/quick/scenegraph/util/qsgpainternode_p.h new file mode 100644 index 0000000000..85f26f6056 --- /dev/null +++ b/src/quick/scenegraph/util/qsgpainternode_p.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGPAINTERNODE_P_H +#define QSGPAINTERNODE_P_H + +#include <QtQuick/qsgnode.h> +#include "qsgtexturematerial.h" +#include "qsgtexture_p.h" + +#include <QtQuick/qquickpainteditem.h> + +#include <QtGui/qcolor.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QOpenGLFramebufferObject; +class QOpenGLPaintDevice; + +class Q_QUICK_EXPORT QSGPainterTexture : public QSGPlainTexture +{ +public: + QSGPainterTexture(); + + void setDirtyRect(const QRect &rect) { m_dirty_rect = rect; } + + void bind(); + +private: + QRect m_dirty_rect; +}; + +class Q_QUICK_EXPORT QSGPainterNode : public QSGGeometryNode +{ +public: + QSGPainterNode(QQuickPaintedItem *item); + virtual ~QSGPainterNode(); + + void setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target); + + void setSize(const QSize &size); + QSize size() const { return m_size; } + + void setDirty(const QRect &dirtyRect = QRect()); + + void setOpaquePainting(bool opaque); + bool opaquePainting() const { return m_opaquePainting; } + + void setLinearFiltering(bool linearFiltering); + bool linearFiltering() const { return m_linear_filtering; } + + void setMipmapping(bool mipmapping); + bool mipmapping() const { return m_mipmapping; } + + void setSmoothPainting(bool s); + bool smoothPainting() const { return m_smoothPainting; } + + void setFillColor(const QColor &c); + QColor fillColor() const { return m_fillColor; } + + void setContentsScale(qreal s); + qreal contentsScale() const { return m_contentsScale; } + + void setFastFBOResizing(bool dynamic); + bool fastFBOResizing() const { return m_fastFBOResizing; } + + QImage toImage() const; + void update(); + + void paint(); + +private: + void updateTexture(); + void updateGeometry(); + void updateRenderTarget(); + void updateFBOSize(); + + QSGContext *m_context; + + QQuickPaintedItem::RenderTarget m_preferredRenderTarget; + QQuickPaintedItem::RenderTarget m_actualRenderTarget; + + QQuickPaintedItem *m_item; + + QOpenGLFramebufferObject *m_fbo; + QOpenGLFramebufferObject *m_multisampledFbo; + QImage m_image; + + QSGOpaqueTextureMaterial m_material; + QSGTextureMaterial m_materialO; + QSGGeometry m_geometry; + QSGPainterTexture *m_texture; + QOpenGLPaintDevice *m_gl_device; + + QSize m_size; + QSize m_fboSize; + bool m_dirtyContents; + QRect m_dirtyRect; + bool m_opaquePainting; + bool m_linear_filtering; + bool m_mipmapping; + bool m_smoothPainting; + bool m_extensionsChecked; + bool m_multisamplingSupported; + bool m_fastFBOResizing; + QColor m_fillColor; + qreal m_contentsScale; + + bool m_dirtyGeometry; + bool m_dirtyRenderTarget; + bool m_dirtyTexture; +}; + +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // QSGPAINTERNODE_P_H diff --git a/src/quick/scenegraph/util/qsgsimplematerial.h b/src/quick/scenegraph/util/qsgsimplematerial.h new file mode 100644 index 0000000000..44beb135f7 --- /dev/null +++ b/src/quick/scenegraph/util/qsgsimplematerial.h @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSIMPLEMATERIAL_H +#define QSGSIMPLEMATERIAL_H + +#include <QtQuick/qsgmaterial.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +template <typename State> +class QSGSimpleMaterialShader : public QSGMaterialShader +{ +public: + void initialize() { + QSGMaterialShader::initialize(); + + m_id_matrix = program()->uniformLocation(uniformMatrixName()); + if (m_id_matrix < 0) { + qFatal("QSGSimpleMaterialShader does not implement 'uniform highp mat4 %s;' in its vertex shader", + uniformMatrixName()); + } + + const char *opacity = uniformOpacityName(); + if (opacity) { + m_id_opacity = program()->uniformLocation(uniformOpacityName()); + if (m_id_opacity < 0) { + qFatal("QSGSimpleMaterialShader does not implement 'uniform lowp float %s' in its fragment shader", + uniformOpacityName()); + } + } else { + m_id_opacity = -1; + } + + resolveUniforms(); + } + + const char *uniformMatrixName() const { return "qt_Matrix"; } + const char *uniformOpacityName() const { return "qt_Opacity"; } + + void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial); + + virtual void updateState(const State *newState, const State *oldState) = 0; + + virtual void resolveUniforms() {} + + virtual QList<QByteArray> attributes() const = 0; + + char const *const *attributeNames() const + { + if (m_attribute_pointers.size()) + return m_attribute_pointers.constData(); + + QList<QByteArray> names = attributes(); + + // Calculate the total number of bytes needed, so we don't get rellocs and + // bad pointers while copying over the individual names. + // Add an extra byte pr entry for the '\0' char. + int total = 0; + for (int i=0; i<names.size(); ++i) + total += names.at(i).size() + 1; + m_attribute_name_data.reserve(total); + + // Copy over the names + for (int i=0; i<names.size(); ++i) { + m_attribute_pointers << m_attribute_name_data.constData() + m_attribute_name_data.size(); + m_attribute_name_data.append(names.at(i)); + m_attribute_name_data.append('\0'); + } + + // Append the "null" terminator + m_attribute_pointers << 0; + + return m_attribute_pointers.constData(); + } + +private: + int m_id_matrix; + int m_id_opacity; + + mutable QByteArray m_attribute_name_data; + mutable QVector<const char *> m_attribute_pointers; +}; + +#define QSG_DECLARE_SIMPLE_SHADER(Shader, State) \ +static QSGMaterialShader *createShader() \ +{ \ + return new Shader; \ +} \ +public: \ +static QSGSimpleMaterial<State> *createMaterial() \ +{ \ + return new QSGSimpleMaterial<State>(createShader); \ +} + + +typedef QSGMaterialShader *(*PtrShaderCreateFunc)(); + + +template <typename State> +class QSGSimpleMaterial : public QSGMaterial +{ + +public: + QSGSimpleMaterial(const State &state, PtrShaderCreateFunc func) + : m_state(state) + , m_func(func) + { + } + + QSGSimpleMaterial(PtrShaderCreateFunc func) + : m_func(func) + { + } + + QSGMaterialShader *createShader() const { return m_func(); } + QSGMaterialType *type() const { return &m_type; } + + State *state() { return &m_state; } + const State *state() const { return &m_state; } + +private: + static QSGMaterialType m_type; + State m_state; + PtrShaderCreateFunc m_func; +}; + +#define QSG_DECLARE_SIMPLE_COMPARABLE_SHADER(Shader, State) \ +static QSGMaterialShader *createShader() \ +{ \ + return new Shader; \ +} \ +public: \ +static QSGSimpleMaterialComparableMaterial<State> *createMaterial() \ +{ \ + return new QSGSimpleMaterialComparableMaterial<State>(createShader); \ +} + +template <typename State> +class QSGSimpleMaterialComparableMaterial : public QSGSimpleMaterial<State> +{ + +public: + QSGSimpleMaterialComparableMaterial(const State &state, PtrShaderCreateFunc func) + : QSGSimpleMaterial<State>(state, func) {} + + QSGSimpleMaterialComparableMaterial(PtrShaderCreateFunc func) + : QSGSimpleMaterial<State>(func) {} + + int compare(const QSGMaterial *other) const { + return QSGSimpleMaterialComparableMaterial<State>::state()->compare(static_cast<const QSGSimpleMaterialComparableMaterial<State> *>(other)->state()); + } +}; + + +template <typename State> +QSGMaterialType QSGSimpleMaterial<State>::m_type; + + +template <typename State> +Q_INLINE_TEMPLATE void QSGSimpleMaterialShader<State>::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) +{ + if (state.isMatrixDirty()) + program()->setUniformValue(m_id_matrix, state.combinedMatrix()); + if (state.isOpacityDirty() && m_id_opacity >= 0) + program()->setUniformValue(m_id_opacity, state.opacity()); + + State *ns = static_cast<QSGSimpleMaterial<State> *>(newMaterial)->state(); + State *old = 0; + if (oldMaterial) + old = static_cast<QSGSimpleMaterial<State> *>(oldMaterial)->state(); + updateState(ns, old); +} + +QT_END_NAMESPACE + +QT_END_HEADER + + +#endif diff --git a/src/quick/scenegraph/util/qsgsimplerectnode.cpp b/src/quick/scenegraph/util/qsgsimplerectnode.cpp new file mode 100644 index 0000000000..c3dc5354ca --- /dev/null +++ b/src/quick/scenegraph/util/qsgsimplerectnode.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsimplerectnode.h" +#include "qsgflatcolormaterial.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSGSimpleRectNode + + \brief The QSGSimpleRectNode class is a convenience class for drawing + solid filled rectangles using scenegraph. + + */ + + + +/*! + Constructs a QSGSimpleRectNode instance which is spanning \a rect with + the color \a color. + */ +QSGSimpleRectNode::QSGSimpleRectNode(const QRectF &rect, const QColor &color) + : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4) +{ + QSGGeometry::updateRectGeometry(&m_geometry, rect); + m_material.setColor(color); + setMaterial(&m_material); + setGeometry(&m_geometry); +} + + + +/*! + Constructs a QSGSimpleRectNode instance with an empty rectangle and + white color. + */ +QSGSimpleRectNode::QSGSimpleRectNode() + : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4) +{ + QSGGeometry::updateRectGeometry(&m_geometry, QRectF()); + setMaterial(&m_material); + setGeometry(&m_geometry); +} + + + +/*! + Sets the rectangle of this rect node to \a rect. + */ +void QSGSimpleRectNode::setRect(const QRectF &rect) +{ + QSGGeometry::updateRectGeometry(&m_geometry, rect); + markDirty(QSGNode::DirtyGeometry); +} + + + +/*! + Returns the rectangle that this rect node covers. + */ +QRectF QSGSimpleRectNode::rect() const +{ + const QSGGeometry::Point2D *pts = m_geometry.vertexDataAsPoint2D(); + return QRectF(pts[0].x, + pts[0].y, + pts[3].x - pts[0].x, + pts[3].y - pts[0].y); +} + + +/*! + Sets the color of this rectangle to \a color. The default + color will be white. + */ +void QSGSimpleRectNode::setColor(const QColor &color) +{ + if (color != m_material.color()) { + m_material.setColor(color); + markDirty(QSGNode::DirtyMaterial); + } +} + + + +/*! + Returns the color of this rectangle. + */ +QColor QSGSimpleRectNode::color() const +{ + return m_material.color(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgsimplerectnode.h b/src/quick/scenegraph/util/qsgsimplerectnode.h new file mode 100644 index 0000000000..6519290cfe --- /dev/null +++ b/src/quick/scenegraph/util/qsgsimplerectnode.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SOLIDRECTNODE_H +#define SOLIDRECTNODE_H + +#include <QtQuick/qsgnode.h> +#include "qsgflatcolormaterial.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGSimpleRectNode : public QSGGeometryNode +{ +public: + QSGSimpleRectNode(const QRectF &rect, const QColor &color); + QSGSimpleRectNode(); + + void setRect(const QRectF &rect); + inline void setRect(qreal x, qreal y, qreal w, qreal h) { setRect(QRectF(x, y, w, h)); } + QRectF rect() const; + + void setColor(const QColor &color); + QColor color() const; + +private: + QSGFlatColorMaterial m_material; + QSGGeometry m_geometry; + void *reserved; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // SOLIDRECTNODE_H diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp new file mode 100644 index 0000000000..00b240e435 --- /dev/null +++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qsgsimpletexturenode.h" + +QT_BEGIN_NAMESPACE + +static void qsgsimpletexturenode_update(QSGGeometry *g, + QSGTexture *texture, + const QRectF &rect) +{ + if (!texture) + return; + + QSize ts = texture->textureSize(); + QRectF sourceRect(0, 0, ts.width(), ts.height()); + QSGGeometry::updateTexturedRectGeometry(g, rect, texture->convertToNormalizedSourceRect(sourceRect)); +} + +/*! + \class QSGSimpleTextureNode + \brief The QSGSimpleTextureNode class is provided for convenience to easily draw + textured content using the QML scene graph. + + \inmodule QtQuick + + \warning The simple texture node class must have a texture before being + added to the scene graph to be rendered. +*/ + +/*! + Constructs a new simple texture node + */ +QSGSimpleTextureNode::QSGSimpleTextureNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) +{ + setGeometry(&m_geometry); + setMaterial(&m_material); + setOpaqueMaterial(&m_opaque_material); +} + +/*! + Sets the filtering to be used for this texture node to \a filtering. + + For smooth scaling, use QSGTexture::Linear; for normal scaling, use + QSGTexture::Nearest. + */ +void QSGSimpleTextureNode::setFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.filtering() == filtering) + return; + + m_material.setFiltering(filtering); + m_opaque_material.setFiltering(filtering); + markDirty(DirtyMaterial); +} + + +/*! + Returns the filtering currently set on this texture node + */ +QSGTexture::Filtering QSGSimpleTextureNode::filtering() const +{ + return m_material.filtering(); +} + + +/*! + Sets the target rect of this texture node to \a r + */ +void QSGSimpleTextureNode::setRect(const QRectF &r) +{ + if (m_rect == r) + return; + m_rect = r; + qsgsimpletexturenode_update(&m_geometry, texture(), m_rect); + markDirty(DirtyGeometry); +} + + +/*! + Returns the target rect of this texture node. + */ +QRectF QSGSimpleTextureNode::rect() const +{ + return m_rect; +} + +/*! + Sets the texture of this texture node to \a texture. + + \warning A texture node must have a texture before being added + to the scenegraph to be rendered. + */ +void QSGSimpleTextureNode::setTexture(QSGTexture *texture) +{ + if (m_material.texture() == texture) + return; + m_material.setTexture(texture); + m_opaque_material.setTexture(texture); + qsgsimpletexturenode_update(&m_geometry, texture, m_rect); + markDirty(DirtyMaterial); +} + + + +/*! + Returns the texture for this texture node + */ +QSGTexture *QSGSimpleTextureNode::texture() const +{ + return m_material.texture(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.h b/src/quick/scenegraph/util/qsgsimpletexturenode.h new file mode 100644 index 0000000000..605cae11e4 --- /dev/null +++ b/src/quick/scenegraph/util/qsgsimpletexturenode.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSIMPLETEXTURENODE_H +#define QSGSIMPLETEXTURENODE_H + +#include <QtQuick/qsgnode.h> +#include <QtQuick/qsggeometry.h> +#include "qsgtexturematerial.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGSimpleTextureNode : public QSGGeometryNode +{ +public: + QSGSimpleTextureNode(); + + void setRect(const QRectF &rect); + inline void setRect(qreal x, qreal y, qreal w, qreal h) { setRect(QRectF(x, y, w, h)); } + QRectF rect() const; + + void setTexture(QSGTexture *texture); + QSGTexture *texture() const; + + void setFiltering(QSGTexture::Filtering filtering); + QSGTexture::Filtering filtering() const; + +private: + QSGGeometry m_geometry; + QSGOpaqueTextureMaterial m_opaque_material; + QSGTextureMaterial m_material; + + QRectF m_rect; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGSIMPLETEXTURENODE_H diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp new file mode 100644 index 0000000000..0abbc8dae1 --- /dev/null +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -0,0 +1,541 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#define GL_GLEXT_PROTOTYPES + +#include "qsgtexture_p.h" +#include <qopenglfunctions.h> +#include <QtQuick/private/qsgcontext_p.h> +#include <qthread.h> +#include <private/qdeclarativedebugtrace_p.h> + +#if !defined(QT_NO_DEBUG) && (defined(Q_OS_LINUX) || defined(Q_OS_MAC)) +#include <execinfo.h> +#include <QHash> +#endif + +QT_BEGIN_NAMESPACE + +inline static bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} + +QSGTexturePrivate::QSGTexturePrivate() + : wrapChanged(false) + , filteringChanged(false) + , horizontalWrap(QSGTexture::ClampToEdge) + , verticalWrap(QSGTexture::ClampToEdge) + , mipmapMode(QSGTexture::None) + , filterMode(QSGTexture::Nearest) +{ +} + +#ifndef QT_NO_DEBUG + +static int qt_debug_texture_count = 0; + +#if defined(Q_OS_LINUX) || defined (Q_OS_MAC) +DEFINE_BOOL_CONFIG_OPTION(qmlDebugLeakBacktrace, QML_DEBUG_LEAK_BACKTRACE) + +#define BACKTRACE_SIZE 20 +class SGTextureTraceItem +{ +public: + void *backTrace[BACKTRACE_SIZE]; + size_t backTraceSize; +}; + +static QHash<QSGTexture*, SGTextureTraceItem*> qt_debug_allocated_textures; +#endif + +inline static void qt_debug_print_texture_count() +{ + qDebug("Number of leaked textures: %i", qt_debug_texture_count); + qt_debug_texture_count = -1; + +#if defined(Q_OS_LINUX) || defined (Q_OS_MAC) + if (qmlDebugLeakBacktrace()) { + while (!qt_debug_allocated_textures.isEmpty()) { + QHash<QSGTexture*, SGTextureTraceItem*>::Iterator it = qt_debug_allocated_textures.begin(); + QSGTexture* texture = it.key(); + SGTextureTraceItem* item = it.value(); + + qt_debug_allocated_textures.erase(it); + + qDebug() << "------"; + qDebug() << "Leaked" << texture << "backtrace:"; + + char** symbols = backtrace_symbols(item->backTrace, item->backTraceSize); + + if (symbols) { + for (int i=0; i<(int) item->backTraceSize; i++) + qDebug("Backtrace <%02d>: %s", i, symbols[i]); + free(symbols); + } + + qDebug() << "------"; + + delete item; + } + } +#endif +} + +inline static void qt_debug_add_texture(QSGTexture* texture) +{ +#if defined(Q_OS_LINUX) || defined (Q_OS_MAC) + if (qmlDebugLeakBacktrace()) { + SGTextureTraceItem* item = new SGTextureTraceItem; + item->backTraceSize = backtrace(item->backTrace, BACKTRACE_SIZE); + qt_debug_allocated_textures.insert(texture, item); + } +#else + Q_UNUSED(texture); +#endif // Q_OS_LINUX + + ++qt_debug_texture_count; + + static bool atexit_registered = false; + if (!atexit_registered) { + atexit(qt_debug_print_texture_count); + atexit_registered = true; + } +} + +static void qt_debug_remove_texture(QSGTexture* texture) +{ +#if defined(Q_OS_LINUX) || defined (Q_OS_MAC) + if (qmlDebugLeakBacktrace()) { + SGTextureTraceItem* item = qt_debug_allocated_textures.value(texture, 0); + if (item) { + qt_debug_allocated_textures.remove(texture); + delete item; + } + } +#else + Q_UNUSED(texture) +#endif + + --qt_debug_texture_count; + + if (qt_debug_texture_count < 0) + qDebug("Material destroyed after qt_debug_print_texture_count() was called."); +} + +#endif // QT_NO_DEBUG + + +QSGTexture::QSGTexture() + : QObject(*(new QSGTexturePrivate)) +{ +#ifndef QT_NO_DEBUG + qt_debug_add_texture(this); +#endif +} + +QSGTexture::~QSGTexture() +{ +#ifndef QT_NO_DEBUG + qt_debug_remove_texture(this); +#endif +} + + +/*! + \fn void QSGTexture::bind() + + Call this function to bind this texture to the current texture + target. + + Binding a texture may also include uploading the texture data from + a previously set QImage. + + \warning This function can only be called from the rendering thread. + */ + +/*! + This function returns a copy of the current texture which is removed + from its atlas. + + The current texture remains unchanged, so texture coordinates do not + need to be updated. + + Removing a texture from an atlas is primarily useful when passing + it to a shader that operates on the texture coordinates 0-1 instead + of the texture subrect inside the atlas. + + If the texture is not part of a texture atlas, this function returns 0. + + Implementations of this function are recommended to return the same instance + for multiple calls to limit memory usage. + + \warning This function can only be called from the rendering thread. + */ + +QSGTexture *QSGTexture::removedFromAtlas() const +{ + Q_ASSERT_X(!isAtlasTexture(), "QSGTexture::removedFromAtlas()", "Called on a non-atlas texture"); + return 0; +} + +/*! + Returns weither this texture is part of an atlas or not. + + The default implementation returns false. + */ +bool QSGTexture::isAtlasTexture() const +{ + return false; +} + +/*! + \fn int QSGTexture::textureId() const + + Returns the OpenGL texture id for this texture. + + The default value is 0, indicating that it is an invalid texture id. + + The function should at all times return the correct texture id. + + \warning This function can only be called from the rendering thread. + */ + + + +/*! + Returns the rectangle inside textureSize() that this texture + represents in normalized coordinates. + + The default implementation returns a rect at position (0, 0) with + width and height of 1. + */ +QRectF QSGTexture::textureSubRect() const +{ + return QRectF(0, 0, 1, 1); +} + +/*! + \fn bool QSGTexture::hasMipmaps() const + + Returns true if the texture data contains mipmap levels. + */ + + +/*! + Sets the mipmap sampling mode to be used for the upcoming bind() call to \a filter. + + Setting the mipmap filtering has no effect it the texture does not have mipmaps. + + \sa hasMipmaps() + */ +void QSGTexture::setMipmapFiltering(Filtering filter) +{ + Q_D(QSGTexture); + if (d->mipmapMode != (uint) filter) { + d->mipmapMode = filter; + d->filteringChanged = true; + } +} + +/*! + Returns whether mipmapping should be used when sampling from this texture. + */ +QSGTexture::Filtering QSGTexture::mipmapFiltering() const +{ + return (QSGTexture::Filtering) d_func()->mipmapMode; +} + + +/*! + Sets the sampling mode to be used for the upcoming bind() call to \a filter. + */ +void QSGTexture::setFiltering(QSGTexture::Filtering filter) +{ + Q_D(QSGTexture); + if (d->filterMode != (uint) filter) { + d->filterMode = filter; + d->filteringChanged = true; + } +} + +QSGTexture::Filtering QSGTexture::filtering() const +{ + return (QSGTexture::Filtering) d_func()->filterMode; +} + + + +/*! + Sets the horizontal wrap mode to be used for the upcoming bind() call to \a hwrap + */ + +void QSGTexture::setHorizontalWrapMode(WrapMode hwrap) +{ + Q_D(QSGTexture); + if ((uint) hwrap != d->horizontalWrap) { + d->horizontalWrap = hwrap; + d->wrapChanged = true; + } +} + +QSGTexture::WrapMode QSGTexture::horizontalWrapMode() const +{ + return (QSGTexture::WrapMode) d_func()->horizontalWrap; +} + + + +void QSGTexture::setVerticalWrapMode(WrapMode vwrap) +{ + Q_D(QSGTexture); + if ((uint) vwrap != d->verticalWrap) { + d->verticalWrap = vwrap; + d->wrapChanged = true; + } +} + +QSGTexture::WrapMode QSGTexture::verticalWrapMode() const +{ + return (QSGTexture::WrapMode) d_func()->verticalWrap; +} + + +/*! + Update the texture state to match the filtering, mipmap and wrap options + currently set. + + If \a force is true, all properties will be updated regardless of weither + they have changed or not. + */ +void QSGTexture::updateBindOptions(bool force) +{ + Q_D(QSGTexture); + if (force || d->filteringChanged) { + bool linear = d->filterMode == Linear; + GLint minFilter = linear ? GL_LINEAR : GL_NEAREST; + GLint magFilter = linear ? GL_LINEAR : GL_NEAREST; + + if (hasMipmaps()) { + if (d->mipmapMode == Nearest) + minFilter = linear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST; + else if (d->mipmapMode == Linear) + minFilter = linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR; + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); + d->filteringChanged = false; + } + + if (force || d->wrapChanged) { +#if !defined(QT_NO_DEBUG) && defined(QT_OPENGL_ES_2) + if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat) { + bool npotSupported = QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::NPOTTextures); + QSize size = textureSize(); + bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); + if (!npotSupported && isNpot) + qWarning("Scene Graph: This system does not support the REPEAT wrap mode for non-power-of-two textures."); + } +#endif + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, d->horizontalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, d->verticalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); + d->wrapChanged = false; + } +} + +QSGPlainTexture::QSGPlainTexture() + : QSGTexture() + , m_texture_id(0) + , m_has_alpha(false) + , m_has_mipmaps(false) + , m_dirty_bind_options(false) + , m_owns_texture(true) + , m_mipmaps_generated(false) +{ +} + + +QSGPlainTexture::~QSGPlainTexture() +{ + if (m_texture_id && m_owns_texture) + glDeleteTextures(1, &m_texture_id); +} + +#ifdef QT_OPENGL_ES +static void swizzleBGRAToRGBA(QImage *image) +{ + const int width = image->width(); + const int height = image->height(); + for (int i = 0; i < height; ++i) { + uint *p = (uint *) image->scanLine(i); + for (int x = 0; x < width; ++x) + p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); + } +} +#endif + +void QSGPlainTexture::setImage(const QImage &image) +{ + m_image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); +#ifdef QT_OPENGL_ES + swizzleBGRAToRGBA(&m_image); +#endif + + m_texture_size = image.size(); + m_has_alpha = image.hasAlphaChannel(); + m_dirty_texture = true; + m_dirty_bind_options = true; + } + +int QSGPlainTexture::textureId() const +{ + if (m_dirty_texture) { + if (m_image.isNull()) { + // The actual texture and id will be updated/deleted in a later bind() + // or ~QSGPlainTexture so just keep it minimal here. + return 0; + } else { + // Generate a texture id for use later and return it. + glGenTextures(1, &const_cast<QSGPlainTexture *>(this)->m_texture_id); + return m_texture_id; + } + } + return m_texture_id; +} + +void QSGPlainTexture::setTextureId(int id) +{ + if (m_texture_id && m_owns_texture) + glDeleteTextures(1, &m_texture_id); + + m_texture_id = id; + m_dirty_texture = false; + m_dirty_bind_options = true; + m_image = QImage(); + m_mipmaps_generated = false; +} + +void QSGPlainTexture::setHasMipmaps(bool mm) +{ + m_has_mipmaps = mm; + m_mipmaps_generated = false; +} + + +void QSGPlainTexture::bind() +{ + if (!m_dirty_texture) { + glBindTexture(GL_TEXTURE_2D, m_texture_id); + if (m_has_mipmaps && !m_mipmaps_generated) { + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D); + m_mipmaps_generated = true; + } + updateBindOptions(m_dirty_bind_options); + m_dirty_bind_options = false; + return; + } + + m_dirty_texture = false; + + + if (m_image.isNull()) { + if (m_texture_id && m_owns_texture) + glDeleteTextures(1, &m_texture_id); + m_texture_id = 0; + m_texture_size = QSize(); + m_has_mipmaps = false; + m_has_alpha = false; + return; + } + + if (m_texture_id == 0) + glGenTextures(1, &m_texture_id); + glBindTexture(GL_TEXTURE_2D, m_texture_id); + + // ### TODO: check for out-of-memory situations... + int w = m_image.width(); + int h = m_image.height(); + +#ifdef QT_OPENGL_ES + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_image.constBits()); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, m_image.constBits()); +#endif + + if (m_has_mipmaps) { + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D); + m_mipmaps_generated = true; + } + + m_texture_size = QSize(w, h); + m_texture_rect = QRectF(0, 0, 1, 1); + + updateBindOptions(m_dirty_bind_options); + m_dirty_bind_options = false; +} + + +/*! + \class QSGDynamicTexture + \brief The QSGDynamicTexture class serves as a baseclass for dynamically changing textures, + such as content that is rendered to FBO's. + + To update the content of the texture, call updateTexture() explicitly. Simply calling bind() + will not update the texture. + */ + + +/*! + \fn bool QSGDynamicTexture::updateTexture() + + Call this function to explicitely update the dynamic texture. Calling bind() will bind + the content that was previously updated. + + The function returns true if the texture was changed as a resul of the update; otherwise + returns false. + */ + + + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgtexture.h b/src/quick/scenegraph/util/qsgtexture.h new file mode 100644 index 0000000000..6fdab9f401 --- /dev/null +++ b/src/quick/scenegraph/util/qsgtexture.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGTEXTURE_H +#define QSGTEXTURE_H + +#include <QtQuick/qtquickglobal.h> +#include <QObject> +#include <QImage> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGTexturePrivate; +class Q_QUICK_EXPORT QSGTexture : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QSGTexture) + +public: + QSGTexture(); + ~QSGTexture(); + + enum WrapMode { + Repeat, + ClampToEdge + }; + + enum Filtering { + None, + Nearest, + Linear + }; + + virtual int textureId() const = 0; + virtual QSize textureSize() const = 0; + virtual bool hasAlphaChannel() const = 0; + virtual bool hasMipmaps() const = 0; + + virtual QRectF textureSubRect() const; + + virtual bool isAtlasTexture() const; + + virtual QSGTexture *removedFromAtlas() const; + + virtual void bind() = 0; + void updateBindOptions(bool force = false); + + void setMipmapFiltering(Filtering filter); + QSGTexture::Filtering mipmapFiltering() const; + + void setFiltering(Filtering filter); + QSGTexture::Filtering filtering() const; + + void setHorizontalWrapMode(WrapMode hwrap); + QSGTexture::WrapMode horizontalWrapMode() const; + + void setVerticalWrapMode(WrapMode vwrap); + QSGTexture::WrapMode verticalWrapMode() const; + + inline QRectF convertToNormalizedSourceRect(const QRectF &rect) const; + +protected: + QSGTexture(QSGTexturePrivate &dd); +}; + +QRectF QSGTexture::convertToNormalizedSourceRect(const QRectF &rect) const +{ + QSize s = textureSize(); + QRectF r = textureSubRect(); + + qreal sx = r.width() / s.width(); + qreal sy = r.height() / s.height(); + + return QRectF(r.x() + rect.x() * sx, + r.y() + rect.y() * sy, + rect.width() * sx, + rect.height() * sy); +} + + +class Q_QUICK_EXPORT QSGDynamicTexture : public QSGTexture +{ + Q_OBJECT +public: + virtual bool updateTexture() = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgtexture_p.h new file mode 100644 index 0000000000..e1d6dd0e32 --- /dev/null +++ b/src/quick/scenegraph/util/qsgtexture_p.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGTEXTURE_P_H +#define QSGTEXTURE_P_H + +#include <QtQuick/qtquickglobal.h> +#include <private/qobject_p.h> + +#include <QtGui/qopengl.h> + +#include "qsgtexture.h" +#include <QtQuick/private/qsgcontext_p.h> + +QT_BEGIN_NAMESPACE + +class QSGTexturePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QSGTexture); +public: + QSGTexturePrivate(); + + uint wrapChanged : 1; + uint filteringChanged : 1; + + uint horizontalWrap : 1; + uint verticalWrap : 1; + uint mipmapMode : 2; + uint filterMode : 2; +}; + +class Q_QUICK_EXPORT QSGPlainTexture : public QSGTexture +{ + Q_OBJECT +public: + QSGPlainTexture(); + virtual ~QSGPlainTexture(); + + void setOwnsTexture(bool owns) { m_owns_texture = owns; } + bool ownsTexture() const { return m_owns_texture; } + + void setTextureId(int id); + int textureId() const; + void setTextureSize(const QSize &size) { m_texture_size = size; } + QSize textureSize() const { return m_texture_size; } + + void setHasAlphaChannel(bool alpha) { m_has_alpha = alpha; } + bool hasAlphaChannel() const { return m_has_alpha; } + + void setHasMipmaps(bool mm); + bool hasMipmaps() const { return m_has_mipmaps; } + + void setImage(const QImage &image); + const QImage &image() { return m_image; } + + virtual void bind(); + + static QSGPlainTexture *fromImage(const QImage &image) { + QSGPlainTexture *t = new QSGPlainTexture(); + t->setImage(image); + return t; + } + +protected: + QImage m_image; + + GLuint m_texture_id; + QSize m_texture_size; + QRectF m_texture_rect; + + uint m_has_alpha : 1; + uint m_has_mipmaps : 1; + uint m_dirty_texture : 1; + uint m_dirty_bind_options : 1; + uint m_owns_texture : 1; + uint m_mipmaps_generated : 1; +}; + +QT_END_NAMESPACE + +#endif // QSGTEXTURE_P_H diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp new file mode 100644 index 0000000000..0bee81993c --- /dev/null +++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp @@ -0,0 +1,410 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgtexturematerial_p.h" + +#include <QtGui/qopenglshaderprogram.h> +#include <QtGui/qopenglfunctions.h> + +QT_BEGIN_NAMESPACE + +inline static bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} + +const char qt_scenegraph_texture_material_vertex_code[] = + "uniform highp mat4 qt_Matrix; \n" + "attribute highp vec4 qt_VertexPosition; \n" + "attribute highp vec2 qt_VertexTexCoord; \n" + "varying highp vec2 qt_TexCoord; \n" + "void main() { \n" + " qt_TexCoord = qt_VertexTexCoord; \n" + " gl_Position = qt_Matrix * qt_VertexPosition; \n" + "}"; + +const char qt_scenegraph_texture_material_fragment[] = + "varying highp vec2 qt_TexCoord; \n" + "uniform sampler2D qt_Texture; \n" + "void main() { \n" + " gl_FragColor = texture2D(qt_Texture, qt_TexCoord);\n" + "}"; + + +const char *QSGOpaqueTextureMaterialShader::vertexShader() const +{ + return qt_scenegraph_texture_material_vertex_code; +} + +const char *QSGOpaqueTextureMaterialShader::fragmentShader() const +{ + return qt_scenegraph_texture_material_fragment; +} + +QSGMaterialType QSGOpaqueTextureMaterialShader::type; + +char const *const *QSGOpaqueTextureMaterialShader::attributeNames() const +{ + static char const *const attr[] = { "qt_VertexPosition", "qt_VertexTexCoord", 0 }; + return attr; +} + +void QSGOpaqueTextureMaterialShader::initialize() +{ + m_matrix_id = program()->uniformLocation("qt_Matrix"); +} + +void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + QSGOpaqueTextureMaterial *tx = static_cast<QSGOpaqueTextureMaterial *>(newEffect); + QSGOpaqueTextureMaterial *oldTx = static_cast<QSGOpaqueTextureMaterial *>(oldEffect); + + QSGTexture *t = tx->texture(); + + t->setFiltering(tx->filtering()); +#ifdef QT_OPENGL_ES_2 + bool npotSupported = QOpenGLFunctions(const_cast<QOpenGLContext *>(state.context())).hasOpenGLFeature(QOpenGLFunctions::NPOTTextures); + QSize size = t->textureSize(); + bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); + if (!npotSupported && isNpot) { + t->setHorizontalWrapMode(QSGTexture::ClampToEdge); + t->setVerticalWrapMode(QSGTexture::ClampToEdge); + } else +#endif + { + t->setHorizontalWrapMode(tx->horizontalWrapMode()); + t->setVerticalWrapMode(tx->verticalWrapMode()); + } + t->setMipmapFiltering(tx->mipmapFiltering()); + + if (oldTx == 0 || oldTx->texture()->textureId() != t->textureId()) + t->bind(); + else + t->updateBindOptions(); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); +} + + +/*! + \class QSGOpaqueTextureMaterial + \brief The QSGOpaqueTextureMaterial class provides a convenient way of + rendering textured geometry in the scene graph. + + The opaque textured material will fill every pixel in a geometry with + the supplied texture. The material does not respect the opacity of the + QSGMaterialShader::RenderState, so opacity nodes in the parent chain + of nodes using this material, have no effect. + + The geometry to be rendered with an opaque texture material requires + vertices in attribute location 0 and texture coordinates in attribute + location 1. The texture coordinate is a 2-dimensional floating-point + tuple. The QSGGeometry::defaultAttributes_TexturedPoint2D returns an + attribute set compatible with this material. + + The texture to be rendered is can be set using setTexture(). How the + texure should be rendered can be specified using setMipmapFiltering(), + setFiltering(), setHorizontalWrapMode() and setVerticalWrapMode(). + The rendering state is set on the texture instance just before it + is bound. + + The opaque textured material respects the current matrix and the alpha + channel of the texture. It will disregard the accumulated opacity in + the scenegraph. + + A texture material must have a texture set before it is used as + a material in the scene graph. + */ + + + +/*! + Creates a new QSGOpaqueTextureMaterial. + + The default mipmap filtering and filtering mode is set to + QSGTexture::Nearest. The default wrap modes is set to + QSGTexture::ClampToEdge. + + */ +QSGOpaqueTextureMaterial::QSGOpaqueTextureMaterial() + : m_texture(0) + , m_filtering(QSGTexture::Nearest) + , m_mipmap_filtering(QSGTexture::Nearest) + , m_horizontal_wrap(QSGTexture::ClampToEdge) + , m_vertical_wrap(QSGTexture::ClampToEdge) +{ +} + + +/*! + \internal + */ +QSGMaterialType *QSGOpaqueTextureMaterial::type() const +{ + return &QSGOpaqueTextureMaterialShader::type; +} + +/*! + \internal + */ +QSGMaterialShader *QSGOpaqueTextureMaterial::createShader() const +{ + return new QSGOpaqueTextureMaterialShader; +} + + + +/*! + \fn QSGTexture *QSGOpaqueTextureMaterial::texture() const + + Returns this texture material's texture. + */ + + + +/*! + Sets the texture of this material to \a texture. + + The material does not take ownership over the texture. + */ + +void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture) +{ + m_texture = texture; + setFlag(Blending, m_texture ? m_texture->hasAlphaChannel() : false); +} + + + +/*! + \fn void QSGOpaqueTextureMaterial::setMipmapFiltering(QSGTexture::Filtering filtering) + + Sets the mipmap mode to \a filtering. + + The mipmap filtering mode is set on the texture instance just before the + texture is bound for rendering. + + If the texture does not have mipmapping support, enabling mipmapping has no + effect. + */ + + + +/*! + \fn QSGTexture::Filtering QSGOpaqueTextureMaterial::mipmapFiltering() const + + Returns this material's mipmap filtering mode. + + The default mipmap mode is QSGTexture::Nearest. + */ + + + +/*! + \fn void QSGOpaqueTextureMaterial::setFiltering(QSGTexture::Filtering filtering) + + Sets the filtering to \a filtering. + + The filtering mode is set on the texture instance just before the texture + is bound for rendering. + */ + + + +/*! + \fn QSGTexture::Filtering filtering() const + + Returns this material's filtering mode. + + The default filtering is QSGTexture::Nearest. + */ + + + +/*! + \fn void setHorizontalWrapMode(QSGTexture::WrapMode mode) + + Sets the horizontal wrap mode to \a mode. + + The horizontal wrap mode is set on the texture instance just before the texture + is bound for rendering. + */ + + + + /*! + \fn QSGTexture::WrapMode horizontalWrapMode() const + + Returns this material's horizontal wrap mode. + + The default horizontal wrap mode is QSGTexutre::ClampToEdge + */ + + + +/*! + \fn void setVerticalWrapMode(QSGTexture::WrapMode mode) + + Sets the vertical wrap mode to \a mode. + + The vertical wrap mode is set on the texture instance just before the texture + is bound for rendering. + */ + + + + /*! + \fn QSGTexture::WrapMode verticalWrapMode() const + + Returns this material's vertical wrap mode. + + The default vertical wrap mode is QSGTexutre::ClampToEdge + */ + + + +/*! + \internal + */ + +int QSGOpaqueTextureMaterial::compare(const QSGMaterial *o) const +{ + Q_ASSERT(o && type() == o->type()); + const QSGOpaqueTextureMaterial *other = static_cast<const QSGOpaqueTextureMaterial *>(o); + if (int diff = m_texture->textureId() - other->texture()->textureId()) + return diff; + return int(m_filtering) - int(other->m_filtering); +} + + + +/*! + \class QSGTextureMaterial + \brief The QSGTextureMaterial class provides a convenient way of + rendering textured geometry in the scene graph. + + The textured material will fill every pixel in a geometry with + the supplied texture. + + The geometry to be rendered with a texture material requires + vertices in attribute location 0 and texture coordinates in attribute + location 1. The texture coordinate is a 2-dimensional floating-point + tuple. The QSGGeometry::defaultAttributes_TexturedPoint2D returns an + attribute set compatible with this material. + + The texture to be rendered is set using setTexture(). How the + texure should be rendered can be specified using setMipmapFiltering(), + setFiltering(), setHorizontalWrapMode() and setVerticalWrapMode(). + The rendering state is set on the texture instance just before it + is bound. + + The textured material respects the current matrix and the alpha + channel of the texture. It will also respect the accumulated opacity + in the scenegraph. + + A texture material must have a texture set before it is used as + a material in the scene graph. + */ + +static const char qt_scenegraph_texture_material_opacity_fragment[] = + "varying highp vec2 qt_TexCoord; \n" + "uniform sampler2D qt_Texture; \n" + "uniform lowp float opacity; \n" + "void main() { \n" + " gl_FragColor = texture2D(qt_Texture, qt_TexCoord) * opacity; \n" + "}"; + +class QSGTextureMaterialShader : public QSGOpaqueTextureMaterialShader +{ +public: + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual void initialize(); + + static QSGMaterialType type; + +protected: + virtual const char *fragmentShader() const { return qt_scenegraph_texture_material_opacity_fragment; } + + int m_opacity_id; +}; +QSGMaterialType QSGTextureMaterialShader::type; + + + +/*! + \internal + */ + +QSGMaterialType *QSGTextureMaterial::type() const +{ + return &QSGTextureMaterialShader::type; +} + + + +/*! + \internal + */ + +QSGMaterialShader *QSGTextureMaterial::createShader() const +{ + return new QSGTextureMaterialShader; +} + +void QSGTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacity_id, state.opacity()); + + QSGOpaqueTextureMaterialShader::updateState(state, newEffect, oldEffect); +} + +void QSGTextureMaterialShader::initialize() +{ + QSGOpaqueTextureMaterialShader::initialize(); + m_opacity_id = program()->uniformLocation("opacity"); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgtexturematerial.h b/src/quick/scenegraph/util/qsgtexturematerial.h new file mode 100644 index 0000000000..b2b3ce6374 --- /dev/null +++ b/src/quick/scenegraph/util/qsgtexturematerial.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEXTUREMATERIAL_H +#define TEXTUREMATERIAL_H + +#include <QtQuick/qsgmaterial.h> +#include <QtQuick/qsgtexture.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGOpaqueTextureMaterial : public QSGMaterial +{ +public: + QSGOpaqueTextureMaterial(); + + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; + virtual int compare(const QSGMaterial *other) const; + + void setTexture(QSGTexture *texture); + QSGTexture *texture() const { return m_texture; } + + void setMipmapFiltering(QSGTexture::Filtering filtering) { m_mipmap_filtering = filtering; } + QSGTexture::Filtering mipmapFiltering() const { return (QSGTexture::Filtering) m_mipmap_filtering; } + + void setFiltering(QSGTexture::Filtering filtering) { m_filtering = filtering; } + QSGTexture::Filtering filtering() const { return (QSGTexture::Filtering) m_filtering; } + + void setHorizontalWrapMode(QSGTexture::WrapMode mode) { m_horizontal_wrap = mode; } + QSGTexture::WrapMode horizontalWrapMode() const { return (QSGTexture::WrapMode) m_horizontal_wrap; } + + void setVerticalWrapMode(QSGTexture::WrapMode mode) { m_vertical_wrap = mode; } + QSGTexture::WrapMode verticalWrapMode() const { return (QSGTexture::WrapMode) m_vertical_wrap; } + +protected: + QSGTexture *m_texture; + + uint m_filtering: 2; + uint m_mipmap_filtering: 2; + uint m_horizontal_wrap : 1; + uint m_vertical_wrap: 1; + + uint m_reserved : 26; +}; + + +class Q_QUICK_EXPORT QSGTextureMaterial : public QSGOpaqueTextureMaterial +{ +public: + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // TEXTUREMATERIAL_H diff --git a/src/quick/scenegraph/util/qsgtexturematerial_p.h b/src/quick/scenegraph/util/qsgtexturematerial_p.h new file mode 100644 index 0000000000..0ab552f4e9 --- /dev/null +++ b/src/quick/scenegraph/util/qsgtexturematerial_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEXTUREMATERIAL_P_H +#define TEXTUREMATERIAL_P_H + +#include "qsgtexturematerial.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGOpaqueTextureMaterialShader : public QSGMaterialShader +{ +public: + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual char const *const *attributeNames() const; + + static QSGMaterialType type; + +protected: + virtual void initialize(); + virtual const char *vertexShader() const; + virtual const char *fragmentShader() const; + + int m_matrix_id; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGTEXTUREMATERIAL_P_H diff --git a/src/quick/scenegraph/util/qsgtextureprovider.cpp b/src/quick/scenegraph/util/qsgtextureprovider.cpp new file mode 100644 index 0000000000..10faf2e5d4 --- /dev/null +++ b/src/quick/scenegraph/util/qsgtextureprovider.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgtextureprovider_p.h" + +#ifndef GL_CLAMP_TO_EDGE +#define GL_CLAMP_TO_EDGE 0x812F +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QSGTextureProvider + \brief The QSGTextureProvider class encapsulates texture based entities in QML. + + The QSGTextureProvider lives primarily in the scene graph rendering thread. + */ + + + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgtextureprovider_p.h b/src/quick/scenegraph/util/qsgtextureprovider_p.h new file mode 100644 index 0000000000..bc4ffec03d --- /dev/null +++ b/src/quick/scenegraph/util/qsgtextureprovider_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGTEXTUREPROVIDER_H +#define QSGTEXTUREPROVIDER_H + +#include "qsgtexture.h" +#include "qobject.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGTextureProvider : public QObject +{ + Q_OBJECT +public: + virtual QSGTexture *texture() const = 0; + +Q_SIGNALS: + void textureChanged(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp new file mode 100644 index 0000000000..8c6996642b --- /dev/null +++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgvertexcolormaterial.h" + +#include <qopenglshaderprogram.h> + +QT_BEGIN_NAMESPACE + +class QSGVertexColorMaterialShader : public QSGMaterialShader +{ +public: + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual char const *const *attributeNames() const; + + static QSGMaterialType type; + +private: + virtual void initialize(); + virtual const char *vertexShader() const; + virtual const char *fragmentShader() const; + + int m_matrix_id; + int m_opacity_id; +}; + +QSGMaterialType QSGVertexColorMaterialShader::type; + +void QSGVertexColorMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) +{ + if (!(newEffect->flags() & QSGMaterial::Blending) || state.isOpacityDirty()) + program()->setUniformValue(m_opacity_id, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); +} + +char const *const *QSGVertexColorMaterialShader::attributeNames() const +{ + static const char *const attr[] = { "vertexCoord", "vertexColor", 0 }; + return attr; +} + +void QSGVertexColorMaterialShader::initialize() +{ + m_matrix_id = program()->uniformLocation("matrix"); + m_opacity_id = program()->uniformLocation("opacity"); +} + +const char *QSGVertexColorMaterialShader::vertexShader() const { + return + "attribute highp vec4 vertexCoord; \n" + "attribute highp vec4 vertexColor; \n" + "uniform highp mat4 matrix; \n" + "uniform highp float opacity; \n" + "varying lowp vec4 color; \n" + "void main() { \n" + " gl_Position = matrix * vertexCoord; \n" + " color = vertexColor * opacity; \n" + "}"; +} + +const char *QSGVertexColorMaterialShader::fragmentShader() const { + return + "varying lowp vec4 color; \n" + "void main() { \n" + " gl_FragColor = color; \n" + "}"; +} + + + +/*! + \class QSGVertexColorMaterial + \brief The QSGVertexColorMaterial class provides a convenient way of rendering per-vertex + colored geometry in the scene graph. + + \inmodule QtQuick + + The vertex color material will give each vertex in a geometry a color. Pixels between + vertices will be linearly interpolated. The colors can contain transparency. + + The geometry to be rendered with vertex color must have the following layout. Attribute + position 0 must contain vertices. Attribute position 1 must contain colors, a tuple of + 4 values with RGBA layout. Both floats in the range of 0 to 1 and unsigned bytes in + the range 0 to 255 are valid for the color values. The + QSGGeometry::defaultAttributes_ColoredPoint2D() constructs an attribute set + compatible with this material. + + The vertex color material respects both current opacity and current matrix when + updating it's rendering state. + */ + + +QSGVertexColorMaterial::QSGVertexColorMaterial() +{ + setFlag(Blending, true); +} + + +/*! + int QSGVertexColorMaterial::compare() const + + As the vertex color material has all its state in the vertex attributes, + all materials will be equal. + + \internal + */ + +int QSGVertexColorMaterial::compare(const QSGMaterial * /* other */) const +{ + return 0; +} + +/*! + \internal + */ + +QSGMaterialType *QSGVertexColorMaterial::type() const +{ + return &QSGVertexColorMaterialShader::type; +} + + + +/*! + \internal + */ + +QSGMaterialShader *QSGVertexColorMaterial::createShader() const +{ + return new QSGVertexColorMaterialShader; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.h b/src/quick/scenegraph/util/qsgvertexcolormaterial.h new file mode 100644 index 0000000000..1d3b5a82e0 --- /dev/null +++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VERTEXCOLORMATERIAL_H +#define VERTEXCOLORMATERIAL_H + +#include <QtQuick/qsgmaterial.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGVertexColorMaterial : public QSGMaterial +{ +public: + QSGVertexColorMaterial(); + + int compare(const QSGMaterial *other) const; + +protected: + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // VERTEXCOLORMATERIAL_H |