diff options
Diffstat (limited to 'src/gui/opengl')
38 files changed, 23271 insertions, 0 deletions
diff --git a/src/gui/opengl/opengl.pri b/src/gui/opengl/opengl.pri new file mode 100644 index 0000000000..ed715d9f35 --- /dev/null +++ b/src/gui/opengl/opengl.pri @@ -0,0 +1,46 @@ +# Qt gui library, opengl module + +contains(QT_CONFIG, opengl):CONFIG += opengl +contains(QT_CONFIG, opengles2):CONFIG += opengles2 +contains(QT_CONFIG, egl):CONFIG += egl + +HEADERS += opengl/qopengl.h \ + opengl/qopengl_p.h \ + opengl/qopenglfunctions.h \ + opengl/qopenglframebufferobject.h \ + opengl/qopenglframebufferobject_p.h \ + opengl/qopenglpaintdevice_p.h \ + opengl/qopenglbuffer.h \ + opengl/qopenglshaderprogram.h \ + opengl/qopenglextensions_p.h \ + opengl/qopenglgradientcache_p.h \ + opengl/qopenglengineshadermanager_p.h \ + opengl/qopengl2pexvertexarray_p.h \ + opengl/qopenglpaintengine_p.h \ + opengl/qopenglengineshadersource_p.h \ + opengl/qopenglcustomshaderstage_p.h \ + opengl/qopengltriangulatingstroker_p.h \ + opengl/qopengltextureglyphcache_p.h \ + opengl/qopenglshadercache_p.h \ + opengl/qopenglshadercache_meego_p.h \ + opengl/qopenglcolormap.h \ + opengl/qtriangulator_p.h \ + opengl/qrbtree_p.h + +SOURCES += opengl/qopengl.cpp \ + opengl/qopenglfunctions.cpp \ + opengl/qopenglframebufferobject.cpp \ + opengl/qopenglpaintdevice.cpp \ + opengl/qopenglbuffer.cpp \ + opengl/qopenglshaderprogram.cpp \ + opengl/qopenglgradientcache.cpp \ + opengl/qopenglengineshadermanager.cpp \ + opengl/qopengl2pexvertexarray.cpp \ + opengl/qopenglpaintengine.cpp \ + opengl/qopenglcustomshaderstage.cpp \ + opengl/qopengltriangulatingstroker.cpp \ + opengl/qopengltextureglyphcache.cpp \ + opengl/qopenglcolormap.cpp \ + opengl/qtriangulator.cpp + +#INCLUDEPATH += ../3rdparty/harfbuzz/src diff --git a/src/gui/opengl/qopengl.cpp b/src/gui/opengl/qopengl.cpp new file mode 100644 index 0000000000..3233fcfa5b --- /dev/null +++ b/src/gui/opengl/qopengl.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qopengl_p.h" + +#include "qopenglcontext.h" + +QT_BEGIN_NAMESPACE + +QOpenGLExtensionMatcher::QOpenGLExtensionMatcher(const char *str) +{ + init(str); +} + +typedef GLubyte * (*qt_glGetStringi)(GLenum, GLuint); + +#ifndef GL_NUM_EXTENSIONS +#define GL_NUM_EXTENSIONS 0x821D +#endif + +QOpenGLExtensionMatcher::QOpenGLExtensionMatcher() +{ + const char *extensionStr = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)); + + if (extensionStr) { + init(extensionStr); + } else { + // clear error state + while (glGetError()) {} + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (ctx) { + qt_glGetStringi glGetStringi = (qt_glGetStringi)ctx->getProcAddress("glGetStringi"); + + if (!glGetStringi) + return; + + GLint numExtensions; + glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); + + for (int i = 0; i < numExtensions; ++i) { + const char *str = reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i)); + + m_offsets << m_extensions.size(); + + while (*str != 0) + m_extensions.append(*str++); + m_extensions.append(' '); + } + } + } +} + +void QOpenGLExtensionMatcher::init(const char *str) +{ + m_extensions = str; + + // make sure extension string ends with a space + if (!m_extensions.endsWith(' ')) + m_extensions.append(' '); + + int index = 0; + int next = 0; + while ((next = m_extensions.indexOf(' ', index)) >= 0) { + m_offsets << index; + index = next + 1; + } +} + + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopengl.h b/src/gui/opengl/qopengl.h new file mode 100644 index 0000000000..cef4277d6b --- /dev/null +++ b/src/gui/opengl/qopengl.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGL_H +#define QOPENGL_H + +#include <qglobal.h> + +QT_BEGIN_HEADER + +#if 1 +#if defined(QT_OPENGL_ES_2) +# if defined(Q_OS_MAC) +# include <OpenGLES/ES2/gl.h> +# else +# include <GLES2/gl2.h> +# endif +# ifndef GL_DOUBLE +# define GL_DOUBLE GL_FLOAT +# endif +# ifndef GLdouble +typedef GLfloat GLdouble; +# endif +#else +# if defined(Q_OS_MAC) +# include <OpenGL/gl.h> +# else +# if defined(Q_OS_WIN) +# include <QtCore/qt_windows.h> +# endif +# include <GL/gl.h> +# endif +#endif +#endif + +QT_END_HEADER + +#endif // QOPENGL_H diff --git a/src/gui/opengl/qopengl2pexvertexarray.cpp b/src/gui/opengl/qopengl2pexvertexarray.cpp new file mode 100644 index 0000000000..ec26fdbf5b --- /dev/null +++ b/src/gui/opengl/qopengl2pexvertexarray.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qopengl2pexvertexarray_p.h" + +#include <private/qbezier_p.h> + +QT_BEGIN_NAMESPACE + +void QOpenGL2PEXVertexArray::clear() +{ + vertexArray.reset(); + vertexArrayStops.reset(); + boundingRectDirty = true; +} + + +QOpenGLRect QOpenGL2PEXVertexArray::boundingRect() const +{ + if (boundingRectDirty) + return QOpenGLRect(0.0, 0.0, 0.0, 0.0); + else + return QOpenGLRect(minX, minY, maxX, maxY); +} + +void QOpenGL2PEXVertexArray::addClosingLine(int index) +{ + QPointF point(vertexArray.at(index)); + if (point != QPointF(vertexArray.last())) + vertexArray.add(point); +} + +void QOpenGL2PEXVertexArray::addCentroid(const QVectorPath &path, int subPathIndex) +{ + const QPointF *const points = reinterpret_cast<const QPointF *>(path.points()); + const QPainterPath::ElementType *const elements = path.elements(); + + QPointF sum = points[subPathIndex]; + int count = 1; + + for (int i = subPathIndex + 1; i < path.elementCount() && (!elements || elements[i] != QPainterPath::MoveToElement); ++i) { + sum += points[i]; + ++count; + } + + const QPointF centroid = sum / qreal(count); + vertexArray.add(centroid); +} + +void QOpenGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline) +{ + const QPointF* const points = reinterpret_cast<const QPointF*>(path.points()); + const QPainterPath::ElementType* const elements = path.elements(); + + if (boundingRectDirty) { + minX = maxX = points[0].x(); + minY = maxY = points[0].y(); + boundingRectDirty = false; + } + + if (!outline && !path.isConvex()) + addCentroid(path, 0); + + int lastMoveTo = vertexArray.size(); + vertexArray.add(points[0]); // The first element is always a moveTo + + do { + if (!elements) { +// qDebug("QVectorPath has no elements"); + // If the path has a null elements pointer, the elements implicitly + // start with a moveTo (already added) and continue with lineTos: + for (int i=1; i<path.elementCount(); ++i) + lineToArray(points[i].x(), points[i].y()); + + break; + } +// qDebug("QVectorPath has element types"); + + for (int i=1; i<path.elementCount(); ++i) { + switch (elements[i]) { + case QPainterPath::MoveToElement: + if (!outline) + addClosingLine(lastMoveTo); +// qDebug("element[%d] is a MoveToElement", i); + vertexArrayStops.add(vertexArray.size()); + if (!outline) { + if (!path.isConvex()) addCentroid(path, i); + lastMoveTo = vertexArray.size(); + } + lineToArray(points[i].x(), points[i].y()); // Add the moveTo as a new vertex + break; + case QPainterPath::LineToElement: +// qDebug("element[%d] is a LineToElement", i); + lineToArray(points[i].x(), points[i].y()); + break; + case QPainterPath::CurveToElement: { + QBezier b = QBezier::fromPoints(*(((const QPointF *) points) + i - 1), + points[i], + points[i+1], + points[i+2]); + QRectF bounds = b.bounds(); + // threshold based on same algorithm as in qtriangulatingstroker.cpp + int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * 3.14f / (curveInverseScale * 6)); + if (threshold < 3) threshold = 3; + qreal one_over_threshold_minus_1 = qreal(1) / (threshold - 1); + for (int t=0; t<threshold; ++t) { + QPointF pt = b.pointAt(t * one_over_threshold_minus_1); + lineToArray(pt.x(), pt.y()); + } + i += 2; + break; } + default: + break; + } + } + } while (0); + + if (!outline) + addClosingLine(lastMoveTo); + vertexArrayStops.add(vertexArray.size()); +} + +void QOpenGL2PEXVertexArray::lineToArray(const GLfloat x, const GLfloat y) +{ + vertexArray.add(QOpenGLPoint(x, y)); + + if (x > maxX) + maxX = x; + else if (x < minX) + minX = x; + if (y > maxY) + maxY = y; + else if (y < minY) + minY = y; +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopengl2pexvertexarray_p.h b/src/gui/opengl/qopengl2pexvertexarray_p.h new file mode 100644 index 0000000000..5ad4f7a237 --- /dev/null +++ b/src/gui/opengl/qopengl2pexvertexarray_p.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** 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 QtGui 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$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef QOPENGL2PEXVERTEXARRAY_P_H +#define QOPENGL2PEXVERTEXARRAY_P_H + +#include <QRectF> + +#include <private/qdatabuffer_p.h> +#include <private/qvectorpath_p.h> +#include <private/qopenglcontext_p.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLPoint +{ +public: + QOpenGLPoint(GLfloat new_x, GLfloat new_y) : + x(new_x), y(new_y) {}; + + QOpenGLPoint(const QPointF &p) : + x(p.x()), y(p.y()) {}; + + QOpenGLPoint(const QPointF* p) : + x(p->x()), y(p->y()) {}; + + GLfloat x; + GLfloat y; + + operator QPointF() {return QPointF(x,y);} + operator QPointF() const {return QPointF(x,y);} +}; + +struct QOpenGLRect +{ + QOpenGLRect(const QRectF &r) + : left(r.left()), top(r.top()), right(r.right()), bottom(r.bottom()) {} + + QOpenGLRect(GLfloat l, GLfloat t, GLfloat r, GLfloat b) + : left(l), top(t), right(r), bottom(b) {} + + GLfloat left; + GLfloat top; + GLfloat right; + GLfloat bottom; + + operator QRectF() const {return QRectF(left, top, right-left, bottom-top);} +}; + +class QOpenGL2PEXVertexArray +{ +public: + QOpenGL2PEXVertexArray() : + vertexArray(0), vertexArrayStops(0), + maxX(-2e10), maxY(-2e10), minX(2e10), minY(2e10), + boundingRectDirty(true) + { } + + inline void addRect(const QRectF &rect) + { + qreal top = rect.top(); + qreal left = rect.left(); + qreal bottom = rect.bottom(); + qreal right = rect.right(); + + vertexArray << QOpenGLPoint(left, top) + << QOpenGLPoint(right, top) + << QOpenGLPoint(right, bottom) + << QOpenGLPoint(right, bottom) + << QOpenGLPoint(left, bottom) + << QOpenGLPoint(left, top); + } + + inline void addQuad(const QRectF &rect) + { + qreal top = rect.top(); + qreal left = rect.left(); + qreal bottom = rect.bottom(); + qreal right = rect.right(); + + vertexArray << QOpenGLPoint(left, top) + << QOpenGLPoint(right, top) + << QOpenGLPoint(left, bottom) + << QOpenGLPoint(right, bottom); + + } + + inline void addVertex(const GLfloat x, const GLfloat y) + { + vertexArray.add(QOpenGLPoint(x, y)); + } + + void addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline = true); + void clear(); + + QOpenGLPoint* data() {return vertexArray.data();} + int *stops() const { return vertexArrayStops.data(); } + int stopCount() const { return vertexArrayStops.size(); } + QOpenGLRect boundingRect() const; + + int vertexCount() const { return vertexArray.size(); } + + void lineToArray(const GLfloat x, const GLfloat y); + +private: + QDataBuffer<QOpenGLPoint> vertexArray; + QDataBuffer<int> vertexArrayStops; + + GLfloat maxX; + GLfloat maxY; + GLfloat minX; + GLfloat minY; + bool boundingRectDirty; + void addClosingLine(int index); + void addCentroid(const QVectorPath &path, int subPathIndex); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/opengl/qopengl_p.h b/src/gui/opengl/qopengl_p.h new file mode 100644 index 0000000000..309ea22acd --- /dev/null +++ b/src/gui/opengl/qopengl_p.h @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGL_P_H +#define QOPENGL_P_H + +#include <qopengl.h> +#include <private/qopenglcontext_p.h> + +#include <qthreadstorage.h> +#include <qcache.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QOpenGLExtensionMatcher +{ +public: + QOpenGLExtensionMatcher(const char *str); + QOpenGLExtensionMatcher(); + + bool match(const char *str) const { + int str_length = qstrlen(str); + + Q_ASSERT(str); + Q_ASSERT(str_length > 0); + Q_ASSERT(str[str_length-1] != ' '); + + for (int i = 0; i < m_offsets.size(); ++i) { + const char *extension = m_extensions.constData() + m_offsets.at(i); + if (qstrncmp(extension, str, str_length) == 0 && extension[str_length] == ' ') + return true; + } + return false; + } + +private: + void init(const char *str); + + QByteArray m_extensions; + QVector<int> m_offsets; +}; + +// this is a class that wraps a QThreadStorage object for storing +// thread local instances of the GL 1 and GL 2 paint engines + +class QPaintEngine; + +template <class T> +class QOpenGLEngineThreadStorage +{ +public: + QPaintEngine *engine() { + QPaintEngine *&localEngine = storage.localData(); + if (!localEngine) + localEngine = new T; + return localEngine; + } + +private: + QThreadStorage<QPaintEngine *> storage; +}; + +class QOpenGLTexture : public QOpenGLSharedResource { +public: + QOpenGLTexture(QOpenGLContext *ctx, GLuint id, bool inverted) + : QOpenGLSharedResource(ctx->shareGroup()) + , m_id(id) + , m_inverted(inverted) + { + } + + GLuint id() const { return m_id; } + bool invertedY() const { return m_inverted; } + +protected: + void invalidateResource() + { + m_id = 0; + } + + void freeResource(QOpenGLContext *) + { + glDeleteTextures(1, &m_id); + } + +private: + GLuint m_id; + bool m_inverted; +}; + +struct QOpenGLTextureCacheKey { + qint64 key; + QOpenGLContextGroup *group; +}; + +inline bool operator==(const QOpenGLTextureCacheKey &a, const QOpenGLTextureCacheKey &b) +{ + return a.key == b.key && a.group == b.group; +} + +inline uint qHash(const QOpenGLTextureCacheKey &key) +{ + return qHash(key.key) ^ qHash(key.group); +} + +class QPlatformPixmap; + +class QOpenGLTextureCache { +public: + QOpenGLTextureCache(); + ~QOpenGLTextureCache(); + + void insert(QOpenGLContext *ctx, qint64 key, QOpenGLTexture *texture, int cost); + void remove(qint64 key); + inline int size(); + inline void setMaxCost(int newMax); + inline int maxCost(); + inline QOpenGLTexture* getTexture(QOpenGLContext *ctx, qint64 key); + + bool remove(QOpenGLContext *ctx, GLuint textureId); + void removeContextTextures(QOpenGLContext *ctx); + static QOpenGLTextureCache *instance(); + static void cleanupTexturesForCacheKey(qint64 cacheKey); + static void cleanupTexturesForPixampData(QPlatformPixmap* pixmap); + static void cleanupBeforePixmapDestruction(QPlatformPixmap* pixmap); + +private: + QCache<QOpenGLTextureCacheKey, QOpenGLTexture> m_cache; + QReadWriteLock m_lock; +}; + +int QOpenGLTextureCache::size() { + QReadLocker locker(&m_lock); + return m_cache.size(); +} + +void QOpenGLTextureCache::setMaxCost(int newMax) +{ + QWriteLocker locker(&m_lock); + m_cache.setMaxCost(newMax); +} + +int QOpenGLTextureCache::maxCost() +{ + QReadLocker locker(&m_lock); + return m_cache.maxCost(); +} + +QOpenGLTexture* QOpenGLTextureCache::getTexture(QOpenGLContext *ctx, qint64 key) +{ + QReadLocker locker(&m_lock); + const QOpenGLTextureCacheKey cacheKey = { key, ctx->shareGroup() }; + return m_cache.object(cacheKey); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QOPENGL_H diff --git a/src/gui/opengl/qopenglbuffer.cpp b/src/gui/opengl/qopenglbuffer.cpp new file mode 100644 index 0000000000..bdd38018fa --- /dev/null +++ b/src/gui/opengl/qopenglbuffer.cpp @@ -0,0 +1,567 @@ +/**************************************************************************** +** +** 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 QtGui 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 <QtGui/qopengl.h> +#include <QtGui/private/qopenglcontext_p.h> +#include <QtCore/qatomic.h> +#include "qopenglbuffer.h" +#include <private/qopenglextensions_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QOpenGLBuffer + \brief The QOpenGLBuffer class provides functions for creating and managing GL buffer objects. + \since 5.0 + \ingroup painting-3D + + Buffer objects are created in the GL server so that the + client application can avoid uploading vertices, indices, + texture image data, etc every time they are needed. + + QOpenGLBuffer objects can be copied around as a reference to the + underlying GL buffer object: + + \code + QOpenGLBuffer buffer1(QOpenGLBuffer::IndexBuffer); + buffer1.create(); + + QOpenGLBuffer buffer2 = buffer1; + \endcode + + QOpenGLBuffer performs a shallow copy when objects are copied in this + manner, but does not implement copy-on-write semantics. The original + object will be affected whenever the copy is modified. +*/ + +/*! + \enum QOpenGLBuffer::Type + This enum defines the type of GL buffer object to create with QOpenGLBuffer. + + \value VertexBuffer Vertex buffer object for use when specifying + vertex arrays. + \value IndexBuffer Index buffer object for use with \c{glDrawElements()}. + \value PixelPackBuffer Pixel pack buffer object for reading pixel + data from the GL server (for example, with \c{glReadPixels()}). + Not supported under OpenGL/ES. + \value PixelUnpackBuffer Pixel unpack buffer object for writing pixel + data to the GL server (for example, with \c{glTexImage2D()}). + Not supported under OpenGL/ES. +*/ + +/*! + \enum QOpenGLBuffer::UsagePattern + This enum defines the usage pattern of a QOpenGLBuffer object. + + \value StreamDraw The data will be set once and used a few times + for drawing operations. Under OpenGL/ES 1.1 this is identical + to StaticDraw. + \value StreamRead The data will be set once and used a few times + for reading data back from the GL server. Not supported + under OpenGL/ES. + \value StreamCopy The data will be set once and used a few times + for reading data back from the GL server for use in further + drawing operations. Not supported under OpenGL/ES. + \value StaticDraw The data will be set once and used many times + for drawing operations. + \value StaticRead The data will be set once and used many times + for reading data back from the GL server. Not supported + under OpenGL/ES. + \value StaticCopy The data will be set once and used many times + for reading data back from the GL server for use in further + drawing operations. Not supported under OpenGL/ES. + \value DynamicDraw The data will be modified repeatedly and used + many times for drawing operations. + \value DynamicRead The data will be modified repeatedly and used + many times for reading data back from the GL server. + Not supported under OpenGL/ES. + \value DynamicCopy The data will be modified repeatedly and used + many times for reading data back from the GL server for + use in further drawing operations. Not supported under OpenGL/ES. +*/ + +/*! + \enum QOpenGLBuffer::Access + This enum defines the access mode for QOpenGLBuffer::map(). + + \value ReadOnly The buffer will be mapped for reading only. + \value WriteOnly The buffer will be mapped for writing only. + \value ReadWrite The buffer will be mapped for reading and writing. +*/ + +class QOpenGLBufferPrivate +{ +public: + QOpenGLBufferPrivate(QOpenGLBuffer::Type t) + : ref(1), + type(t), + guard(0), + usagePattern(QOpenGLBuffer::StaticDraw), + actualUsagePattern(QOpenGLBuffer::StaticDraw), + funcs(0) + { + } + + QAtomicInt ref; + QOpenGLBuffer::Type type; + QOpenGLSharedResourceGuard *guard; + QOpenGLBuffer::UsagePattern usagePattern; + QOpenGLBuffer::UsagePattern actualUsagePattern; + QOpenGLExtensions *funcs; +}; + +/*! + Constructs a new buffer object of type QOpenGLBuffer::VertexBuffer. + + Note: this constructor just creates the QOpenGLBuffer instance. The actual + buffer object in the GL server is not created until create() is called. + + \sa create() +*/ +QOpenGLBuffer::QOpenGLBuffer() + : d_ptr(new QOpenGLBufferPrivate(QOpenGLBuffer::VertexBuffer)) +{ +} + +/*! + Constructs a new buffer object of \a type. + + Note: this constructor just creates the QOpenGLBuffer instance. The actual + buffer object in the GL server is not created until create() is called. + + \sa create() +*/ +QOpenGLBuffer::QOpenGLBuffer(QOpenGLBuffer::Type type) + : d_ptr(new QOpenGLBufferPrivate(type)) +{ +} + +/*! + Constructs a shallow copy of \a other. + + Note: QOpenGLBuffer does not implement copy-on-write semantics, + so \a other will be affected whenever the copy is modified. +*/ +QOpenGLBuffer::QOpenGLBuffer(const QOpenGLBuffer &other) + : d_ptr(other.d_ptr) +{ + d_ptr->ref.ref(); +} + +/*! + Destroys this buffer object, including the storage being + used in the GL server. +*/ +QOpenGLBuffer::~QOpenGLBuffer() +{ + if (!d_ptr->ref.deref()) { + destroy(); + delete d_ptr; + } +} + +/*! + Assigns a shallow copy of \a other to this object. + + Note: QOpenGLBuffer does not implement copy-on-write semantics, + so \a other will be affected whenever the copy is modified. +*/ +QOpenGLBuffer &QOpenGLBuffer::operator=(const QOpenGLBuffer &other) +{ + if (d_ptr != other.d_ptr) { + other.d_ptr->ref.ref(); + if (!d_ptr->ref.deref()) + destroy(); + d_ptr = other.d_ptr; + } + return *this; +} + +/*! + Returns the type of buffer represented by this object. +*/ +QOpenGLBuffer::Type QOpenGLBuffer::type() const +{ + Q_D(const QOpenGLBuffer); + return d->type; +} + +/*! + Returns the usage pattern for this buffer object. + The default value is StaticDraw. + + \sa setUsagePattern() +*/ +QOpenGLBuffer::UsagePattern QOpenGLBuffer::usagePattern() const +{ + Q_D(const QOpenGLBuffer); + return d->usagePattern; +} + +/*! + Sets the usage pattern for this buffer object to \a value. + This function must be called before allocate() or write(). + + \sa usagePattern(), allocate(), write() +*/ +void QOpenGLBuffer::setUsagePattern(QOpenGLBuffer::UsagePattern value) +{ + Q_D(QOpenGLBuffer); + d->usagePattern = d->actualUsagePattern = value; +} + +namespace { + void freeBufferFunc(QOpenGLFunctions *funcs, GLuint id) + { + funcs->glDeleteBuffers(1, &id); + } +} + +/*! + Creates the buffer object in the GL server. Returns true if + the object was created; false otherwise. + + This function must be called with a current QOpenGLContext. + The buffer will be bound to and can only be used in + that context (or any other context that is shared with it). + + This function will return false if the GL implementation + does not support buffers, or there is no current QOpenGLContext. + + \sa isCreated(), allocate(), write(), destroy() +*/ +bool QOpenGLBuffer::create() +{ + Q_D(QOpenGLBuffer); + if (d->guard && d->guard->id()) + return true; + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (ctx) { + delete d->funcs; + d->funcs = new QOpenGLExtensions(ctx); + GLuint bufferId = 0; + d->funcs->glGenBuffers(1, &bufferId); + if (bufferId) { + if (d->guard) + d->guard->free(); + + d->guard = new QOpenGLSharedResourceGuard(ctx, bufferId, freeBufferFunc); + return true; + } + } + return false; +} + +/*! + Returns true if this buffer has been created; false otherwise. + + \sa create(), destroy() +*/ +bool QOpenGLBuffer::isCreated() const +{ + Q_D(const QOpenGLBuffer); + return d->guard && d->guard->id(); +} + +/*! + Destroys this buffer object, including the storage being + used in the GL server. All references to the buffer will + become invalid. +*/ +void QOpenGLBuffer::destroy() +{ + Q_D(QOpenGLBuffer); + if (d->guard) { + d->guard->free(); + d->guard = 0; + } +} + +/*! + Reads the \a count bytes in this buffer starting at \a offset + into \a data. Returns true on success; false if reading from + the buffer is not supported. Buffer reading is not supported + under OpenGL/ES. + + It is assumed that this buffer has been bound to the current context. + + \sa write(), bind() +*/ +bool QOpenGLBuffer::read(int offset, void *data, int count) +{ +#if !defined(QT_OPENGL_ES) + Q_D(QOpenGLBuffer); + if (!d->funcs->hasOpenGLFeature(QOpenGLFunctions::Buffers) || !d->guard->id()) + return false; + while (glGetError() != GL_NO_ERROR) ; // Clear error state. + d->funcs->glGetBufferSubData(d->type, offset, count, data); + return glGetError() == GL_NO_ERROR; +#else + Q_UNUSED(offset); + Q_UNUSED(data); + Q_UNUSED(count); + return false; +#endif +} + +/*! + Replaces the \a count bytes of this buffer starting at \a offset + with the contents of \a data. Any other bytes in the buffer + will be left unmodified. + + It is assumed that create() has been called on this buffer and that + it has been bound to the current context. + + \sa create(), read(), allocate() +*/ +void QOpenGLBuffer::write(int offset, const void *data, int count) +{ +#ifndef QT_NO_DEBUG + if (!isCreated()) + qWarning("QOpenGLBuffer::allocate(): buffer not created"); +#endif + Q_D(QOpenGLBuffer); + if (d->guard && d->guard->id()) + d->funcs->glBufferSubData(d->type, offset, count, data); +} + +/*! + Allocates \a count bytes of space to the buffer, initialized to + the contents of \a data. Any previous contents will be removed. + + It is assumed that create() has been called on this buffer and that + it has been bound to the current context. + + \sa create(), read(), write() +*/ +void QOpenGLBuffer::allocate(const void *data, int count) +{ +#ifndef QT_NO_DEBUG + if (!isCreated()) + qWarning("QOpenGLBuffer::allocate(): buffer not created"); +#endif + Q_D(QOpenGLBuffer); + if (d->guard && d->guard->id()) + d->funcs->glBufferData(d->type, count, data, d->actualUsagePattern); +} + +/*! + \fn void QOpenGLBuffer::allocate(int count) + \overload + + Allocates \a count bytes of space to the buffer. Any previous + contents will be removed. + + It is assumed that create() has been called on this buffer and that + it has been bound to the current context. + + \sa create(), write() +*/ + +/*! + Binds the buffer associated with this object to the current + GL context. Returns false if binding was not possible, usually because + type() is not supported on this GL implementation. + + The buffer must be bound to the same QOpenGLContext current when create() + was called, or to another QOpenGLContext that is sharing with it. + Otherwise, false will be returned from this function. + + \sa release(), create() +*/ +bool QOpenGLBuffer::bind() +{ +#ifndef QT_NO_DEBUG + if (!isCreated()) + qWarning("QOpenGLBuffer::bind(): buffer not created"); +#endif + Q_D(const QOpenGLBuffer); + GLuint bufferId = d->guard ? d->guard->id() : 0; + if (bufferId) { + if (d->guard->group() != QOpenGLContextGroup::currentContextGroup()) { +#ifndef QT_NO_DEBUG + qWarning("QOpenGLBuffer::bind: buffer is not valid in the current context"); +#endif + return false; + } + d->funcs->glBindBuffer(d->type, bufferId); + return true; + } else { + return false; + } +} + +/*! + Releases the buffer associated with this object from the + current GL context. + + This function must be called with the same QOpenGLContext current + as when bind() was called on the buffer. + + \sa bind() +*/ +void QOpenGLBuffer::release() +{ +#ifndef QT_NO_DEBUG + if (!isCreated()) + qWarning("QOpenGLBuffer::release(): buffer not created"); +#endif + Q_D(const QOpenGLBuffer); + if (d->guard && d->guard->id()) + d->funcs->glBindBuffer(d->type, 0); +} + +/*! + Releases the buffer associated with \a type in the current + QOpenGLContext. + + This function is a direct call to \c{glBindBuffer(type, 0)} + for use when the caller does not know which QOpenGLBuffer has + been bound to the context but wants to make sure that it + is released. + + \code + QOpenGLBuffer::release(QOpenGLBuffer::VertexBuffer); + \endcode +*/ +void QOpenGLBuffer::release(QOpenGLBuffer::Type type) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (ctx) + ctx->functions()->glBindBuffer(GLenum(type), 0); +} + +/*! + Returns the GL identifier associated with this buffer; zero if + the buffer has not been created. + + \sa isCreated() +*/ +GLuint QOpenGLBuffer::bufferId() const +{ + Q_D(const QOpenGLBuffer); + return d->guard ? d->guard->id() : 0; +} + +/*! + Returns the size of the data in this buffer, for reading operations. + Returns -1 if fetching the buffer size is not supported, or the + buffer has not been created. + + It is assumed that this buffer has been bound to the current context. + + \sa isCreated(), bind() +*/ +int QOpenGLBuffer::size() const +{ + Q_D(const QOpenGLBuffer); + if (!d->guard || !d->guard->id()) + return -1; + GLint value = -1; + d->funcs->glGetBufferParameteriv(d->type, GL_BUFFER_SIZE, &value); + return value; +} + +/*! + Maps the contents of this buffer into the application's memory + space and returns a pointer to it. Returns null if memory + mapping is not possible. The \a access parameter indicates the + type of access to be performed. + + It is assumed that create() has been called on this buffer and that + it has been bound to the current context. + + This function is only supported under OpenGL/ES if the + \c{GL_OES_mapbuffer} extension is present. + + \sa unmap(), create(), bind() +*/ +void *QOpenGLBuffer::map(QOpenGLBuffer::Access access) +{ + Q_D(QOpenGLBuffer); +#ifndef QT_NO_DEBUG + if (!isCreated()) + qWarning("QOpenGLBuffer::map(): buffer not created"); +#endif + if (!d->guard || !d->guard->id()) + return 0; +#if 0 + if (!glMapBufferARB) + return 0; + return glMapBufferARB(d->type, access); +#endif + Q_UNUSED(access); + qWarning("QOpenGLBuffer::map(): pending implementation"); + return 0; +} + +/*! + Unmaps the buffer after it was mapped into the application's + memory space with a previous call to map(). Returns true if + the unmap succeeded; false otherwise. + + It is assumed that this buffer has been bound to the current context, + and that it was previously mapped with map(). + + This function is only supported under OpenGL/ES if the + \c{GL_OES_mapbuffer} extension is present. + + \sa map() +*/ +bool QOpenGLBuffer::unmap() +{ + Q_D(QOpenGLBuffer); +#ifndef QT_NO_DEBUG + if (!isCreated()) + qWarning("QOpenGLBuffer::unmap(): buffer not created"); +#endif + if (!d->guard || !d->guard->id()) + return false; +#if 0 + if (!glUnmapBufferARB) + return false; + return glUnmapBufferARB(d->type) == GL_TRUE; +#endif + qWarning("QOpenGLBuffer::map(): pending implementation"); + return 0; +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglbuffer.h b/src/gui/opengl/qopenglbuffer.h new file mode 100644 index 0000000000..52a2c4d640 --- /dev/null +++ b/src/gui/opengl/qopenglbuffer.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGLBUFFER_H +#define QOPENGLBUFFER_H + +#include <QtCore/qscopedpointer.h> +#include <QtGui/qopengl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QOpenGLBufferPrivate; + +class Q_GUI_EXPORT QOpenGLBuffer +{ +public: + enum Type + { + VertexBuffer = 0x8892, // GL_ARRAY_BUFFER + IndexBuffer = 0x8893, // GL_ELEMENT_ARRAY_BUFFER + PixelPackBuffer = 0x88EB, // GL_PIXEL_PACK_BUFFER + PixelUnpackBuffer = 0x88EC // GL_PIXEL_UNPACK_BUFFER + }; + + QOpenGLBuffer(); + explicit QOpenGLBuffer(QOpenGLBuffer::Type type); + QOpenGLBuffer(const QOpenGLBuffer &other); + ~QOpenGLBuffer(); + + QOpenGLBuffer &operator=(const QOpenGLBuffer &other); + + enum UsagePattern + { + StreamDraw = 0x88E0, // GL_STREAM_DRAW + StreamRead = 0x88E1, // GL_STREAM_READ + StreamCopy = 0x88E2, // GL_STREAM_COPY + StaticDraw = 0x88E4, // GL_STATIC_DRAW + StaticRead = 0x88E5, // GL_STATIC_READ + StaticCopy = 0x88E6, // GL_STATIC_COPY + DynamicDraw = 0x88E8, // GL_DYNAMIC_DRAW + DynamicRead = 0x88E9, // GL_DYNAMIC_READ + DynamicCopy = 0x88EA // GL_DYNAMIC_COPY + }; + + enum Access + { + ReadOnly = 0x88B8, // GL_READ_ONLY + WriteOnly = 0x88B9, // GL_WRITE_ONLY + ReadWrite = 0x88BA // GL_READ_WRITE + }; + + QOpenGLBuffer::Type type() const; + + QOpenGLBuffer::UsagePattern usagePattern() const; + void setUsagePattern(QOpenGLBuffer::UsagePattern value); + + bool create(); + bool isCreated() const; + + void destroy(); + + bool bind(); + void release(); + + static void release(QOpenGLBuffer::Type type); + + GLuint bufferId() const; + + int size() const; + + bool read(int offset, void *data, int count); + void write(int offset, const void *data, int count); + + void allocate(const void *data, int count); + inline void allocate(int count) { allocate(0, count); } + + void *map(QOpenGLBuffer::Access access); + bool unmap(); + +private: + QOpenGLBufferPrivate *d_ptr; + + Q_DECLARE_PRIVATE(QOpenGLBuffer) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/opengl/qopenglcolormap.cpp b/src/gui/opengl/qopenglcolormap.cpp new file mode 100644 index 0000000000..386358ac23 --- /dev/null +++ b/src/gui/opengl/qopenglcolormap.cpp @@ -0,0 +1,297 @@ +/**************************************************************************** +** +** 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 QtGui 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$ +** +****************************************************************************/ + +/*! + \class QOpenGLColormap + \brief The QOpenGLColormap class is used for installing custom colormaps into + a QOpenGLWidget. + + \module OpenGL + \ingroup painting-3D + \ingroup shared + + QOpenGLColormap provides a platform independent way of specifying and + installing indexed colormaps for a QOpenGLWidget. QOpenGLColormap is + especially useful when using the OpenGL color-index mode. + + Under X11 you must use an X server that supports either a \c + PseudoColor or \c DirectColor visual class. If your X server + currently only provides a \c GrayScale, \c TrueColor, \c + StaticColor or \c StaticGray visual, you will not be able to + allocate colorcells for writing. If this is the case, try setting + your X server to 8 bit mode. It should then provide you with at + least a \c PseudoColor visual. Note that you may experience + colormap flashing if your X server is running in 8 bit mode. + + The size() of the colormap is always set to 256 + colors. Note that under Windows you can also install colormaps + in child widgets. + + This class uses \l{implicit sharing} as a memory and speed + optimization. + + Example of use: + \snippet doc/src/snippets/code/src_opengl_qopenglcolormap.cpp 0 + + \sa QOpenGLWidget::setColormap(), QOpenGLWidget::colormap() +*/ + +/*! + \fn Qt::HANDLE QOpenGLColormap::handle() + + \internal + + Returns the handle for this color map. +*/ + +/*! + \fn void QOpenGLColormap::setHandle(Qt::HANDLE handle) + + \internal + + Sets the handle for this color map to \a handle. +*/ + +#include "qopenglcolormap.h" + +QT_BEGIN_NAMESPACE + +QOpenGLColormap::QOpenGLColormapData QOpenGLColormap::shared_null = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0 }; + +/*! + Construct a QOpenGLColormap. +*/ +QOpenGLColormap::QOpenGLColormap() + : d(&shared_null) +{ + d->ref.ref(); +} + + +/*! + Construct a shallow copy of \a map. +*/ +QOpenGLColormap::QOpenGLColormap(const QOpenGLColormap &map) + : d(map.d) +{ + d->ref.ref(); +} + +/*! + Dereferences the QOpenGLColormap and deletes it if this was the last + reference to it. +*/ +QOpenGLColormap::~QOpenGLColormap() +{ + if (!d->ref.deref()) + cleanup(d); +} + +void QOpenGLColormap::cleanup(QOpenGLColormap::QOpenGLColormapData *x) +{ + delete x->cells; + x->cells = 0; + delete x; +} + +/*! + Assign a shallow copy of \a map to this QOpenGLColormap. +*/ +QOpenGLColormap & QOpenGLColormap::operator=(const QOpenGLColormap &map) +{ + map.d->ref.ref(); + if (!d->ref.deref()) + cleanup(d); + d = map.d; + return *this; +} + +/*! + \fn void QOpenGLColormap::detach() + \internal + + Detaches this QOpenGLColormap from the shared block. +*/ + +void QOpenGLColormap::detach_helper() +{ + QOpenGLColormapData *x = new QOpenGLColormapData; + x->ref = 1; + x->cmapHandle = 0; + x->cells = 0; + if (d->cells) { + x->cells = new QVector<QRgb>(256); + *x->cells = *d->cells; + } + if (!d->ref.deref()) + cleanup(d); + d = x; +} + +/*! + Set cell at index \a idx in the colormap to color \a color. +*/ +void QOpenGLColormap::setEntry(int idx, QRgb color) +{ + detach(); + if (!d->cells) + d->cells = new QVector<QRgb>(256); + d->cells->replace(idx, color); +} + +/*! + Set an array of cells in this colormap. \a count is the number of + colors that should be set, \a colors is the array of colors, and + \a base is the starting index. The first element in \a colors + is set at \a base in the colormap. +*/ +void QOpenGLColormap::setEntries(int count, const QRgb *colors, int base) +{ + detach(); + if (!d->cells) + d->cells = new QVector<QRgb>(256); + + Q_ASSERT_X(colors && base >= 0 && (base + count) <= d->cells->size(), "QOpenGLColormap::setEntries", + "preconditions not met"); + for (int i = 0; i < count; ++i) + setEntry(base + i, colors[i]); +} + +/*! + Returns the QRgb value in the colorcell with index \a idx. +*/ +QRgb QOpenGLColormap::entryRgb(int idx) const +{ + if (d == &shared_null || !d->cells) + return 0; + else + return d->cells->at(idx); +} + +/*! + \overload + + Set the cell with index \a idx in the colormap to color \a color. +*/ +void QOpenGLColormap::setEntry(int idx, const QColor &color) +{ + setEntry(idx, color.rgb()); +} + +/*! + Returns the QRgb value in the colorcell with index \a idx. +*/ +QColor QOpenGLColormap::entryColor(int idx) const +{ + if (d == &shared_null || !d->cells) + return QColor(); + else + return QColor(d->cells->at(idx)); +} + +/*! + Returns true if the colormap is empty or it is not in use + by a QOpenGLWidget; otherwise returns false. + + A colormap with no color values set is considered to be empty. + For historical reasons, a colormap that has color values set + but which is not in use by a QOpenGLWidget is also considered empty. + + Compare size() with zero to determine if the colormap is empty + regardless of whether it is in use by a QOpenGLWidget or not. + + \sa size() +*/ +bool QOpenGLColormap::isEmpty() const +{ + return d == &shared_null || d->cells == 0 || d->cells->size() == 0 || d->cmapHandle == 0; +} + + +/*! + Returns the number of colorcells in the colormap. +*/ +int QOpenGLColormap::size() const +{ + return d->cells ? d->cells->size() : 0; +} + +/*! + Returns the index of the color \a color. If \a color is not in the + map, -1 is returned. +*/ +int QOpenGLColormap::find(QRgb color) const +{ + if (d->cells) + return d->cells->indexOf(color); + return -1; +} + +/*! + Returns the index of the color that is the closest match to color + \a color. +*/ +int QOpenGLColormap::findNearest(QRgb color) const +{ + int idx = find(color); + if (idx >= 0) + return idx; + int mapSize = size(); + int mindist = 200000; + int r = qRed(color); + int g = qGreen(color); + int b = qBlue(color); + int rx, gx, bx, dist; + for (int i = 0; i < mapSize; ++i) { + QRgb ci = d->cells->at(i); + rx = r - qRed(ci); + gx = g - qGreen(ci); + bx = b - qBlue(ci); + dist = rx * rx + gx * gx + bx * bx; // calculate distance + if (dist < mindist) { // minimal? + mindist = dist; + idx = i; + } + } + return idx; +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglcolormap.h b/src/gui/opengl/qopenglcolormap.h new file mode 100644 index 0000000000..b05e3f331d --- /dev/null +++ b/src/gui/opengl/qopenglcolormap.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGLCOLORMAP_H +#define QOPENGLCOLORMAP_H + +#include <QtGui/qcolor.h> +#include <QtCore/qvector.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QOpenGLColormap +{ +public: + QOpenGLColormap(); + QOpenGLColormap(const QOpenGLColormap &); + ~QOpenGLColormap(); + + QOpenGLColormap &operator=(const QOpenGLColormap &); + + bool isEmpty() const; + int size() const; + void detach(); + + void setEntries(int count, const QRgb * colors, int base = 0); + void setEntry(int idx, QRgb color); + void setEntry(int idx, const QColor & color); + QRgb entryRgb(int idx) const; + QColor entryColor(int idx) const; + int find(QRgb color) const; + int findNearest(QRgb color) const; + +protected: + Qt::HANDLE handle() { return d ? d->cmapHandle : 0; } + void setHandle(Qt::HANDLE ahandle) { d->cmapHandle = ahandle; } + +private: + struct QOpenGLColormapData { + QBasicAtomicInt ref; + QVector<QRgb> *cells; + Qt::HANDLE cmapHandle; + }; + + QOpenGLColormapData *d; + static struct QOpenGLColormapData shared_null; + static void cleanup(QOpenGLColormapData *x); + void detach_helper(); + + friend class QOpenGLWidget; + friend class QOpenGLWidgetPrivate; +}; + +inline void QOpenGLColormap::detach() +{ + if (d->ref != 1) + detach_helper(); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QOPENGLCOLORMAP_H diff --git a/src/gui/opengl/qopenglcustomshaderstage.cpp b/src/gui/opengl/qopenglcustomshaderstage.cpp new file mode 100644 index 0000000000..6cedf66df1 --- /dev/null +++ b/src/gui/opengl/qopenglcustomshaderstage.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qopenglcustomshaderstage_p.h" +#include "qopenglengineshadermanager_p.h" +#include "qopenglpaintengine_p.h" +#include <private/qpainter_p.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLCustomShaderStagePrivate +{ +public: + QOpenGLCustomShaderStagePrivate() : + m_manager(0) {} + + QPointer<QOpenGLEngineShaderManager> m_manager; + QByteArray m_source; +}; + + + + +QOpenGLCustomShaderStage::QOpenGLCustomShaderStage() + : d_ptr(new QOpenGLCustomShaderStagePrivate) +{ +} + +QOpenGLCustomShaderStage::~QOpenGLCustomShaderStage() +{ + Q_D(QOpenGLCustomShaderStage); + if (d->m_manager) { + d->m_manager->removeCustomStage(); + d->m_manager->sharedShaders->cleanupCustomStage(this); + } +} + +void QOpenGLCustomShaderStage::setUniformsDirty() +{ + Q_D(QOpenGLCustomShaderStage); + if (d->m_manager) + d->m_manager->setDirty(); // ### Probably a bit overkill! +} + +bool QOpenGLCustomShaderStage::setOnPainter(QPainter* p) +{ + Q_D(QOpenGLCustomShaderStage); + if (p->paintEngine()->type() != QPaintEngine::OpenGL2) { + qWarning("QOpenGLCustomShaderStage::setOnPainter() - paint engine not OpenGL2"); + return false; + } + if (d->m_manager) + qWarning("Custom shader is already set on a painter"); + + QOpenGL2PaintEngineEx *engine = static_cast<QOpenGL2PaintEngineEx*>(p->paintEngine()); + d->m_manager = QOpenGL2PaintEngineExPrivate::shaderManagerForEngine(engine); + Q_ASSERT(d->m_manager); + + d->m_manager->setCustomStage(this); + return true; +} + +void QOpenGLCustomShaderStage::removeFromPainter(QPainter* p) +{ + Q_D(QOpenGLCustomShaderStage); + if (p->paintEngine()->type() != QPaintEngine::OpenGL2) + return; + + QOpenGL2PaintEngineEx *engine = static_cast<QOpenGL2PaintEngineEx*>(p->paintEngine()); + d->m_manager = QOpenGL2PaintEngineExPrivate::shaderManagerForEngine(engine); + Q_ASSERT(d->m_manager); + + // Just set the stage to null, don't call removeCustomStage(). + // This should leave the program in a compiled/linked state + // if the next custom shader stage is this one again. + d->m_manager->setCustomStage(0); + d->m_manager = 0; +} + +QByteArray QOpenGLCustomShaderStage::source() const +{ + Q_D(const QOpenGLCustomShaderStage); + return d->m_source; +} + +// Called by the shader manager if another custom shader is attached or +// the manager is deleted +void QOpenGLCustomShaderStage::setInactive() +{ + Q_D(QOpenGLCustomShaderStage); + d->m_manager = 0; +} + +void QOpenGLCustomShaderStage::setSource(const QByteArray& s) +{ + Q_D(QOpenGLCustomShaderStage); + d->m_source = s; +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglcustomshaderstage_p.h b/src/gui/opengl/qopenglcustomshaderstage_p.h new file mode 100644 index 0000000000..de459c0050 --- /dev/null +++ b/src/gui/opengl/qopenglcustomshaderstage_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGL_CUSTOM_SHADER_STAGE_H +#define QOPENGL_CUSTOM_SHADER_STAGE_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 <QOpenGLShaderProgram> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPainter; +class QOpenGLCustomShaderStagePrivate; +class Q_GUI_EXPORT QOpenGLCustomShaderStage +{ + Q_DECLARE_PRIVATE(QOpenGLCustomShaderStage) +public: + QOpenGLCustomShaderStage(); + virtual ~QOpenGLCustomShaderStage(); + virtual void setUniforms(QOpenGLShaderProgram*) {} + + void setUniformsDirty(); + + bool setOnPainter(QPainter*); + void removeFromPainter(QPainter*); + QByteArray source() const; + + void setInactive(); +protected: + void setSource(const QByteArray&); + +private: + QOpenGLCustomShaderStagePrivate* d_ptr; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + + +#endif diff --git a/src/gui/opengl/qopenglengineshadermanager.cpp b/src/gui/opengl/qopenglengineshadermanager.cpp new file mode 100644 index 0000000000..aaca6ad89c --- /dev/null +++ b/src/gui/opengl/qopenglengineshadermanager.cpp @@ -0,0 +1,881 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qopenglengineshadermanager_p.h" +#include "qopenglengineshadersource_p.h" +#include "qopenglpaintengine_p.h" +#include "qopenglshadercache_p.h" + +#include <QtGui/private/qopenglcontext_p.h> +#include <QtCore/qthreadstorage.h> + +#if defined(QT_DEBUG) +#include <QMetaEnum> +#endif + +// #define QT_GL_SHARED_SHADER_DEBUG + +QT_BEGIN_NAMESPACE + +class QOpenGLEngineSharedShadersResource : public QOpenGLSharedResource +{ +public: + QOpenGLEngineSharedShadersResource(QOpenGLContext *ctx) + : QOpenGLSharedResource(ctx->shareGroup()) + , m_shaders(new QOpenGLEngineSharedShaders(ctx)) + { + } + + ~QOpenGLEngineSharedShadersResource() + { + delete m_shaders; + } + + void invalidateResource() + { + delete m_shaders; + m_shaders = 0; + } + + void freeResource(QOpenGLContext *) + { + } + + QOpenGLEngineSharedShaders *shaders() const { return m_shaders; } + +private: + QOpenGLEngineSharedShaders *m_shaders; +}; + +class QOpenGLShaderStorage +{ +public: + QOpenGLEngineSharedShaders *shadersForThread(QOpenGLContext *context) { + QOpenGLMultiGroupSharedResource *&shaders = m_storage.localData(); + if (!shaders) + shaders = new QOpenGLMultiGroupSharedResource; + QOpenGLEngineSharedShadersResource *resource = + shaders->value<QOpenGLEngineSharedShadersResource>(context); + return resource ? resource->shaders() : 0; + } + +private: + QThreadStorage<QOpenGLMultiGroupSharedResource *> m_storage; +}; + +Q_GLOBAL_STATIC(QOpenGLShaderStorage, qt_shader_storage); + +QOpenGLEngineSharedShaders *QOpenGLEngineSharedShaders::shadersForContext(QOpenGLContext *context) +{ + return qt_shader_storage()->shadersForThread(context); +} + +const char* QOpenGLEngineSharedShaders::qShaderSnippets[] = { + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0 +}; + +QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context) + : blitShaderProg(0) + , simpleShaderProg(0) +{ + +/* + Rather than having the shader source array statically initialised, it is initialised + here instead. This is to allow new shader names to be inserted or existing names moved + around without having to change the order of the glsl strings. It is hoped this will + make future hard-to-find runtime bugs more obvious and generally give more solid code. +*/ + static bool snippetsPopulated = false; + if (!snippetsPopulated) { + + const char** code = qShaderSnippets; // shortcut + + code[MainVertexShader] = qopenglslMainVertexShader; + code[MainWithTexCoordsVertexShader] = qopenglslMainWithTexCoordsVertexShader; + code[MainWithTexCoordsAndOpacityVertexShader] = qopenglslMainWithTexCoordsAndOpacityVertexShader; + + code[UntransformedPositionVertexShader] = qopenglslUntransformedPositionVertexShader; + code[PositionOnlyVertexShader] = qopenglslPositionOnlyVertexShader; + code[ComplexGeometryPositionOnlyVertexShader] = qopenglslComplexGeometryPositionOnlyVertexShader; + code[PositionWithPatternBrushVertexShader] = qopenglslPositionWithPatternBrushVertexShader; + code[PositionWithLinearGradientBrushVertexShader] = qopenglslPositionWithLinearGradientBrushVertexShader; + code[PositionWithConicalGradientBrushVertexShader] = qopenglslPositionWithConicalGradientBrushVertexShader; + code[PositionWithRadialGradientBrushVertexShader] = qopenglslPositionWithRadialGradientBrushVertexShader; + code[PositionWithTextureBrushVertexShader] = qopenglslPositionWithTextureBrushVertexShader; + code[AffinePositionWithPatternBrushVertexShader] = qopenglslAffinePositionWithPatternBrushVertexShader; + code[AffinePositionWithLinearGradientBrushVertexShader] = qopenglslAffinePositionWithLinearGradientBrushVertexShader; + code[AffinePositionWithConicalGradientBrushVertexShader] = qopenglslAffinePositionWithConicalGradientBrushVertexShader; + code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader; + code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader; + + code[MainFragmentShader_CMO] = qopenglslMainFragmentShader_CMO; + code[MainFragmentShader_CM] = qopenglslMainFragmentShader_CM; + code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO; + code[MainFragmentShader_M] = qopenglslMainFragmentShader_M; + code[MainFragmentShader_CO] = qopenglslMainFragmentShader_CO; + code[MainFragmentShader_C] = qopenglslMainFragmentShader_C; + code[MainFragmentShader_O] = qopenglslMainFragmentShader_O; + code[MainFragmentShader] = qopenglslMainFragmentShader; + code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays; + + code[ImageSrcFragmentShader] = qopenglslImageSrcFragmentShader; + code[ImageSrcWithPatternFragmentShader] = qopenglslImageSrcWithPatternFragmentShader; + code[NonPremultipliedImageSrcFragmentShader] = qopenglslNonPremultipliedImageSrcFragmentShader; + code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader; // Calls "customShader", which must be appended + code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader; + code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader; + code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader; + code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader; + code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader; + code[RadialGradientBrushSrcFragmentShader] = qopenglslRadialGradientBrushSrcFragmentShader; + code[ConicalGradientBrushSrcFragmentShader] = qopenglslConicalGradientBrushSrcFragmentShader; + code[ShockingPinkSrcFragmentShader] = qopenglslShockingPinkSrcFragmentShader; + + code[NoMaskFragmentShader] = ""; + code[MaskFragmentShader] = qopenglslMaskFragmentShader; + code[RgbMaskFragmentShaderPass1] = qopenglslRgbMaskFragmentShaderPass1; + code[RgbMaskFragmentShaderPass2] = qopenglslRgbMaskFragmentShaderPass2; + code[RgbMaskWithGammaFragmentShader] = ""; //### + + code[NoCompositionModeFragmentShader] = ""; + code[MultiplyCompositionModeFragmentShader] = ""; //### + code[ScreenCompositionModeFragmentShader] = ""; //### + code[OverlayCompositionModeFragmentShader] = ""; //### + code[DarkenCompositionModeFragmentShader] = ""; //### + code[LightenCompositionModeFragmentShader] = ""; //### + code[ColorDodgeCompositionModeFragmentShader] = ""; //### + code[ColorBurnCompositionModeFragmentShader] = ""; //### + code[HardLightCompositionModeFragmentShader] = ""; //### + code[SoftLightCompositionModeFragmentShader] = ""; //### + code[DifferenceCompositionModeFragmentShader] = ""; //### + code[ExclusionCompositionModeFragmentShader] = ""; //### + +#if defined(QT_DEBUG) + // Check that all the elements have been filled: + for (int i = 0; i < TotalSnippetCount; ++i) { + if (qShaderSnippets[i] == 0) { + qFatal("Shader snippet for %s (#%d) is missing!", + snippetNameStr(SnippetName(i)).constData(), i); + } + } +#endif + snippetsPopulated = true; + } + + QOpenGLShader* fragShader; + QOpenGLShader* vertexShader; + QByteArray vertexSource; + QByteArray fragSource; + + // Compile up the simple shader: + vertexSource.append(qShaderSnippets[MainVertexShader]); + vertexSource.append(qShaderSnippets[PositionOnlyVertexShader]); + + fragSource.append(qShaderSnippets[MainFragmentShader]); + fragSource.append(qShaderSnippets[ShockingPinkSrcFragmentShader]); + + simpleShaderProg = new QOpenGLShaderProgram; + + CachedShader simpleShaderCache(fragSource, vertexSource); + + bool inCache = simpleShaderCache.load(simpleShaderProg, context); + + if (!inCache) { + vertexShader = new QOpenGLShader(QOpenGLShader::Vertex); + shaders.append(vertexShader); + if (!vertexShader->compileSourceCode(vertexSource)) + qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile"); + + fragShader = new QOpenGLShader(QOpenGLShader::Fragment); + shaders.append(fragShader); + if (!fragShader->compileSourceCode(fragSource)) + qWarning("Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile"); + + simpleShaderProg->addShader(vertexShader); + simpleShaderProg->addShader(fragShader); + + simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + simpleShaderProg->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR); + simpleShaderProg->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR); + simpleShaderProg->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR); + } + + simpleShaderProg->link(); + + if (simpleShaderProg->isLinked()) { + if (!inCache) + simpleShaderCache.store(simpleShaderProg, context); + } else { + qCritical() << "Errors linking simple shader:" + << simpleShaderProg->log(); + } + + // Compile the blit shader: + vertexSource.clear(); + vertexSource.append(qShaderSnippets[MainWithTexCoordsVertexShader]); + vertexSource.append(qShaderSnippets[UntransformedPositionVertexShader]); + + fragSource.clear(); + fragSource.append(qShaderSnippets[MainFragmentShader]); + fragSource.append(qShaderSnippets[ImageSrcFragmentShader]); + + blitShaderProg = new QOpenGLShaderProgram; + + CachedShader blitShaderCache(fragSource, vertexSource); + + inCache = blitShaderCache.load(blitShaderProg, context); + + if (!inCache) { + vertexShader = new QOpenGLShader(QOpenGLShader::Vertex); + shaders.append(vertexShader); + if (!vertexShader->compileSourceCode(vertexSource)) + qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile"); + + fragShader = new QOpenGLShader(QOpenGLShader::Fragment); + shaders.append(fragShader); + if (!fragShader->compileSourceCode(fragSource)) + qWarning("Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile"); + + blitShaderProg->addShader(vertexShader); + blitShaderProg->addShader(fragShader); + + blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + blitShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + } + + blitShaderProg->link(); + if (blitShaderProg->isLinked()) { + if (!inCache) + blitShaderCache.store(blitShaderProg, context); + } else { + qCritical() << "Errors linking blit shader:" + << blitShaderProg->log(); + } + +#ifdef QT_GL_SHARED_SHADER_DEBUG + qDebug(" -> QOpenGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread()); +#endif +} + +QOpenGLEngineSharedShaders::~QOpenGLEngineSharedShaders() +{ +#ifdef QT_GL_SHARED_SHADER_DEBUG + qDebug(" -> ~QOpenGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread()); +#endif + qDeleteAll(shaders); + shaders.clear(); + + qDeleteAll(cachedPrograms); + cachedPrograms.clear(); + + if (blitShaderProg) { + delete blitShaderProg; + blitShaderProg = 0; + } + + if (simpleShaderProg) { + delete simpleShaderProg; + simpleShaderProg = 0; + } +} + +#if defined (QT_DEBUG) +QByteArray QOpenGLEngineSharedShaders::snippetNameStr(SnippetName name) +{ + QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("SnippetName")); + return QByteArray(m.valueToKey(name)); +} +#endif + +// The address returned here will only be valid until next time this function is called. +// The program is return bound. +QOpenGLEngineShaderProg *QOpenGLEngineSharedShaders::findProgramInCache(const QOpenGLEngineShaderProg &prog) +{ + for (int i = 0; i < cachedPrograms.size(); ++i) { + QOpenGLEngineShaderProg *cachedProg = cachedPrograms[i]; + if (*cachedProg == prog) { + // Move the program to the top of the list as a poor-man's cache algo + cachedPrograms.move(i, 0); + cachedProg->program->bind(); + return cachedProg; + } + } + + QScopedPointer<QOpenGLEngineShaderProg> newProg; + + do { + QByteArray fragSource; + // Insert the custom stage before the srcPixel shader to work around an ATI driver bug + // where you cannot forward declare a function that takes a sampler as argument. + if (prog.srcPixelFragShader == CustomImageSrcFragmentShader) + fragSource.append(prog.customStageSource); + fragSource.append(qShaderSnippets[prog.mainFragShader]); + fragSource.append(qShaderSnippets[prog.srcPixelFragShader]); + if (prog.compositionFragShader) + fragSource.append(qShaderSnippets[prog.compositionFragShader]); + if (prog.maskFragShader) + fragSource.append(qShaderSnippets[prog.maskFragShader]); + + QByteArray vertexSource; + vertexSource.append(qShaderSnippets[prog.mainVertexShader]); + vertexSource.append(qShaderSnippets[prog.positionVertexShader]); + + QScopedPointer<QOpenGLShaderProgram> shaderProgram(new QOpenGLShaderProgram); + + CachedShader shaderCache(fragSource, vertexSource); + bool inCache = shaderCache.load(shaderProgram.data(), QOpenGLContext::currentContext()); + + if (!inCache) { + + QScopedPointer<QOpenGLShader> fragShader(new QOpenGLShader(QOpenGLShader::Fragment)); + QByteArray description; +#if defined(QT_DEBUG) + // Name the shader for easier debugging + description.append("Fragment shader: main="); + description.append(snippetNameStr(prog.mainFragShader)); + description.append(", srcPixel="); + description.append(snippetNameStr(prog.srcPixelFragShader)); + if (prog.compositionFragShader) { + description.append(", composition="); + description.append(snippetNameStr(prog.compositionFragShader)); + } + if (prog.maskFragShader) { + description.append(", mask="); + description.append(snippetNameStr(prog.maskFragShader)); + } + fragShader->setObjectName(QString::fromLatin1(description)); +#endif + if (!fragShader->compileSourceCode(fragSource)) { + qWarning() << "Warning:" << description << "failed to compile!"; + break; + } + + QScopedPointer<QOpenGLShader> vertexShader(new QOpenGLShader(QOpenGLShader::Vertex)); +#if defined(QT_DEBUG) + // Name the shader for easier debugging + description.clear(); + description.append("Vertex shader: main="); + description.append(snippetNameStr(prog.mainVertexShader)); + description.append(", position="); + description.append(snippetNameStr(prog.positionVertexShader)); + vertexShader->setObjectName(QString::fromLatin1(description)); +#endif + if (!vertexShader->compileSourceCode(vertexSource)) { + qWarning() << "Warning:" << description << "failed to compile!"; + break; + } + + shaders.append(vertexShader.data()); + shaders.append(fragShader.data()); + shaderProgram->addShader(vertexShader.take()); + shaderProgram->addShader(fragShader.take()); + + // We have to bind the vertex attribute names before the program is linked: + shaderProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + if (prog.useTextureCoords) + shaderProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + if (prog.useOpacityAttribute) + shaderProgram->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR); + if (prog.usePmvMatrixAttribute) { + shaderProgram->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR); + shaderProgram->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR); + shaderProgram->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR); + } + } + + newProg.reset(new QOpenGLEngineShaderProg(prog)); + newProg->program = shaderProgram.take(); + + newProg->program->link(); + if (newProg->program->isLinked()) { + if (!inCache) + shaderCache.store(newProg->program, QOpenGLContext::currentContext()); + } else { + QLatin1String none("none"); + QLatin1String br("\n"); + QString error; + error = QLatin1String("Shader program failed to link,"); +#if defined(QT_DEBUG) + error += QLatin1String("\n Shaders Used:\n"); + for (int i = 0; i < newProg->program->shaders().count(); ++i) { + QOpenGLShader *shader = newProg->program->shaders().at(i); + error += QLatin1String(" ") + shader->objectName() + QLatin1String(": \n") + + QLatin1String(shader->sourceCode()) + br; + } +#endif + error += QLatin1String(" Error Log:\n") + + QLatin1String(" ") + newProg->program->log(); + qWarning() << error; + break; + } + + newProg->program->bind(); + + if (newProg->maskFragShader != QOpenGLEngineSharedShaders::NoMaskFragmentShader) { + GLuint location = newProg->program->uniformLocation("maskTexture"); + newProg->program->setUniformValue(location, QT_MASK_TEXTURE_UNIT); + } + + if (cachedPrograms.count() > 30) { + // The cache is full, so delete the last 5 programs in the list. + // These programs will be least used, as a program us bumped to + // the top of the list when it's used. + for (int i = 0; i < 5; ++i) { + delete cachedPrograms.last(); + cachedPrograms.removeLast(); + } + } + + cachedPrograms.insert(0, newProg.data()); + } while (false); + + return newProg.take(); +} + +void QOpenGLEngineSharedShaders::cleanupCustomStage(QOpenGLCustomShaderStage* stage) +{ + // Remove any shader programs which has this as the custom shader src: + for (int i = 0; i < cachedPrograms.size(); ++i) { + QOpenGLEngineShaderProg *cachedProg = cachedPrograms[i]; + if (cachedProg->customStageSource == stage->source()) { + delete cachedProg; + cachedPrograms.removeAt(i); + i--; + } + } +} + + +QOpenGLEngineShaderManager::QOpenGLEngineShaderManager(QOpenGLContext* context) + : ctx(context), + shaderProgNeedsChanging(true), + complexGeometry(false), + srcPixelType(Qt::NoBrush), + opacityMode(NoOpacity), + maskType(NoMask), + compositionMode(QPainter::CompositionMode_SourceOver), + customSrcStage(0), + currentShaderProg(0) +{ + sharedShaders = QOpenGLEngineSharedShaders::shadersForContext(context); +} + +QOpenGLEngineShaderManager::~QOpenGLEngineShaderManager() +{ + //### + removeCustomStage(); +} + +GLuint QOpenGLEngineShaderManager::getUniformLocation(Uniform id) +{ + if (!currentShaderProg) + return 0; + + QVector<uint> &uniformLocations = currentShaderProg->uniformLocations; + if (uniformLocations.isEmpty()) + uniformLocations.fill(GLuint(-1), NumUniforms); + + static const char *uniformNames[] = { + "imageTexture", + "patternColor", + "globalOpacity", + "depth", + "maskTexture", + "fragmentColor", + "linearData", + "angle", + "halfViewportSize", + "fmp", + "fmp2_m_radius2", + "inverse_2_fmp2_m_radius2", + "sqrfr", + "bradius", + "invertedTextureSize", + "brushTransform", + "brushTexture", + "matrix" + }; + + if (uniformLocations.at(id) == GLuint(-1)) + uniformLocations[id] = currentShaderProg->program->uniformLocation(uniformNames[id]); + + return uniformLocations.at(id); +} + + +void QOpenGLEngineShaderManager::optimiseForBrushTransform(QTransform::TransformationType transformType) +{ + Q_UNUSED(transformType); // Currently ignored +} + +void QOpenGLEngineShaderManager::setDirty() +{ + shaderProgNeedsChanging = true; +} + +void QOpenGLEngineShaderManager::setSrcPixelType(Qt::BrushStyle style) +{ + Q_ASSERT(style != Qt::NoBrush); + if (srcPixelType == PixelSrcType(style)) + return; + + srcPixelType = style; + shaderProgNeedsChanging = true; //### +} + +void QOpenGLEngineShaderManager::setSrcPixelType(PixelSrcType type) +{ + if (srcPixelType == type) + return; + + srcPixelType = type; + shaderProgNeedsChanging = true; //### +} + +void QOpenGLEngineShaderManager::setOpacityMode(OpacityMode mode) +{ + if (opacityMode == mode) + return; + + opacityMode = mode; + shaderProgNeedsChanging = true; //### +} + +void QOpenGLEngineShaderManager::setMaskType(MaskType type) +{ + if (maskType == type) + return; + + maskType = type; + shaderProgNeedsChanging = true; //### +} + +void QOpenGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode) +{ + if (compositionMode == mode) + return; + + compositionMode = mode; + shaderProgNeedsChanging = true; //### +} + +void QOpenGLEngineShaderManager::setCustomStage(QOpenGLCustomShaderStage* stage) +{ + if (customSrcStage) + removeCustomStage(); + customSrcStage = stage; + shaderProgNeedsChanging = true; +} + +void QOpenGLEngineShaderManager::removeCustomStage() +{ + if (customSrcStage) + customSrcStage->setInactive(); + customSrcStage = 0; + shaderProgNeedsChanging = true; +} + +QOpenGLShaderProgram* QOpenGLEngineShaderManager::currentProgram() +{ + if (currentShaderProg) + return currentShaderProg->program; + else + return sharedShaders->simpleProgram(); +} + +void QOpenGLEngineShaderManager::useSimpleProgram() +{ + sharedShaders->simpleProgram()->bind(); + QOpenGLContextPrivate* ctx_d = ctx->d_func(); + Q_UNUSED(ctx_d); + + QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine); + + active_engine->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false); + + shaderProgNeedsChanging = true; +} + +void QOpenGLEngineShaderManager::useBlitProgram() +{ + sharedShaders->blitProgram()->bind(); + QOpenGLContextPrivate* ctx_d = ctx->d_func(); + QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, true); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false); + shaderProgNeedsChanging = true; +} + +QOpenGLShaderProgram* QOpenGLEngineShaderManager::simpleProgram() +{ + return sharedShaders->simpleProgram(); +} + +QOpenGLShaderProgram* QOpenGLEngineShaderManager::blitProgram() +{ + return sharedShaders->blitProgram(); +} + + + +// Select & use the correct shader program using the current state. +// Returns true if program needed changing. +bool QOpenGLEngineShaderManager::useCorrectShaderProg() +{ + if (!shaderProgNeedsChanging) + return false; + + bool useCustomSrc = customSrcStage != 0; + if (useCustomSrc && srcPixelType != QOpenGLEngineShaderManager::ImageSrc && srcPixelType != Qt::TexturePattern) { + useCustomSrc = false; + qWarning("QOpenGLEngineShaderManager - Ignoring custom shader stage for non image src"); + } + + QOpenGLEngineShaderProg requiredProgram; + + bool texCoords = false; + + // Choose vertex shader shader position function (which typically also sets + // varyings) and the source pixel (srcPixel) fragment shader function: + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::InvalidSnippetName; + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::InvalidSnippetName; + bool isAffine = brushTransform.isAffine(); + if ( (srcPixelType >= Qt::Dense1Pattern) && (srcPixelType <= Qt::DiagCrossPattern) ) { + if (isAffine) + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader; + else + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionWithPatternBrushVertexShader; + + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::PatternBrushSrcFragmentShader; + } + else switch (srcPixelType) { + default: + case Qt::NoBrush: + qFatal("QOpenGLEngineShaderManager::useCorrectShaderProg() - Qt::NoBrush style is set"); + break; + case QOpenGLEngineShaderManager::ImageSrc: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ImageSrcFragmentShader; + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader; + texCoords = true; + break; + case QOpenGLEngineShaderManager::NonPremultipliedImageSrc: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader; + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader; + texCoords = true; + break; + case QOpenGLEngineShaderManager::PatternSrc: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ImageSrcWithPatternFragmentShader; + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader; + texCoords = true; + break; + case QOpenGLEngineShaderManager::TextureSrcWithPattern: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader + : QOpenGLEngineSharedShaders::PositionWithTextureBrushVertexShader; + break; + case Qt::SolidPattern: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::SolidBrushSrcFragmentShader; + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader; + break; + case Qt::LinearGradientPattern: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader + : QOpenGLEngineSharedShaders::PositionWithLinearGradientBrushVertexShader; + break; + case Qt::ConicalGradientPattern: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader + : QOpenGLEngineSharedShaders::PositionWithConicalGradientBrushVertexShader; + break; + case Qt::RadialGradientPattern: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader + : QOpenGLEngineSharedShaders::PositionWithRadialGradientBrushVertexShader; + break; + case Qt::TexturePattern: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::TextureBrushSrcFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader + : QOpenGLEngineSharedShaders::PositionWithTextureBrushVertexShader; + break; + }; + + if (useCustomSrc) { + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::CustomImageSrcFragmentShader; + requiredProgram.customStageSource = customSrcStage->source(); + } + + const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus; + const bool hasMask = maskType != QOpenGLEngineShaderManager::NoMask; + + // Choose fragment shader main function: + if (opacityMode == AttributeOpacity) { + Q_ASSERT(!hasCompose && !hasMask); + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_ImageArrays; + } else { + bool useGlobalOpacity = (opacityMode == UniformOpacity); + if (hasCompose && hasMask && useGlobalOpacity) + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_CMO; + if (hasCompose && hasMask && !useGlobalOpacity) + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_CM; + if (!hasCompose && hasMask && useGlobalOpacity) + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_MO; + if (!hasCompose && hasMask && !useGlobalOpacity) + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_M; + if (hasCompose && !hasMask && useGlobalOpacity) + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_CO; + if (hasCompose && !hasMask && !useGlobalOpacity) + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_C; + if (!hasCompose && !hasMask && useGlobalOpacity) + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_O; + if (!hasCompose && !hasMask && !useGlobalOpacity) + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader; + } + + if (hasMask) { + if (maskType == PixelMask) { + requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::MaskFragmentShader; + texCoords = true; + } else if (maskType == SubPixelMaskPass1) { + requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskFragmentShaderPass1; + texCoords = true; + } else if (maskType == SubPixelMaskPass2) { + requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskFragmentShaderPass2; + texCoords = true; + } else if (maskType == SubPixelWithGammaMask) { + requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskWithGammaFragmentShader; + texCoords = true; + } else { + qCritical("QOpenGLEngineShaderManager::useCorrectShaderProg() - Unknown mask type"); + } + } else { + requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::NoMaskFragmentShader; + } + + if (hasCompose) { + switch (compositionMode) { + case QPainter::CompositionMode_Multiply: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::MultiplyCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Screen: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ScreenCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Overlay: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::OverlayCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Darken: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::DarkenCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Lighten: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::LightenCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_ColorDodge: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_ColorBurn: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_HardLight: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::HardLightCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_SoftLight: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::SoftLightCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Difference: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::DifferenceCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Exclusion: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ExclusionCompositionModeFragmentShader; + break; + default: + qWarning("QOpenGLEngineShaderManager::useCorrectShaderProg() - Unsupported composition mode"); + } + } else { + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::NoCompositionModeFragmentShader; + } + + // Choose vertex shader main function + if (opacityMode == AttributeOpacity) { + Q_ASSERT(texCoords); + requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader; + } else if (texCoords) { + requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainWithTexCoordsVertexShader; + } else { + requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainVertexShader; + } + requiredProgram.useTextureCoords = texCoords; + requiredProgram.useOpacityAttribute = (opacityMode == AttributeOpacity); + if (complexGeometry && srcPixelType == Qt::SolidPattern) { + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::ComplexGeometryPositionOnlyVertexShader; + requiredProgram.usePmvMatrixAttribute = false; + } else { + requiredProgram.usePmvMatrixAttribute = true; + + // Force complexGeometry off, since we currently don't support that mode for + // non-solid brushes + complexGeometry = false; + } + + // At this point, requiredProgram is fully populated so try to find the program in the cache + currentShaderProg = sharedShaders->findProgramInCache(requiredProgram); + + if (currentShaderProg && useCustomSrc) { + customSrcStage->setUniforms(currentShaderProg->program); + } + + // Make sure all the vertex attribute arrays the program uses are enabled (and the ones it + // doesn't use are disabled) + QOpenGLContextPrivate* ctx_d = ctx->d_func(); + QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, currentShaderProg && currentShaderProg->useTextureCoords); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, currentShaderProg && currentShaderProg->useOpacityAttribute); + + shaderProgNeedsChanging = false; + return true; +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglengineshadermanager_p.h b/src/gui/opengl/qopenglengineshadermanager_p.h new file mode 100644 index 0000000000..1dcc4fe7a7 --- /dev/null +++ b/src/gui/opengl/qopenglengineshadermanager_p.h @@ -0,0 +1,514 @@ +/**************************************************************************** +** +** 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 QtGui 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$ +** +****************************************************************************/ + +// +// 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. +// + +/* + VERTEX SHADERS + ============== + + Vertex shaders are specified as multiple (partial) shaders. On desktop, + this works fine. On ES, QOpenGLShader & QOpenGLShaderProgram will make partial + shaders work by concatenating the source in each QOpenGLShader and compiling + it as a single shader. This is abstracted nicely by QOpenGLShaderProgram and + the GL2 engine doesn't need to worry about it. + + Generally, there's two vertex shader objects. The position shaders are + the ones which set gl_Position. There's also two "main" vertex shaders, + one which just calls the position shader and another which also passes + through some texture coordinates from a vertex attribute array to a + varying. These texture coordinates are used for mask position in text + rendering and for the source coordinates in drawImage/drawPixmap. There's + also a "Simple" vertex shader for rendering a solid colour (used to render + into the stencil buffer where the actual colour value is discarded). + + The position shaders for brushes look scary. This is because many of the + calculations which logically belong in the fragment shader have been moved + into the vertex shader to improve performance. This is why the position + calculation is in a separate shader. Not only does it calculate the + position, but it also calculates some data to be passed to the fragment + shader as a varying. It is optimal to move as much of the calculation as + possible into the vertex shader as this is executed less often. + + The varyings passed to the fragment shaders are interpolated (which is + cheap). Unfortunately, GL will apply perspective correction to the + interpolation calusing errors. To get around this, the vertex shader must + apply perspective correction itself and set the w-value of gl_Position to + zero. That way, GL will be tricked into thinking it doesn't need to apply a + perspective correction and use linear interpolation instead (which is what + we want). Of course, if the brush transform is affeine, no perspective + correction is needed and a simpler vertex shader can be used instead. + + So there are the following "main" vertex shaders: + qopenglslMainVertexShader + qopenglslMainWithTexCoordsVertexShader + + And the the following position vertex shaders: + qopenglslPositionOnlyVertexShader + qopenglslPositionWithTextureBrushVertexShader + qopenglslPositionWithPatternBrushVertexShader + qopenglslPositionWithLinearGradientBrushVertexShader + qopenglslPositionWithRadialGradientBrushVertexShader + qopenglslPositionWithConicalGradientBrushVertexShader + qopenglslAffinePositionWithTextureBrushVertexShader + qopenglslAffinePositionWithPatternBrushVertexShader + qopenglslAffinePositionWithLinearGradientBrushVertexShader + qopenglslAffinePositionWithRadialGradientBrushVertexShader + qopenglslAffinePositionWithConicalGradientBrushVertexShader + + Leading to 23 possible vertex shaders + + + FRAGMENT SHADERS + ================ + + Fragment shaders are also specified as multiple (partial) shaders. The + different fragment shaders represent the different stages in Qt's fragment + pipeline. There are 1-3 stages in this pipeline: First stage is to get the + fragment's colour value. The next stage is to get the fragment's mask value + (coverage value for anti-aliasing) and the final stage is to blend the + incoming fragment with the background (for composition modes not supported + by GL). + + Of these, the first stage will always be present. If Qt doesn't need to + apply anti-aliasing (because it's off or handled by multisampling) then + the coverage value doesn't need to be applied. (Note: There are two types + of mask, one for regular anti-aliasing and one for sub-pixel anti- + aliasing.) If the composition mode is one which GL supports natively then + the blending stage doesn't need to be applied. + + As eash stage can have multiple implementations, they are abstracted as + GLSL function calls with the following signatures: + + Brushes & image drawing are implementations of "qcolorp vec4 srcPixel()": + qopenglslImageSrcFragShader + qopenglslImageSrcWithPatternFragShader + qopenglslNonPremultipliedImageSrcFragShader + qopenglslSolidBrushSrcFragShader + qopenglslTextureBrushSrcFragShader + qopenglslTextureBrushWithPatternFragShader + qopenglslPatternBrushSrcFragShader + qopenglslLinearGradientBrushSrcFragShader + qopenglslRadialGradientBrushSrcFragShader + qopenglslConicalGradientBrushSrcFragShader + NOTE: It is assumed the colour returned by srcPixel() is pre-multiplied + + Masks are implementations of "qcolorp vec4 applyMask(qcolorp vec4 src)": + qopenglslMaskFragmentShader + qopenglslRgbMaskFragmentShaderPass1 + qopenglslRgbMaskFragmentShaderPass2 + qopenglslRgbMaskWithGammaFragmentShader + + Composition modes are "qcolorp vec4 compose(qcolorp vec4 src)": + qopenglslColorBurnCompositionModeFragmentShader + qopenglslColorDodgeCompositionModeFragmentShader + qopenglslDarkenCompositionModeFragmentShader + qopenglslDifferenceCompositionModeFragmentShader + qopenglslExclusionCompositionModeFragmentShader + qopenglslHardLightCompositionModeFragmentShader + qopenglslLightenCompositionModeFragmentShader + qopenglslMultiplyCompositionModeFragmentShader + qopenglslOverlayCompositionModeFragmentShader + qopenglslScreenCompositionModeFragmentShader + qopenglslSoftLightCompositionModeFragmentShader + + + Note: In the future, some GLSL compilers will support an extension allowing + a new 'color' precision specifier. To support this, qcolorp is used for + all color components so it can be defined to colorp or lowp depending upon + the implementation. + + So there are differnt frament shader main functions, depending on the + number & type of pipelines the fragment needs to go through. + + The choice of which main() fragment shader string to use depends on: + - Use of global opacity + - Brush style (some brushes apply opacity themselves) + - Use & type of mask (TODO: Need to support high quality anti-aliasing & text) + - Use of non-GL Composition mode + + Leading to the following fragment shader main functions: + gl_FragColor = compose(applyMask(srcPixel()*globalOpacity)); + gl_FragColor = compose(applyMask(srcPixel())); + gl_FragColor = applyMask(srcPixel()*globalOpacity); + gl_FragColor = applyMask(srcPixel()); + gl_FragColor = compose(srcPixel()*globalOpacity); + gl_FragColor = compose(srcPixel()); + gl_FragColor = srcPixel()*globalOpacity; + gl_FragColor = srcPixel(); + + Called: + qopenglslMainFragmentShader_CMO + qopenglslMainFragmentShader_CM + qopenglslMainFragmentShader_MO + qopenglslMainFragmentShader_M + qopenglslMainFragmentShader_CO + qopenglslMainFragmentShader_C + qopenglslMainFragmentShader_O + qopenglslMainFragmentShader + + Where: + M = Mask + C = Composition + O = Global Opacity + + + CUSTOM SHADER CODE + ================== + + The use of custom shader code is supported by the engine for drawImage and + drawPixmap calls. This is implemented via hooks in the fragment pipeline. + + The custom shader is passed to the engine as a partial fragment shader + (QOpenGLCustomShaderStage). The shader will implement a pre-defined method name + which Qt's fragment pipeline will call: + + lowp vec4 customShader(lowp sampler2d imageTexture, highp vec2 textureCoords) + + The provided src and srcCoords parameters can be used to sample from the + source image. + + Transformations, clipping, opacity, and composition modes set using QPainter + will be respected when using the custom shader hook. +*/ + +#ifndef QOPENGLENGINE_SHADER_MANAGER_H +#define QOPENGLENGINE_SHADER_MANAGER_H + +#include <QOpenGLShader> +#include <QOpenGLShaderProgram> +#include <QPainter> +#include <private/qopenglcontext_p.h> +#include <private/qopenglcustomshaderstage_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + + +/* +struct QOpenGLEngineCachedShaderProg +{ + QOpenGLEngineCachedShaderProg(QOpenGLEngineShaderManager::ShaderName vertexMain, + QOpenGLEngineShaderManager::ShaderName vertexPosition, + QOpenGLEngineShaderManager::ShaderName fragMain, + QOpenGLEngineShaderManager::ShaderName pixelSrc, + QOpenGLEngineShaderManager::ShaderName mask, + QOpenGLEngineShaderManager::ShaderName composition); + + int cacheKey; + QOpenGLShaderProgram* program; +} +*/ + +static const GLuint QT_VERTEX_COORDS_ATTR = 0; +static const GLuint QT_TEXTURE_COORDS_ATTR = 1; +static const GLuint QT_OPACITY_ATTR = 2; +static const GLuint QT_PMV_MATRIX_1_ATTR = 3; +static const GLuint QT_PMV_MATRIX_2_ATTR = 4; +static const GLuint QT_PMV_MATRIX_3_ATTR = 5; + +class QOpenGLEngineShaderProg; + +class Q_GUI_EXPORT QOpenGLEngineSharedShaders +{ + Q_GADGET +public: + + enum SnippetName { + MainVertexShader, + MainWithTexCoordsVertexShader, + MainWithTexCoordsAndOpacityVertexShader, + + // UntransformedPositionVertexShader must be first in the list: + UntransformedPositionVertexShader, + PositionOnlyVertexShader, + ComplexGeometryPositionOnlyVertexShader, + PositionWithPatternBrushVertexShader, + PositionWithLinearGradientBrushVertexShader, + PositionWithConicalGradientBrushVertexShader, + PositionWithRadialGradientBrushVertexShader, + PositionWithTextureBrushVertexShader, + AffinePositionWithPatternBrushVertexShader, + AffinePositionWithLinearGradientBrushVertexShader, + AffinePositionWithConicalGradientBrushVertexShader, + AffinePositionWithRadialGradientBrushVertexShader, + AffinePositionWithTextureBrushVertexShader, + + // MainFragmentShader_CMO must be first in the list: + MainFragmentShader_CMO, + MainFragmentShader_CM, + MainFragmentShader_MO, + MainFragmentShader_M, + MainFragmentShader_CO, + MainFragmentShader_C, + MainFragmentShader_O, + MainFragmentShader, + MainFragmentShader_ImageArrays, + + // ImageSrcFragmentShader must be first in the list:: + ImageSrcFragmentShader, + ImageSrcWithPatternFragmentShader, + NonPremultipliedImageSrcFragmentShader, + CustomImageSrcFragmentShader, + SolidBrushSrcFragmentShader, + TextureBrushSrcFragmentShader, + TextureBrushSrcWithPatternFragmentShader, + PatternBrushSrcFragmentShader, + LinearGradientBrushSrcFragmentShader, + RadialGradientBrushSrcFragmentShader, + ConicalGradientBrushSrcFragmentShader, + ShockingPinkSrcFragmentShader, + + // NoMaskFragmentShader must be first in the list: + NoMaskFragmentShader, + MaskFragmentShader, + RgbMaskFragmentShaderPass1, + RgbMaskFragmentShaderPass2, + RgbMaskWithGammaFragmentShader, + + // NoCompositionModeFragmentShader must be first in the list: + NoCompositionModeFragmentShader, + MultiplyCompositionModeFragmentShader, + ScreenCompositionModeFragmentShader, + OverlayCompositionModeFragmentShader, + DarkenCompositionModeFragmentShader, + LightenCompositionModeFragmentShader, + ColorDodgeCompositionModeFragmentShader, + ColorBurnCompositionModeFragmentShader, + HardLightCompositionModeFragmentShader, + SoftLightCompositionModeFragmentShader, + DifferenceCompositionModeFragmentShader, + ExclusionCompositionModeFragmentShader, + + TotalSnippetCount, InvalidSnippetName + }; +#if defined (QT_DEBUG) + Q_ENUMS(SnippetName) + static QByteArray snippetNameStr(SnippetName snippetName); +#endif + +/* + // These allow the ShaderName enum to be used as a cache key + const int mainVertexOffset = 0; + const int positionVertexOffset = (1<<2) - PositionOnlyVertexShader; + const int mainFragOffset = (1<<6) - MainFragmentShader_CMO; + const int srcPixelOffset = (1<<10) - ImageSrcFragmentShader; + const int maskOffset = (1<<14) - NoMaskShader; + const int compositionOffset = (1 << 16) - MultiplyCompositionModeFragmentShader; +*/ + + QOpenGLEngineSharedShaders(QOpenGLContext *context); + ~QOpenGLEngineSharedShaders(); + + QOpenGLShaderProgram *simpleProgram() { return simpleShaderProg; } + QOpenGLShaderProgram *blitProgram() { return blitShaderProg; } + // Compile the program if it's not already in the cache, return the item in the cache. + QOpenGLEngineShaderProg *findProgramInCache(const QOpenGLEngineShaderProg &prog); + // Compile the custom shader if it's not already in the cache, return the item in the cache. + + static QOpenGLEngineSharedShaders *shadersForContext(QOpenGLContext *context); + + // Ideally, this would be static and cleanup all programs in all contexts which + // contain the custom code. Currently it is just a hint and we rely on deleted + // custom shaders being cleaned up by being kicked out of the cache when it's + // full. + void cleanupCustomStage(QOpenGLCustomShaderStage* stage); + +private: + QOpenGLShaderProgram *blitShaderProg; + QOpenGLShaderProgram *simpleShaderProg; + QList<QOpenGLEngineShaderProg*> cachedPrograms; + QList<QOpenGLShader *> shaders; + + static const char* qShaderSnippets[TotalSnippetCount]; +}; + + +class QOpenGLEngineShaderProg +{ +public: + QOpenGLEngineShaderProg() : program(0) {} + + ~QOpenGLEngineShaderProg() { + if (program) + delete program; + } + + QOpenGLEngineSharedShaders::SnippetName mainVertexShader; + QOpenGLEngineSharedShaders::SnippetName positionVertexShader; + QOpenGLEngineSharedShaders::SnippetName mainFragShader; + QOpenGLEngineSharedShaders::SnippetName srcPixelFragShader; + QOpenGLEngineSharedShaders::SnippetName maskFragShader; + QOpenGLEngineSharedShaders::SnippetName compositionFragShader; + + QByteArray customStageSource; //TODO: Decent cache key for custom stages + QOpenGLShaderProgram* program; + + QVector<uint> uniformLocations; + + bool useTextureCoords; + bool useOpacityAttribute; + bool usePmvMatrixAttribute; + + bool operator==(const QOpenGLEngineShaderProg& other) { + // We don't care about the program + return ( mainVertexShader == other.mainVertexShader && + positionVertexShader == other.positionVertexShader && + mainFragShader == other.mainFragShader && + srcPixelFragShader == other.srcPixelFragShader && + maskFragShader == other.maskFragShader && + compositionFragShader == other.compositionFragShader && + customStageSource == other.customStageSource + ); + } +}; + +class Q_GUI_EXPORT QOpenGLEngineShaderManager : public QObject +{ + Q_OBJECT +public: + QOpenGLEngineShaderManager(QOpenGLContext* context); + ~QOpenGLEngineShaderManager(); + + enum MaskType {NoMask, PixelMask, SubPixelMaskPass1, SubPixelMaskPass2, SubPixelWithGammaMask}; + enum PixelSrcType { + ImageSrc = Qt::TexturePattern+1, + NonPremultipliedImageSrc = Qt::TexturePattern+2, + PatternSrc = Qt::TexturePattern+3, + TextureSrcWithPattern = Qt::TexturePattern+4 + }; + + enum Uniform { + ImageTexture, + PatternColor, + GlobalOpacity, + Depth, + MaskTexture, + FragmentColor, + LinearData, + Angle, + HalfViewportSize, + Fmp, + Fmp2MRadius2, + Inverse2Fmp2MRadius2, + SqrFr, + BRadius, + InvertedTextureSize, + BrushTransform, + BrushTexture, + Matrix, + NumUniforms + }; + + enum OpacityMode { + NoOpacity, + UniformOpacity, + AttributeOpacity + }; + + // There are optimizations we can do, depending on the brush transform: + // 1) May not have to apply perspective-correction + // 2) Can use lower precision for matrix + void optimiseForBrushTransform(QTransform::TransformationType transformType); + void setSrcPixelType(Qt::BrushStyle); + void setSrcPixelType(PixelSrcType); // For non-brush sources, like pixmaps & images + void setOpacityMode(OpacityMode); + void setMaskType(MaskType); + void setCompositionMode(QPainter::CompositionMode); + void setCustomStage(QOpenGLCustomShaderStage* stage); + void removeCustomStage(); + + GLuint getUniformLocation(Uniform id); + + void setDirty(); // someone has manually changed the current shader program + bool useCorrectShaderProg(); // returns true if the shader program needed to be changed + + void useSimpleProgram(); + void useBlitProgram(); + void setHasComplexGeometry(bool hasComplexGeometry) + { + complexGeometry = hasComplexGeometry; + shaderProgNeedsChanging = true; + } + bool hasComplexGeometry() const + { + return complexGeometry; + } + + QOpenGLShaderProgram* currentProgram(); // Returns pointer to the shader the manager has chosen + QOpenGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers + QOpenGLShaderProgram* blitProgram(); // Used to blit a texture into the framebuffer + + QOpenGLEngineSharedShaders* sharedShaders; + +private: + QOpenGLContext* ctx; + bool shaderProgNeedsChanging; + bool complexGeometry; + + // Current state variables which influence the choice of shader: + QTransform brushTransform; + int srcPixelType; + OpacityMode opacityMode; + MaskType maskType; + QPainter::CompositionMode compositionMode; + QOpenGLCustomShaderStage* customSrcStage; + + QOpenGLEngineShaderProg* currentShaderProg; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QOPENGLENGINE_SHADER_MANAGER_H diff --git a/src/gui/opengl/qopenglengineshadersource_p.h b/src/gui/opengl/qopenglengineshadersource_p.h new file mode 100644 index 0000000000..cb85212308 --- /dev/null +++ b/src/gui/opengl/qopenglengineshadersource_p.h @@ -0,0 +1,529 @@ +/**************************************************************************** +** +** 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 QtGui 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$ +** +****************************************************************************/ + +// +// 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. +// + + +#ifndef QOPENGL_ENGINE_SHADER_SOURCE_H +#define QOPENGL_ENGINE_SHADER_SOURCE_H + +#include "qopenglengineshadermanager_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + + +static const char* const qopenglslMainVertexShader = "\n\ + void setPosition(); \n\ + void main(void) \n\ + { \n\ + setPosition(); \n\ + }\n"; + +static const char* const qopenglslMainWithTexCoordsVertexShader = "\n\ + attribute highp vec2 textureCoordArray; \n\ + varying highp vec2 textureCoords; \n\ + void setPosition(); \n\ + void main(void) \n\ + { \n\ + setPosition(); \n\ + textureCoords = textureCoordArray; \n\ + }\n"; + +static const char* const qopenglslMainWithTexCoordsAndOpacityVertexShader = "\n\ + attribute highp vec2 textureCoordArray; \n\ + attribute lowp float opacityArray; \n\ + varying highp vec2 textureCoords; \n\ + varying lowp float opacity; \n\ + void setPosition(); \n\ + void main(void) \n\ + { \n\ + setPosition(); \n\ + textureCoords = textureCoordArray; \n\ + opacity = opacityArray; \n\ + }\n"; + +// NOTE: We let GL do the perspective correction so texture lookups in the fragment +// shader are also perspective corrected. +static const char* const qopenglslPositionOnlyVertexShader = "\n\ + attribute highp vec2 vertexCoordsArray; \n\ + attribute highp vec3 pmvMatrix1; \n\ + attribute highp vec3 pmvMatrix2; \n\ + attribute highp vec3 pmvMatrix3; \n\ + void setPosition(void) \n\ + { \n\ + highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position = vec4(transformedPos.xy, 0.0, transformedPos.z); \n\ + }\n"; + +static const char* const qopenglslComplexGeometryPositionOnlyVertexShader = "\n\ + uniform highp mat3 matrix; \n\ + attribute highp vec2 vertexCoordsArray; \n\ + void setPosition(void) \n\ + { \n\ + gl_Position = vec4(matrix * vec3(vertexCoordsArray, 1), 1);\n\ + } \n"; + +static const char* const qopenglslUntransformedPositionVertexShader = "\n\ + attribute highp vec4 vertexCoordsArray; \n\ + void setPosition(void) \n\ + { \n\ + gl_Position = vertexCoordsArray; \n\ + }\n"; + +// Pattern Brush - This assumes the texture size is 8x8 and thus, the inverted size is 0.125 +static const char* const qopenglslPositionWithPatternBrushVertexShader = "\n\ + attribute highp vec2 vertexCoordsArray; \n\ + attribute highp vec3 pmvMatrix1; \n\ + attribute highp vec3 pmvMatrix2; \n\ + attribute highp vec3 pmvMatrix3; \n\ + uniform mediump vec2 halfViewportSize; \n\ + uniform highp vec2 invertedTextureSize; \n\ + uniform highp mat3 brushTransform; \n\ + varying highp vec2 patternTexCoords; \n\ + void setPosition(void) \n\ + { \n\ + highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1.0); \n\ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + patternTexCoords.xy = (hTexCoords.xy * 0.125) * invertedHTexCoordsZ; \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithPatternBrushVertexShader + = qopenglslPositionWithPatternBrushVertexShader; + +static const char* const qopenglslPatternBrushSrcFragmentShader = "\n\ + uniform sampler2D brushTexture; \n\ + uniform lowp vec4 patternColor; \n\ + varying highp vec2 patternTexCoords;\n\ + lowp vec4 srcPixel() \n\ + { \n\ + return patternColor * (1.0 - texture2D(brushTexture, patternTexCoords).r); \n\ + }\n"; + + +// Linear Gradient Brush +static const char* const qopenglslPositionWithLinearGradientBrushVertexShader = "\n\ + attribute highp vec2 vertexCoordsArray; \n\ + attribute highp vec3 pmvMatrix1; \n\ + attribute highp vec3 pmvMatrix2; \n\ + attribute highp vec3 pmvMatrix3; \n\ + uniform mediump vec2 halfViewportSize; \n\ + uniform highp vec3 linearData; \n\ + uniform highp mat3 brushTransform; \n\ + varying mediump float index; \n\ + void setPosition() \n\ + { \n\ + highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + index = (dot(linearData.xy, hTexCoords.xy) * linearData.z) * invertedHTexCoordsZ; \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithLinearGradientBrushVertexShader + = qopenglslPositionWithLinearGradientBrushVertexShader; + +static const char* const qopenglslLinearGradientBrushSrcFragmentShader = "\n\ + uniform sampler2D brushTexture; \n\ + varying mediump float index; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + mediump vec2 val = vec2(index, 0.5); \n\ + return texture2D(brushTexture, val); \n\ + }\n"; + + +// Conical Gradient Brush +static const char* const qopenglslPositionWithConicalGradientBrushVertexShader = "\n\ + attribute highp vec2 vertexCoordsArray; \n\ + attribute highp vec3 pmvMatrix1; \n\ + attribute highp vec3 pmvMatrix2; \n\ + attribute highp vec3 pmvMatrix3; \n\ + uniform mediump vec2 halfViewportSize; \n\ + uniform highp mat3 brushTransform; \n\ + varying highp vec2 A; \n\ + void setPosition(void) \n\ + { \n\ + highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + A = hTexCoords.xy * invertedHTexCoordsZ; \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithConicalGradientBrushVertexShader + = qopenglslPositionWithConicalGradientBrushVertexShader; + +static const char* const qopenglslConicalGradientBrushSrcFragmentShader = "\n\ + #define INVERSE_2PI 0.1591549430918953358 \n\ + uniform sampler2D brushTexture; \n\ + uniform mediump float angle; \n\ + varying highp vec2 A; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + highp float t; \n\ + if (abs(A.y) == abs(A.x)) \n\ + t = (atan(-A.y + 0.002, A.x) + angle) * INVERSE_2PI; \n\ + else \n\ + t = (atan(-A.y, A.x) + angle) * INVERSE_2PI; \n\ + return texture2D(brushTexture, vec2(t - floor(t), 0.5)); \n\ + }\n"; + + +// Radial Gradient Brush +static const char* const qopenglslPositionWithRadialGradientBrushVertexShader = "\n\ + attribute highp vec2 vertexCoordsArray;\n\ + attribute highp vec3 pmvMatrix1; \n\ + attribute highp vec3 pmvMatrix2; \n\ + attribute highp vec3 pmvMatrix3; \n\ + uniform mediump vec2 halfViewportSize; \n\ + uniform highp mat3 brushTransform; \n\ + uniform highp vec2 fmp; \n\ + uniform highp vec3 bradius; \n\ + varying highp float b; \n\ + varying highp vec2 A; \n\ + void setPosition(void) \n\ + {\n\ + highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + A = hTexCoords.xy * invertedHTexCoordsZ; \n\ + b = bradius.x + 2.0 * dot(A, fmp); \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithRadialGradientBrushVertexShader + = qopenglslPositionWithRadialGradientBrushVertexShader; + +static const char* const qopenglslRadialGradientBrushSrcFragmentShader = "\n\ + uniform sampler2D brushTexture; \n\ + uniform highp float fmp2_m_radius2; \n\ + uniform highp float inverse_2_fmp2_m_radius2; \n\ + uniform highp float sqrfr; \n\ + varying highp float b; \n\ + varying highp vec2 A; \n\ + uniform highp vec3 bradius; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + highp float c = sqrfr-dot(A, A); \n\ + highp float det = b*b - 4.0*fmp2_m_radius2*c; \n\ + lowp vec4 result = vec4(0.0); \n\ + if (det >= 0.0) { \n\ + highp float detSqrt = sqrt(det); \n\ + highp float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2); \n\ + if (bradius.y + w * bradius.z >= 0.0) \n\ + result = texture2D(brushTexture, vec2(w, 0.5)); \n\ + } \n\ + return result; \n\ + }\n"; + + +// Texture Brush +static const char* const qopenglslPositionWithTextureBrushVertexShader = "\n\ + attribute highp vec2 vertexCoordsArray; \n\ + attribute highp vec3 pmvMatrix1; \n\ + attribute highp vec3 pmvMatrix2; \n\ + attribute highp vec3 pmvMatrix3; \n\ + uniform mediump vec2 halfViewportSize; \n\ + uniform highp vec2 invertedTextureSize; \n\ + uniform highp mat3 brushTransform; \n\ + varying highp vec2 brushTextureCoords; \n\ + void setPosition(void) \n\ + { \n\ + highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + brushTextureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithTextureBrushVertexShader + = qopenglslPositionWithTextureBrushVertexShader; + +#if defined(QT_OPENGL_ES_2) +// OpenGL ES does not support GL_REPEAT wrap modes for NPOT textures. So instead, +// we emulate GL_REPEAT by only taking the fractional part of the texture coords. +// TODO: Special case POT textures which don't need this emulation +static const char* const qopenglslTextureBrushSrcFragmentShader = "\n\ + varying highp vec2 brushTextureCoords; \n\ + uniform sampler2D brushTexture; \n\ + lowp vec4 srcPixel() { \n\ + return texture2D(brushTexture, fract(brushTextureCoords)); \n\ + }\n"; +#else +static const char* const qopenglslTextureBrushSrcFragmentShader = "\n\ + varying highp vec2 brushTextureCoords; \n\ + uniform sampler2D brushTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + return texture2D(brushTexture, brushTextureCoords); \n\ + }\n"; +#endif + +static const char* const qopenglslTextureBrushSrcWithPatternFragmentShader = "\n\ + varying highp vec2 brushTextureCoords; \n\ + uniform lowp vec4 patternColor; \n\ + uniform sampler2D brushTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + return patternColor * (1.0 - texture2D(brushTexture, brushTextureCoords).r); \n\ + }\n"; + +// Solid Fill Brush +static const char* const qopenglslSolidBrushSrcFragmentShader = "\n\ + uniform lowp vec4 fragmentColor; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + return fragmentColor; \n\ + }\n"; + +static const char* const qopenglslImageSrcFragmentShader = "\n\ + varying highp vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n" + "return texture2D(imageTexture, textureCoords); \n" + "}\n"; + +static const char* const qopenglslCustomSrcFragmentShader = "\n\ + varying highp vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + return customShader(imageTexture, textureCoords); \n\ + }\n"; + +static const char* const qopenglslImageSrcWithPatternFragmentShader = "\n\ + varying highp vec2 textureCoords; \n\ + uniform lowp vec4 patternColor; \n\ + uniform sampler2D imageTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + return patternColor * (1.0 - texture2D(imageTexture, textureCoords).r); \n\ + }\n"; + +static const char* const qopenglslNonPremultipliedImageSrcFragmentShader = "\n\ + varying highp vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + lowp vec4 sample = texture2D(imageTexture, textureCoords); \n\ + sample.rgb = sample.rgb * sample.a; \n\ + return sample; \n\ + }\n"; + +static const char* const qopenglslShockingPinkSrcFragmentShader = "\n\ + lowp vec4 srcPixel() \n\ + { \n\ + return vec4(0.98, 0.06, 0.75, 1.0); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_ImageArrays = "\n\ + varying lowp float opacity; \n\ + lowp vec4 srcPixel(); \n\ + void main() \n\ + { \n\ + gl_FragColor = srcPixel() * opacity; \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_CMO = "\n\ + uniform lowp float globalOpacity; \n\ + lowp vec4 srcPixel(); \n\ + lowp vec4 applyMask(lowp vec4); \n\ + lowp vec4 compose(lowp vec4); \n\ + void main() \n\ + { \n\ + gl_FragColor = applyMask(compose(srcPixel()*globalOpacity))); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_CM = "\n\ + lowp vec4 srcPixel(); \n\ + lowp vec4 applyMask(lowp vec4); \n\ + lowp vec4 compose(lowp vec4); \n\ + void main() \n\ + { \n\ + gl_FragColor = applyMask(compose(srcPixel())); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_MO = "\n\ + uniform lowp float globalOpacity; \n\ + lowp vec4 srcPixel(); \n\ + lowp vec4 applyMask(lowp vec4); \n\ + void main() \n\ + { \n\ + gl_FragColor = applyMask(srcPixel()*globalOpacity); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_M = "\n\ + lowp vec4 srcPixel(); \n\ + lowp vec4 applyMask(lowp vec4); \n\ + void main() \n\ + { \n\ + gl_FragColor = applyMask(srcPixel()); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_CO = "\n\ + uniform lowp float globalOpacity; \n\ + lowp vec4 srcPixel(); \n\ + lowp vec4 compose(lowp vec4); \n\ + void main() \n\ + { \n\ + gl_FragColor = compose(srcPixel()*globalOpacity); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_C = "\n\ + lowp vec4 srcPixel(); \n\ + lowp vec4 compose(lowp vec4); \n\ + void main() \n\ + { \n\ + gl_FragColor = compose(srcPixel()); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_O = "\n\ + uniform lowp float globalOpacity; \n\ + lowp vec4 srcPixel(); \n\ + void main() \n\ + { \n\ + gl_FragColor = srcPixel()*globalOpacity; \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader = "\n\ + lowp vec4 srcPixel(); \n\ + void main() \n\ + { \n\ + gl_FragColor = srcPixel(); \n\ + }\n"; + +static const char* const qopenglslMaskFragmentShader = "\n\ + varying highp vec2 textureCoords;\n\ + uniform sampler2D maskTexture;\n\ + lowp vec4 applyMask(lowp vec4 src) \n\ + {\n\ + lowp vec4 mask = texture2D(maskTexture, textureCoords); \n\ + return src * mask.a; \n\ + }\n"; + +// For source over with subpixel antialiasing, the final color is calculated per component as follows +// (.a is alpha component, .c is red, green or blue component): +// alpha = src.a * mask.c * opacity +// dest.c = dest.c * (1 - alpha) + src.c * alpha +// +// In the first pass, calculate: dest.c = dest.c * (1 - alpha) with blend funcs: zero, 1 - source color +// In the second pass, calculate: dest.c = dest.c + src.c * alpha with blend funcs: one, one +// +// If source is a solid color (src is constant), only the first pass is needed, with blend funcs: constant, 1 - source color + +// For source composition with subpixel antialiasing, the final color is calculated per component as follows: +// alpha = src.a * mask.c * opacity +// dest.c = dest.c * (1 - mask.c) + src.c * alpha +// + +static const char* const qopenglslRgbMaskFragmentShaderPass1 = "\n\ + varying highp vec2 textureCoords;\n\ + uniform sampler2D maskTexture;\n\ + lowp vec4 applyMask(lowp vec4 src) \n\ + { \n\ + lowp vec4 mask = texture2D(maskTexture, textureCoords); \n\ + return src.a * mask; \n\ + }\n"; + +static const char* const qopenglslRgbMaskFragmentShaderPass2 = "\n\ + varying highp vec2 textureCoords;\n\ + uniform sampler2D maskTexture;\n\ + lowp vec4 applyMask(lowp vec4 src) \n\ + { \n\ + lowp vec4 mask = texture2D(maskTexture, textureCoords); \n\ + return src * mask; \n\ + }\n"; + +/* + Left to implement: + RgbMaskFragmentShader, + RgbMaskWithGammaFragmentShader, + + MultiplyCompositionModeFragmentShader, + ScreenCompositionModeFragmentShader, + OverlayCompositionModeFragmentShader, + DarkenCompositionModeFragmentShader, + LightenCompositionModeFragmentShader, + ColorDodgeCompositionModeFragmentShader, + ColorBurnCompositionModeFragmentShader, + HardLightCompositionModeFragmentShader, + SoftLightCompositionModeFragmentShader, + DifferenceCompositionModeFragmentShader, + ExclusionCompositionModeFragmentShader, +*/ + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // GLGC_SHADER_SOURCE_H diff --git a/src/gui/opengl/qopenglextensions_p.h b/src/gui/opengl/qopenglextensions_p.h new file mode 100644 index 0000000000..65d92e3a65 --- /dev/null +++ b/src/gui/opengl/qopenglextensions_p.h @@ -0,0 +1,677 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGL_EXTENSIONS_P_H +#define QOPENGL_EXTENSIONS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Qt OpenGL classes. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qopenglfunctions.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if 0 +#ifndef GL_ARB_vertex_buffer_object +typedef ptrdiff_t GLintptrARB; +typedef ptrdiff_t GLsizeiptrARB; +#endif +#endif + +#ifndef GL_VERSION_2_0 +typedef char GLchar; +#endif + +class QOpenGLExtensionsPrivate; + +class Q_GUI_EXPORT QOpenGLExtensions : public QOpenGLFunctions +{ + Q_DECLARE_PRIVATE(QOpenGLExtensions) +public: + QOpenGLExtensions(); + QOpenGLExtensions(QOpenGLContext *context); + ~QOpenGLExtensions() {} + + enum OpenGLExtension { + TextureRectangle = 0x00000001, + GenerateMipmap = 0x00000002, + TextureCompression = 0x00000004, + MirroredRepeat = 0x00000008, + FramebufferMultisample = 0x00000010, + StencilTwoSide = 0x00000020, + StencilWrap = 0x00000040, + PackedDepthStencil = 0x00000080, + NVFloatBuffer = 0x00000100, + PixelBufferObject = 0x00000200, + FramebufferBlit = 0x00000400, + BGRATextureFormat = 0x00000800, + DDSTextureCompression = 0x00001000, + ETC1TextureCompression = 0x00002000, + PVRTCTextureCompression = 0x00004000, + ElementIndexUint = 0x00008000, + Depth24 = 0x00010000, + SRGBFrameBuffer = 0x00020000, + MapBuffer = 0x00040000, + GeometryShaders = 0x00080000 + }; + Q_DECLARE_FLAGS(OpenGLExtensions, OpenGLExtension) + + OpenGLExtensions openGLExtensions(); + bool hasOpenGLExtension(QOpenGLExtensions::OpenGLExtension extension) const; + + void initializeGLExtensions(); + + GLvoid *glMapBuffer(GLenum target, GLenum access); + GLboolean glUnmapBuffer(GLenum target); + + void glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); + + void glRenderbufferStorageMultisample(GLenum target, GLsizei samples, + GLenum internalFormat, + GLsizei width, GLsizei height); + + void glGetBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, GLvoid *data); + +private: + static bool isInitialized(const QOpenGLFunctionsPrivate *d) { return d != 0; } +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLExtensions::OpenGLExtensions) + +class QOpenGLExtensionsPrivate : public QOpenGLFunctionsPrivate +{ +public: + explicit QOpenGLExtensionsPrivate(QOpenGLContext *ctx); + + GLvoid* (QOPENGLF_APIENTRYP MapBuffer)(GLenum target, GLenum access); + GLboolean (QOPENGLF_APIENTRYP UnmapBuffer)(GLenum target); + void (QOPENGLF_APIENTRYP BlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); + void (QOPENGLF_APIENTRYP RenderbufferStorageMultisample)(GLenum target, GLsizei samples, + GLenum internalFormat, + GLsizei width, GLsizei height); + void (QOPENGLF_APIENTRYP GetBufferSubData)(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, GLvoid *data); +}; + +inline GLvoid *QOpenGLExtensions::glMapBuffer(GLenum target, GLenum access) +{ + Q_D(QOpenGLExtensions); + Q_ASSERT(QOpenGLExtensions::isInitialized(d)); + GLvoid *result = d->MapBuffer(target, access); + Q_OPENGL_FUNCTIONS_DEBUG + return result; +} + +inline GLboolean QOpenGLExtensions::glUnmapBuffer(GLenum target) +{ + Q_D(QOpenGLExtensions); + Q_ASSERT(QOpenGLExtensions::isInitialized(d)); + GLboolean result = d->UnmapBuffer(target); + Q_OPENGL_FUNCTIONS_DEBUG + return result; +} + +inline void QOpenGLExtensions::glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) +{ + Q_D(QOpenGLExtensions); + Q_ASSERT(QOpenGLExtensions::isInitialized(d)); + d->BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLExtensions::glRenderbufferStorageMultisample(GLenum target, GLsizei samples, + GLenum internalFormat, + GLsizei width, GLsizei height) +{ + Q_D(QOpenGLExtensions); + Q_ASSERT(QOpenGLExtensions::isInitialized(d)); + d->RenderbufferStorageMultisample(target, samples, internalFormat, width, height); + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLExtensions::glGetBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, GLvoid *data) +{ + Q_D(QOpenGLExtensions); + Q_ASSERT(QOpenGLExtensions::isInitialized(d)); + d->GetBufferSubData(target, offset, size, data); + Q_OPENGL_FUNCTIONS_DEBUG +} + +#ifndef GL_FRAMEBUFFER_SRGB_CAPABLE +#define GL_FRAMEBUFFER_SRGB_CAPABLE 0x8DBA +#endif +#ifndef GL_FRAMEBUFFER_SRGB +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#endif +#ifndef GL_ARRAY_BUFFER +#define GL_ARRAY_BUFFER 0x8892 +#endif +#ifndef GL_STATIC_DRAW +#define GL_STATIC_DRAW 0x88E4 +#endif +#ifndef GL_TEXTURE_RECTANGLE +#define GL_TEXTURE_RECTANGLE 0x84F5 +#endif +#ifndef GL_TEXTURE_BINDING_RECTANGLE +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#endif +#ifndef GL_PROXY_TEXTURE_RECTANGLE +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#endif +#ifndef GL_MAX_RECTANGLE_TEXTURE_SIZE +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#endif +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif +#ifndef GL_RGB16 +#define GL_RGB16 0x8054 +#endif +#ifndef GL_UNSIGNED_SHORT_5_6_5 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#endif +#ifndef GL_UNSIGNED_INT_8_8_8_8_REV +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#endif +#ifndef GL_MULTISAMPLE +#define GL_MULTISAMPLE 0x809D +#endif +#ifndef GL_CLAMP_TO_EDGE +#define GL_CLAMP_TO_EDGE 0x812F +#endif +#ifndef GL_MIRRORED_REPEAT +#define GL_MIRRORED_REPEAT 0x8370 +#endif +#ifndef GL_GENERATE_MIPMAP +#define GL_GENERATE_MIPMAP 0x8191 +#endif +#ifndef GL_GENERATE_MIPMAP_HINT +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#endif +#ifndef GL_FRAGMENT_PROGRAM +#define GL_FRAGMENT_PROGRAM 0x8804 +#endif +#ifndef GL_PROGRAM_FORMAT_ASCII +#define GL_PROGRAM_FORMAT_ASCII 0x8875 +#endif +#ifndef GL_PIXEL_UNPACK_BUFFER +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#endif +#ifndef GL_WRITE_ONLY +#define GL_WRITE_ONLY 0x88B9 +#endif +#ifndef GL_STREAM_DRAW +#define GL_STREAM_DRAW 0x88E0 +#endif +#ifndef GL_STENCIL_TEST_TWO_SIDE +#define GL_STENCIL_TEST_TWO_SIDE 0x8910 +#endif +#ifndef GL_INCR_WRAP +#define GL_INCR_WRAP 0x8507 +#endif +#ifndef GL_DECR_WRAP +#define GL_DECR_WRAP 0x8508 +#endif +#ifndef GL_TEXTURE0 +#define GL_TEXTURE0 0x84C0 +#endif +#ifndef GL_TEXTURE1 +#define GL_TEXTURE1 0x84C1 +#endif +#ifndef GL_DEPTH_COMPONENT16 +#define GL_DEPTH_COMPONENT16 0x81A5 +#endif +#ifndef GL_DEPTH_COMPONENT24 +#define GL_DEPTH_COMPONENT24 0x81A6 +#endif +#ifndef GL_INVALID_FRAMEBUFFER_OPERATION +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#endif +#ifndef GL_MAX_RENDERBUFFER_SIZE +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#endif +#ifndef GL_FRAMEBUFFER_BINDING +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#endif +#ifndef GL_RENDERBUFFER_BINDING +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET 0x8CD4 +#endif +#ifndef GL_FRAMEBUFFER_COMPLETE +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT +#define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT 0x8CD8 +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS 0x8CDA +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#endif +#ifndef GL_FRAMEBUFFER_UNSUPPORTED +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#endif +#ifndef GL_MAX_COLOR_ATTACHMENTS +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#endif +#ifndef GL_COLOR_ATTACHMENT0 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#endif +#ifndef GL_COLOR_ATTACHMENT1 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#endif +#ifndef GL_COLOR_ATTACHMENT2 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#endif +#ifndef GL_COLOR_ATTACHMENT3 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#endif +#ifndef GL_COLOR_ATTACHMENT4 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#endif +#ifndef GL_COLOR_ATTACHMENT5 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#endif +#ifndef GL_COLOR_ATTACHMENT6 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#endif +#ifndef GL_COLOR_ATTACHMENT7 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#endif +#ifndef GL_COLOR_ATTACHMENT8 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#endif +#ifndef GL_COLOR_ATTACHMENT9 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#endif +#ifndef GL_COLOR_ATTACHMENT10 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#endif +#ifndef GL_COLOR_ATTACHMENT11 +#define GL_COLOR_ATTACHMENT11 0x8CEB +#endif +#ifndef GL_COLOR_ATTACHMENT12 +#define GL_COLOR_ATTACHMENT12 0x8CEC +#endif +#ifndef GL_COLOR_ATTACHMENT13 +#define GL_COLOR_ATTACHMENT13 0x8CED +#endif +#ifndef GL_COLOR_ATTACHMENT14 +#define GL_COLOR_ATTACHMENT14 0x8CEE +#endif +#ifndef GL_COLOR_ATTACHMENT15 +#define GL_COLOR_ATTACHMENT15 0x8CEF +#endif +#ifndef GL_DEPTH_ATTACHMENT +#define GL_DEPTH_ATTACHMENT 0x8D00 +#endif +#ifndef GL_STENCIL_ATTACHMENT +#define GL_STENCIL_ATTACHMENT 0x8D20 +#endif +#ifndef GL_FRAMEBUFFER +#define GL_FRAMEBUFFER 0x8D40 +#endif +#ifndef GL_RENDERBUFFER +#define GL_RENDERBUFFER 0x8D41 +#endif +#ifndef GL_RENDERBUFFER_WIDTH +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#endif +#ifndef GL_RENDERBUFFER_HEIGHT +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#endif +#ifndef GL_RENDERBUFFER_INTERNAL_FORMAT +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#endif +#ifndef GL_STENCIL_INDEX +#define GL_STENCIL_INDEX 0x8D45 +#endif +#ifndef GL_STENCIL_INDEX1 +#define GL_STENCIL_INDEX1 0x8D46 +#endif +#ifndef GL_STENCIL_INDEX4 +#define GL_STENCIL_INDEX4 0x8D47 +#endif +#ifndef GL_STENCIL_INDEX8 +#define GL_STENCIL_INDEX8 0x8D48 +#endif +#ifndef GL_STENCIL_INDEX16 +#define GL_STENCIL_INDEX16 0x8D49 +#endif +#ifndef GL_RENDERBUFFER_RED_SIZE +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#endif +#ifndef GL_RENDERBUFFER_GREEN_SIZE +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#endif +#ifndef GL_RENDERBUFFER_BLUE_SIZE +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#endif +#ifndef GL_RENDERBUFFER_ALPHA_SIZE +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#endif +#ifndef GL_RENDERBUFFER_DEPTH_SIZE +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#endif +#ifndef GL_RENDERBUFFER_STENCIL_SIZE +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#endif +#ifndef GL_READ_FRAMEBUFFER +#define GL_READ_FRAMEBUFFER 0x8CA8 +#endif +#ifndef GL_RENDERBUFFER_SAMPLES +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#endif +#ifndef GL_MAX_SAMPLES +#define GL_MAX_SAMPLES 0x8D57 +#endif +#ifndef GL_DRAW_FRAMEBUFFER +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#endif +#ifndef GL_DEPTH_STENCIL +#define GL_DEPTH_STENCIL 0x84F9 +#endif +#ifndef GL_UNSIGNED_INT_24_8 +#define GL_UNSIGNED_INT_24_8 0x84FA +#endif +#ifndef GL_DEPTH24_STENCIL8 +#define GL_DEPTH24_STENCIL8 0x88F0 +#endif +#ifndef GL_TEXTURE_STENCIL_SIZE +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#endif +#ifndef GL_CLAMP_TO_EDGE +#define GL_CLAMP_TO_EDGE 0x812F +#endif +#ifndef GL_PACK_SKIP_IMAGES +#define GL_PACK_SKIP_IMAGES 0x806B +#endif +#ifndef GL_PACK_IMAGE_HEIGHT +#define GL_PACK_IMAGE_HEIGHT 0x806C +#endif +#ifndef GL_UNPACK_SKIP_IMAGES +#define GL_UNPACK_SKIP_IMAGES 0x806D +#endif +#ifndef GL_UNPACK_IMAGE_HEIGHT +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#endif +#ifndef GL_CONSTANT_COLOR +#define GL_CONSTANT_COLOR 0x8001 +#endif +#ifndef GL_ONE_MINUS_CONSTANT_COLOR +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#endif +#ifndef GL_CONSTANT_ALPHA +#define GL_CONSTANT_ALPHA 0x8003 +#endif +#ifndef GL_ONE_MINUS_CONSTANT_ALPHA +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#endif +#ifndef GL_INCR_WRAP +#define GL_INCR_WRAP 0x8507 +#endif +#ifndef GL_DECR_WRAP +#define GL_DECR_WRAP 0x8508 +#endif +#ifndef GL_ARRAY_BUFFER +#define GL_ARRAY_BUFFER 0x8892 +#endif +#ifndef GL_ELEMENT_ARRAY_BUFFER +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#endif +#ifndef GL_STREAM_DRAW +#define GL_STREAM_DRAW 0x88E0 +#endif +#ifndef GL_STREAM_READ +#define GL_STREAM_READ 0x88E1 +#endif +#ifndef GL_STREAM_COPY +#define GL_STREAM_COPY 0x88E2 +#endif +#ifndef GL_STATIC_DRAW +#define GL_STATIC_DRAW 0x88E4 +#endif +#ifndef GL_STATIC_READ +#define GL_STATIC_READ 0x88E5 +#endif +#ifndef GL_STATIC_COPY +#define GL_STATIC_COPY 0x88E6 +#endif +#ifndef GL_DYNAMIC_DRAW +#define GL_DYNAMIC_DRAW 0x88E8 +#endif +#ifndef GL_DYNAMIC_READ +#define GL_DYNAMIC_READ 0x88E9 +#endif +#ifndef GL_DYNAMIC_COPY +#define GL_DYNAMIC_COPY 0x88EA +#endif +#ifndef GL_FRAGMENT_SHADER +#define GL_FRAGMENT_SHADER 0x8B30 +#endif +#ifndef GL_VERTEX_SHADER +#define GL_VERTEX_SHADER 0x8B31 +#endif +#ifndef GL_FLOAT_VEC2 +#define GL_FLOAT_VEC2 0x8B50 +#endif +#ifndef GL_FLOAT_VEC3 +#define GL_FLOAT_VEC3 0x8B51 +#endif +#ifndef GL_FLOAT_VEC4 +#define GL_FLOAT_VEC4 0x8B52 +#endif +#ifndef GL_INT_VEC2 +#define GL_INT_VEC2 0x8B53 +#endif +#ifndef GL_INT_VEC3 +#define GL_INT_VEC3 0x8B54 +#endif +#ifndef GL_INT_VEC4 +#define GL_INT_VEC4 0x8B55 +#endif +#ifndef GL_BOOL +#define GL_BOOL 0x8B56 +#endif +#ifndef GL_BOOL_VEC2 +#define GL_BOOL_VEC2 0x8B57 +#endif +#ifndef GL_BOOL_VEC3 +#define GL_BOOL_VEC3 0x8B58 +#endif +#ifndef GL_BOOL_VEC4 +#define GL_BOOL_VEC4 0x8B59 +#endif +#ifndef GL_FLOAT_MAT2 +#define GL_FLOAT_MAT2 0x8B5A +#endif +#ifndef GL_FLOAT_MAT3 +#define GL_FLOAT_MAT3 0x8B5B +#endif +#ifndef GL_FLOAT_MAT4 +#define GL_FLOAT_MAT4 0x8B5C +#endif +#ifndef GL_SAMPLER_1D +#define GL_SAMPLER_1D 0x8B5D +#endif +#ifndef GL_SAMPLER_2D +#define GL_SAMPLER_2D 0x8B5E +#endif +#ifndef GL_SAMPLER_3D +#define GL_SAMPLER_3D 0x8B5F +#endif +#ifndef GL_SAMPLER_CUBE +#define GL_SAMPLER_CUBE 0x8B60 +#endif +#ifndef GL_COMPILE_STATUS +#define GL_COMPILE_STATUS 0x8B81 +#endif +#ifndef GL_LINK_STATUS +#define GL_LINK_STATUS 0x8B82 +#endif +#ifndef GL_INFO_LOG_LENGTH +#define GL_INFO_LOG_LENGTH 0x8B84 +#endif +#ifndef GL_ACTIVE_UNIFORMS +#define GL_ACTIVE_UNIFORMS 0x8B86 +#endif +#ifndef GL_ACTIVE_UNIFORM_MAX_LENGTH +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#endif +#ifndef GL_ACTIVE_ATTRIBUTES +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#endif +#ifndef GL_ACTIVE_ATTRIBUTE_MAX_LENGTH +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#endif +#ifndef GL_GEOMETRY_SHADER +#define GL_GEOMETRY_SHADER 0x8DD9 +#endif +#ifndef GL_GEOMETRY_VERTICES_OUT +#define GL_GEOMETRY_VERTICES_OUT 0x8DDA +#endif +#ifndef GL_GEOMETRY_INPUT_TYPE +#define GL_GEOMETRY_INPUT_TYPE 0x8DDB +#endif +#ifndef GL_GEOMETRY_OUTPUT_TYPE +#define GL_GEOMETRY_OUTPUT_TYPE 0x8DDC +#endif +#ifndef GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#endif +#ifndef GL_MAX_GEOMETRY_VARYING_COMPONENTS +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS 0x8DDD +#endif +#ifndef GL_MAX_VERTEX_VARYING_COMPONENTS +#define GL_MAX_VERTEX_VARYING_COMPONENTS 0x8DDE +#endif +#ifndef GL_MAX_VARYING_COMPONENTS +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#endif +#ifndef GL_MAX_GEOMETRY_UNIFORM_COMPONENTS +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#endif +#ifndef GL_MAX_GEOMETRY_OUTPUT_VERTICES +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#endif +#ifndef GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#endif +#ifndef GL_LINES_ADJACENCY +#define GL_LINES_ADJACENCY 0xA +#endif +#ifndef GL_LINE_STRIP_ADJACENCY +#define GL_LINE_STRIP_ADJACENCY 0xB +#endif +#ifndef GL_TRIANGLES_ADJACENCY +#define GL_TRIANGLES_ADJACENCY 0xC +#endif +#ifndef GL_TRIANGLE_STRIP_ADJACENCY +#define GL_TRIANGLE_STRIP_ADJACENCY 0xD +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT 0x8DA9 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_LAYERED +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#endif +#ifndef GL_PROGRAM_POINT_SIZE +#define GL_PROGRAM_POINT_SIZE 0x8642 +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QOPENGL_EXTENSIONS_P_H diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp new file mode 100644 index 0000000000..51d6f7ee32 --- /dev/null +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -0,0 +1,1336 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qopenglframebufferobject.h" +#include "qopenglframebufferobject_p.h" + +#include <qdebug.h> +#include <private/qopengl_p.h> +#include <private/qopenglcontext_p.h> +#include <private/qopenglextensions_p.h> +#include <private/qfont_p.h> +#include <private/qopenglpaintengine_p.h> + +#include <qwindow.h> +#include <qlibrary.h> +#include <qimage.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_DEBUG +#define QT_RESET_GLERROR() \ +{ \ + while (glGetError() != GL_NO_ERROR) {} \ +} +#define QT_CHECK_GLERROR() \ +{ \ + GLenum err = glGetError(); \ + if (err != GL_NO_ERROR) { \ + qDebug("[%s line %d] GL Error: %d", \ + __FILE__, __LINE__, (int)err); \ + } \ +} +#else +#define QT_RESET_GLERROR() {} +#define QT_CHECK_GLERROR() {} +#endif + +/*! + \class QOpenGLFramebufferObjectFormat + \brief The QOpenGLFramebufferObjectFormat class specifies the format of an OpenGL + framebuffer object. + + \since 5.0 + + \ingroup painting-3D + + A framebuffer object has several characteristics: + \list + \i \link setSamples() Number of samples per pixels.\endlink + \i \link setAttachment() Depth and/or stencil attachments.\endlink + \i \link setTextureTarget() Texture target.\endlink + \i \link setInternalTextureFormat() Internal texture format.\endlink + \endlist + + Note that the desired attachments or number of samples per pixels might not + be supported by the hardware driver. Call QOpenGLFramebufferObject::format() + after creating a QOpenGLFramebufferObject to find the exact format that was + used to create the frame buffer object. + + \sa QOpenGLFramebufferObject +*/ + +/*! + \internal +*/ +void QOpenGLFramebufferObjectFormat::detach() +{ + if (d->ref != 1) { + QOpenGLFramebufferObjectFormatPrivate *newd + = new QOpenGLFramebufferObjectFormatPrivate(d); + if (!d->ref.deref()) + delete d; + d = newd; + } +} + +/*! + Creates a QOpenGLFramebufferObjectFormat object for specifying + the format of an OpenGL framebuffer object. + + By default the format specifies a non-multisample framebuffer object with no + attachments, texture target \c GL_TEXTURE_2D, and internal format \c GL_RGBA8. + On OpenGL/ES systems, the default internal format is \c GL_RGBA. + + \sa samples(), attachment(), internalTextureFormat() +*/ + +QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat() +{ + d = new QOpenGLFramebufferObjectFormatPrivate; +} + +/*! + Constructs a copy of \a other. +*/ + +QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat(const QOpenGLFramebufferObjectFormat &other) +{ + d = other.d; + d->ref.ref(); +} + +/*! + Assigns \a other to this object. +*/ + +QOpenGLFramebufferObjectFormat &QOpenGLFramebufferObjectFormat::operator=(const QOpenGLFramebufferObjectFormat &other) +{ + if (d != other.d) { + other.d->ref.ref(); + if (!d->ref.deref()) + delete d; + d = other.d; + } + return *this; +} + +/*! + Destroys the QOpenGLFramebufferObjectFormat. +*/ +QOpenGLFramebufferObjectFormat::~QOpenGLFramebufferObjectFormat() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + Sets the number of samples per pixel for a multisample framebuffer object + to \a samples. The default sample count of 0 represents a regular + non-multisample framebuffer object. + + If the desired amount of samples per pixel is not supported by the hardware + then the maximum number of samples per pixel will be used. Note that + multisample framebuffer objects can not be bound as textures. Also, the + \c{GL_EXT_framebuffer_multisample} extension is required to create a + framebuffer with more than one sample per pixel. + + \sa samples() +*/ +void QOpenGLFramebufferObjectFormat::setSamples(int samples) +{ + detach(); + d->samples = samples; +} + +/*! + Returns the number of samples per pixel if a framebuffer object + is a multisample framebuffer object. Otherwise, returns 0. + The default value is 0. + + \sa setSamples() +*/ +int QOpenGLFramebufferObjectFormat::samples() const +{ + return d->samples; +} + +/*! + Enables mipmapping if \a enabled is true; otherwise disables it. + + Mipmapping is disabled by default. + + If mipmapping is enabled, additional memory will be allocated for + the mipmap levels. The mipmap levels can be updated by binding the + texture and calling glGenerateMipmap(). Mipmapping cannot be enabled + for multisampled framebuffer objects. + + \sa mipmap(), QOpenGLFramebufferObject::texture() +*/ +void QOpenGLFramebufferObjectFormat::setMipmap(bool enabled) +{ + detach(); + d->mipmap = enabled; +} + +/*! + Returns true if mipmapping is enabled. + + \sa setMipmap() +*/ +bool QOpenGLFramebufferObjectFormat::mipmap() const +{ + return d->mipmap; +} + +/*! + Sets the attachment configuration of a framebuffer object to \a attachment. + + \sa attachment() +*/ +void QOpenGLFramebufferObjectFormat::setAttachment(QOpenGLFramebufferObject::Attachment attachment) +{ + detach(); + d->attachment = attachment; +} + +/*! + Returns the configuration of the depth and stencil buffers attached to + a framebuffer object. The default is QOpenGLFramebufferObject::NoAttachment. + + \sa setAttachment() +*/ +QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObjectFormat::attachment() const +{ + return d->attachment; +} + +/*! + Sets the texture target of the texture attached to a framebuffer object to + \a target. Ignored for multisample framebuffer objects. + + \sa textureTarget(), samples() +*/ +void QOpenGLFramebufferObjectFormat::setTextureTarget(GLenum target) +{ + detach(); + d->target = target; +} + +/*! + Returns the texture target of the texture attached to a framebuffer object. + Ignored for multisample framebuffer objects. The default is + \c GL_TEXTURE_2D. + + \sa setTextureTarget(), samples() +*/ +GLenum QOpenGLFramebufferObjectFormat::textureTarget() const +{ + return d->target; +} + +/*! + Sets the internal format of a framebuffer object's texture or + multisample framebuffer object's color buffer to + \a internalTextureFormat. + + \sa internalTextureFormat() +*/ +void QOpenGLFramebufferObjectFormat::setInternalTextureFormat(GLenum internalTextureFormat) +{ + detach(); + d->internal_format = internalTextureFormat; +} + +/*! + Returns the internal format of a framebuffer object's texture or + multisample framebuffer object's color buffer. The default is + \c GL_RGBA8 on desktop OpenGL systems, and \c GL_RGBA on + OpenGL/ES systems. + + \sa setInternalTextureFormat() +*/ +GLenum QOpenGLFramebufferObjectFormat::internalTextureFormat() const +{ + return d->internal_format; +} + +/*! + Returns true if all the options of this framebuffer object format + are the same as \a other; otherwise returns false. +*/ +bool QOpenGLFramebufferObjectFormat::operator==(const QOpenGLFramebufferObjectFormat& other) const +{ + if (d == other.d) + return true; + else + return d->equals(other.d); +} + +/*! + Returns false if all the options of this framebuffer object format + are the same as \a other; otherwise returns true. +*/ +bool QOpenGLFramebufferObjectFormat::operator!=(const QOpenGLFramebufferObjectFormat& other) const +{ + return !(*this == other); +} + +void QOpenGLFBOGLPaintDevice::setFBO(QOpenGLFramebufferObject* f, + QOpenGLFramebufferObject::Attachment attachment) +{ + fbo = f; + m_thisFBO = fbo->d_func()->fbo(); // This shouldn't be needed + + // The context that the fbo was created in may not have depth + // and stencil buffers, but the fbo itself might. + fboFormat = QOpenGLContext::currentContext()->format(); + if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil) { + fboFormat.setDepthBufferSize(24); + fboFormat.setStencilBufferSize(8); + } else if (attachment == QOpenGLFramebufferObject::Depth) { + fboFormat.setDepthBufferSize(24); + fboFormat.setStencilBufferSize(0); + } else { + fboFormat.setDepthBufferSize(0); + fboFormat.setStencilBufferSize(0); + } + + GLenum format = f->format().internalTextureFormat(); + reqAlpha = (format != GL_RGB +#ifndef QT_OPENGL_ES + && format != GL_RGB5 && format != GL_RGB8 +#endif + ); +} + +QOpenGLContextGroup *QOpenGLFBOGLPaintDevice::group() const +{ + QOpenGLSharedResourceGuard *fbo_guard = + fbo->d_func()->fbo_guard; + return fbo_guard ? fbo_guard->group() : 0; +} + +bool QOpenGLFramebufferObjectPrivate::checkFramebufferStatus() const +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) + return false; // Context no longer exists. + GLenum status = ctx->functions()->glCheckFramebufferStatus(GL_FRAMEBUFFER); + switch(status) { + case GL_NO_ERROR: + case GL_FRAMEBUFFER_COMPLETE: + return true; + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + qDebug("QOpenGLFramebufferObject: Unsupported framebuffer format."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + qDebug("QOpenGLFramebufferObject: Framebuffer incomplete attachment."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing attachment."); + break; +#ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT + case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT: + qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, duplicate attachment."); + break; +#endif + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_FORMATS: + qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same format."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing draw buffer."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing read buffer."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel."); + break; + default: + qDebug() <<"QOpenGLFramebufferObject: An undefined error has occurred: "<< status; + break; + } + return false; +} + +namespace +{ + void freeFramebufferFunc(QOpenGLFunctions *funcs, GLuint id) + { + funcs->glDeleteFramebuffers(1, &id); + } + + void freeRenderbufferFunc(QOpenGLFunctions *funcs, GLuint id) + { + funcs->glDeleteRenderbuffers(1, &id); + } + + void freeTextureFunc(QOpenGLFunctions *, GLuint id) + { + glDeleteTextures(1, &id); + } +} + +void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *q, const QSize &sz, + QOpenGLFramebufferObject::Attachment attachment, + GLenum texture_target, GLenum internal_format, + GLint samples, bool mipmap) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + funcs.initializeGLFunctions(); + + if (!funcs.hasOpenGLFeature(QOpenGLFunctions::Framebuffers)) + return; + + size = sz; + target = texture_target; + // texture dimensions + + QT_RESET_GLERROR(); // reset error state + GLuint fbo = 0; + + funcs.glGenFramebuffers(1, &fbo); + funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + GLuint texture = 0; + GLuint color_buffer = 0; + GLuint depth_buffer = 0; + GLuint stencil_buffer = 0; + + QT_CHECK_GLERROR(); + // init texture + if (samples == 0) { + glGenTextures(1, &texture); + glBindTexture(target, texture); + glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if (mipmap) + funcs.glGenerateMipmap(GL_TEXTURE_2D); +#ifndef QT_OPENGL_ES + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#else + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#endif + funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + target, texture, 0); + + QT_CHECK_GLERROR(); + valid = checkFramebufferStatus(); + glBindTexture(target, 0); + + color_buffer = 0; + } else { + mipmap = false; + GLint maxSamples; + glGetIntegerv(GL_MAX_SAMPLES, &maxSamples); + + samples = qBound(0, int(samples), int(maxSamples)); + + funcs.glGenRenderbuffers(1, &color_buffer); + funcs.glBindRenderbuffer(GL_RENDERBUFFER, color_buffer); + if (funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample) && samples > 0) { + funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, + internal_format, size.width(), size.height()); + } else { + samples = 0; + funcs.glRenderbufferStorage(GL_RENDERBUFFER, internal_format, + size.width(), size.height()); + } + + funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, color_buffer); + + QT_CHECK_GLERROR(); + valid = checkFramebufferStatus(); + + if (valid) + funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + } + + // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a + // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer + // might not be supported while separate buffers are, according to QTBUG-12861. + + if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil + && funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil)) + { + // depth and stencil buffer needs another extension + funcs.glGenRenderbuffers(1, &depth_buffer); + Q_ASSERT(!funcs.glIsRenderbuffer(depth_buffer)); + funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer); + Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer)); + if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) + funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, + GL_DEPTH24_STENCIL8, size.width(), size.height()); + else + funcs.glRenderbufferStorage(GL_RENDERBUFFER, + GL_DEPTH24_STENCIL8, size.width(), size.height()); + + stencil_buffer = depth_buffer; + funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, depth_buffer); + funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, stencil_buffer); + + valid = checkFramebufferStatus(); + if (!valid) { + funcs.glDeleteRenderbuffers(1, &depth_buffer); + stencil_buffer = depth_buffer = 0; + } + } + + if (depth_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil + || (attachment == QOpenGLFramebufferObject::Depth))) + { + funcs.glGenRenderbuffers(1, &depth_buffer); + Q_ASSERT(!funcs.glIsRenderbuffer(depth_buffer)); + funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer); + Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer)); + if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) { +#ifdef QT_OPENGL_ES + if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) { + funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, + GL_DEPTH_COMPONENT24, size.width(), size.height()); + } else { + funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, + GL_DEPTH_COMPONENT16, size.width(), size.height()); + } +#else + funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, + GL_DEPTH_COMPONENT, size.width(), size.height()); +#endif + } else { +#ifdef QT_OPENGL_ES + if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) { + funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, + size.width(), size.height()); + } else { + funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, + size.width(), size.height()); + } +#else + funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height()); +#endif + } + funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, depth_buffer); + valid = checkFramebufferStatus(); + if (!valid) { + funcs.glDeleteRenderbuffers(1, &depth_buffer); + depth_buffer = 0; + } + } + + if (stencil_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil)) { + funcs.glGenRenderbuffers(1, &stencil_buffer); + Q_ASSERT(!funcs.glIsRenderbuffer(stencil_buffer)); + funcs.glBindRenderbuffer(GL_RENDERBUFFER, stencil_buffer); + Q_ASSERT(funcs.glIsRenderbuffer(stencil_buffer)); + if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) { +#ifdef QT_OPENGL_ES + funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, + GL_STENCIL_INDEX8, size.width(), size.height()); +#else + funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, + GL_STENCIL_INDEX, size.width(), size.height()); +#endif + } else { +#ifdef QT_OPENGL_ES + funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, + size.width(), size.height()); +#else + funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX, + size.width(), size.height()); +#endif + } + funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, stencil_buffer); + valid = checkFramebufferStatus(); + if (!valid) { + funcs.glDeleteRenderbuffers(1, &stencil_buffer); + stencil_buffer = 0; + } + } + + // The FBO might have become valid after removing the depth or stencil buffer. + valid = checkFramebufferStatus(); + + if (depth_buffer && stencil_buffer) { + fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil; + } else if (depth_buffer) { + fbo_attachment = QOpenGLFramebufferObject::Depth; + } else { + fbo_attachment = QOpenGLFramebufferObject::NoAttachment; + } + + funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo); + if (valid) { + fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc); + if (color_buffer) + color_buffer_guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc); + else + texture_guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc); + if (depth_buffer) + depth_buffer_guard = new QOpenGLSharedResourceGuard(ctx, depth_buffer, freeRenderbufferFunc); + if (stencil_buffer) { + if (stencil_buffer == depth_buffer) + stencil_buffer_guard = depth_buffer_guard; + else + stencil_buffer_guard = new QOpenGLSharedResourceGuard(ctx, stencil_buffer, freeRenderbufferFunc); + } + } else { + if (color_buffer) + funcs.glDeleteRenderbuffers(1, &color_buffer); + else + glDeleteTextures(1, &texture); + if (depth_buffer) + funcs.glDeleteRenderbuffers(1, &depth_buffer); + if (stencil_buffer && depth_buffer != stencil_buffer) + funcs.glDeleteRenderbuffers(1, &stencil_buffer); + funcs.glDeleteFramebuffers(1, &fbo); + } + QT_CHECK_GLERROR(); + + format.setTextureTarget(target); + format.setSamples(int(samples)); + format.setAttachment(fbo_attachment); + format.setInternalTextureFormat(internal_format); + format.setMipmap(mipmap); + + glDevice.setFBO(q, attachment); +} + +/*! + \class QOpenGLFramebufferObject + \brief The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer object. + \since 5.0 + + \ingroup painting-3D + + The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer + object, defined by the \c{GL_EXT_framebuffer_object} extension. In + addition it provides a rendering surface that can be painted on + with a QPainter, rendered to using native GL calls, or both. This + surface can be bound and used as a regular texture in your own GL + drawing code. By default, the QOpenGLFramebufferObject class + generates a 2D GL texture (using the \c{GL_TEXTURE_2D} target), + which is used as the internal rendering target. + + \bold{It is important to have a current GL context when creating a + QOpenGLFramebufferObject, otherwise initialization will fail.} + + OpenGL framebuffer objects and pbuffers (see + \l{QOpenGLPixelBuffer}{QOpenGLPixelBuffer}) can both be used to render to + offscreen surfaces, but there are a number of advantages with + using framebuffer objects instead of pbuffers: + + \list 1 + \o A framebuffer object does not require a separate rendering + context, so no context switching will occur when switching + rendering targets. There is an overhead involved in switching + targets, but in general it is cheaper than a context switch to a + pbuffer. + + \o Rendering to dynamic textures (i.e. render-to-texture + functionality) works on all platforms. No need to do explicit copy + calls from a render buffer into a texture, as was necessary on + systems that did not support the \c{render_texture} extension. + + \o It is possible to attach several rendering buffers (or texture + objects) to the same framebuffer object, and render to all of them + without doing a context switch. + + \o The OpenGL framebuffer extension is a pure GL extension with no + system dependant WGL, CGL, or GLX parts. This makes using + framebuffer objects more portable. + \endlist + + When using a QPainter to paint to a QOpenGLFramebufferObject you should take + care that the QOpenGLFramebufferObject is created with the CombinedDepthStencil + attachment for QPainter to be able to render correctly. + Note that you need to create a QOpenGLFramebufferObject with more than one + sample per pixel for primitives to be antialiased when drawing using a + QPainter. To create a multisample framebuffer object you should use one of + the constructors that take a QOpenGLFramebufferObject parameter, and set the + QOpenGLFramebufferObject::samples() property to a non-zero value. + + When painting to a QOpenGLFramebufferObject using QPainter, the state of + the current GL context will be altered by the paint engine to reflect + its needs. Applications should not rely upon the GL state being reset + to its original conditions, particularly the current shader program, + GL viewport, texture units, and drawing modes. + + For multisample framebuffer objects a color render buffer is created, + otherwise a texture with the specified texture target is created. + The color render buffer or texture will have the specified internal + format, and will be bound to the \c GL_COLOR_ATTACHMENT0 + attachment in the framebuffer object. + + If you want to use a framebuffer object with multisampling enabled + as a texture, you first need to copy from it to a regular framebuffer + object using QOpenGLContext::blitFramebuffer(). + + \section1 Threading + + As of Qt 4.8, it's possible to draw into a QOpenGLFramebufferObject + using a QPainter in a separate thread. Note that OpenGL 2.0 or + OpenGL ES 2.0 is required for this to work. + + \sa {Framebuffer Object Example} +*/ + + +/*! + \enum QOpenGLFramebufferObject::Attachment + + This enum type is used to configure the depth and stencil buffers + attached to the framebuffer object when it is created. + + \value NoAttachment No attachment is added to the framebuffer object. Note that the + OpenGL depth and stencil tests won't work when rendering to a + framebuffer object without any depth or stencil buffers. + This is the default value. + + \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present, + a combined depth and stencil buffer is attached. + If the extension is not present, only a depth buffer is attached. + + \value Depth A depth buffer is attached to the framebuffer object. + + \sa attachment() +*/ + + +/*! \fn QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum target) + + Constructs an OpenGL framebuffer object and binds a 2D GL texture + to the buffer of the size \a size. The texture is bound to the + \c GL_COLOR_ATTACHMENT0 target in the framebuffer object. + + The \a target parameter is used to specify the GL texture + target. The default target is \c GL_TEXTURE_2D. Keep in mind that + \c GL_TEXTURE_2D textures must have a power of 2 width and height + (e.g. 256x512), unless you are using OpenGL 2.0 or higher. + + By default, no depth and stencil buffers are attached. This behavior + can be toggled using one of the overloaded constructors. + + The default internal texture format is \c GL_RGBA8 for desktop + OpenGL, and \c GL_RGBA for OpenGL/ES. + + It is important that you have a current GL context set when + creating the QOpenGLFramebufferObject, otherwise the initialization + will fail. + + \sa size(), texture(), attachment() +*/ + +QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum target) + : d_ptr(new QOpenGLFramebufferObjectPrivate) +{ + Q_D(QOpenGLFramebufferObject); + d->init(this, size, NoAttachment, target, DEFAULT_FORMAT); +} + +/*! \overload + + Constructs an OpenGL framebuffer object and binds a 2D GL texture + to the buffer of the given \a width and \a height. + + \sa size(), texture() +*/ +QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, GLenum target) + : d_ptr(new QOpenGLFramebufferObjectPrivate) +{ + Q_D(QOpenGLFramebufferObject); + d->init(this, QSize(width, height), NoAttachment, target, DEFAULT_FORMAT); +} + +/*! \overload + + Constructs an OpenGL framebuffer object of the given \a size based on the + supplied \a format. +*/ + +QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, const QOpenGLFramebufferObjectFormat &format) + : d_ptr(new QOpenGLFramebufferObjectPrivate) +{ + Q_D(QOpenGLFramebufferObject); + d->init(this, size, format.attachment(), format.textureTarget(), format.internalTextureFormat(), + format.samples(), format.mipmap()); +} + +/*! \overload + + Constructs an OpenGL framebuffer object of the given \a width and \a height + based on the supplied \a format. +*/ + +QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, const QOpenGLFramebufferObjectFormat &format) + : d_ptr(new QOpenGLFramebufferObjectPrivate) +{ + Q_D(QOpenGLFramebufferObject); + d->init(this, QSize(width, height), format.attachment(), format.textureTarget(), + format.internalTextureFormat(), format.samples(), format.mipmap()); +} + +/*! \overload + + Constructs an OpenGL framebuffer object and binds a texture to the + buffer of the given \a width and \a height. + + The \a attachment parameter describes the depth/stencil buffer + configuration, \a target the texture target and \a internal_format + the internal texture format. The default texture target is \c + GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8 + for desktop OpenGL and \c GL_RGBA for OpenGL/ES. + + \sa size(), texture(), attachment() +*/ +QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, Attachment attachment, + GLenum target, GLenum internal_format) + : d_ptr(new QOpenGLFramebufferObjectPrivate) +{ + Q_D(QOpenGLFramebufferObject); + d->init(this, QSize(width, height), attachment, target, internal_format); +} + +/*! \overload + + Constructs an OpenGL framebuffer object and binds a texture to the + buffer of the given \a size. + + The \a attachment parameter describes the depth/stencil buffer + configuration, \a target the texture target and \a internal_format + the internal texture format. The default texture target is \c + GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8 + for desktop OpenGL and \c GL_RGBA for OpenGL/ES. + + \sa size(), texture(), attachment() +*/ +QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, Attachment attachment, + GLenum target, GLenum internal_format) + : d_ptr(new QOpenGLFramebufferObjectPrivate) +{ + Q_D(QOpenGLFramebufferObject); + d->init(this, size, attachment, target, internal_format); +} + +/*! + \fn QOpenGLFramebufferObject::~QOpenGLFramebufferObject() + + Destroys the framebuffer object and frees any allocated resources. +*/ +QOpenGLFramebufferObject::~QOpenGLFramebufferObject() +{ + Q_D(QOpenGLFramebufferObject); + + delete d->engine; + + if (d->texture_guard) + d->texture_guard->free(); + if (d->color_buffer_guard) + d->color_buffer_guard->free(); + if (d->depth_buffer_guard) + d->depth_buffer_guard->free(); + if (d->stencil_buffer_guard && d->stencil_buffer_guard != d->depth_buffer_guard) + d->stencil_buffer_guard->free(); + if (d->fbo_guard) + d->fbo_guard->free(); +} + +/*! + \fn bool QOpenGLFramebufferObject::isValid() const + + Returns true if the framebuffer object is valid. + + The framebuffer can become invalid if the initialization process + fails, the user attaches an invalid buffer to the framebuffer + object, or a non-power of two width/height is specified as the + texture size if the texture target is \c{GL_TEXTURE_2D}. + The non-power of two limitation does not apply if the OpenGL version + is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension + is present. + + The framebuffer can also become invalid if the QOpenGLContext that + the framebuffer was created within is destroyed and there are + no other shared contexts that can take over ownership of the + framebuffer. +*/ +bool QOpenGLFramebufferObject::isValid() const +{ + Q_D(const QOpenGLFramebufferObject); + return d->valid && d->fbo_guard && d->fbo_guard->id(); +} + +/*! + \fn bool QOpenGLFramebufferObject::bind() + + Switches rendering from the default, windowing system provided + framebuffer to this framebuffer object. + Returns true upon success, false otherwise. + + \sa release() +*/ +bool QOpenGLFramebufferObject::bind() +{ + if (!isValid()) + return false; + Q_D(QOpenGLFramebufferObject); + QOpenGLContext *current = QOpenGLContext::currentContext(); + if (!current) + return false; +#ifdef QT_DEBUG + if (current->shareGroup() != d->fbo_guard->group()) + qWarning("QOpenGLFramebufferObject::bind() called from incompatible context"); +#endif + d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo()); + d->valid = d->checkFramebufferStatus(); + if (d->valid && current) + current->d_func()->current_fbo = d->fbo(); + return d->valid; +} + +/*! + \fn bool QOpenGLFramebufferObject::release() + + Switches rendering back to the default, windowing system provided + framebuffer. + Returns true upon success, false otherwise. + + \sa bind() +*/ +bool QOpenGLFramebufferObject::release() +{ + if (!isValid()) + return false; + + QOpenGLContext *current = QOpenGLContext::currentContext(); + if (!current) + return false; + + Q_D(QOpenGLFramebufferObject); +#ifdef QT_DEBUG + if (current->shareGroup() != d->fbo_guard->group()) + qWarning("QOpenGLFramebufferObject::release() called from incompatible context"); +#endif + + if (current) { + current->d_func()->current_fbo = current->d_func()->default_fbo; + d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->default_fbo); + } + + return true; +} + +/*! + \fn GLuint QOpenGLFramebufferObject::texture() const + + Returns the texture id for the texture attached as the default + rendering target in this framebuffer object. This texture id can + be bound as a normal texture in your own GL code. + + If a multisample framebuffer object is used then the value returned + from this function will be invalid. +*/ +GLuint QOpenGLFramebufferObject::texture() const +{ + Q_D(const QOpenGLFramebufferObject); + return d->texture_guard ? d->texture_guard->id() : 0; +} + +/*! + \fn QSize QOpenGLFramebufferObject::size() const + + Returns the size of the texture attached to this framebuffer + object. +*/ +QSize QOpenGLFramebufferObject::size() const +{ + Q_D(const QOpenGLFramebufferObject); + return d->size; +} + +/*! + Returns the format of this framebuffer object. +*/ +QOpenGLFramebufferObjectFormat QOpenGLFramebufferObject::format() const +{ + Q_D(const QOpenGLFramebufferObject); + return d->format; +} + +namespace { +/* + Read back the contents of the currently bound framebuffer, used in + QGLWidget::grabFrameBuffer(), QGLPixelbuffer::toImage() and + QGLFramebufferObject::toImage() +*/ + +void convertFromGLImage(QImage &img, int w, int h, bool alpha_format, bool include_alpha) +{ + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + // OpenGL gives RGBA; Qt wants ARGB + uint *p = (uint*)img.bits(); + uint *end = p + w*h; + if (alpha_format && include_alpha) { + while (p < end) { + uint a = *p << 24; + *p = (*p >> 8) | a; + p++; + } + } else { + // This is an old legacy fix for PowerPC based Macs, which + // we shouldn't remove + while (p < end) { + *p = 0xff000000 | (*p>>8); + ++p; + } + } + } else { + // OpenGL gives ABGR (i.e. RGBA backwards); Qt wants ARGB + for (int y = 0; y < h; y++) { + uint *q = (uint*)img.scanLine(y); + for (int x=0; x < w; ++x) { + const uint pixel = *q; + if (alpha_format && include_alpha) { + *q = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) + | (pixel & 0xff00ff00); + } else { + *q = 0xff000000 | ((pixel << 16) & 0xff0000) + | ((pixel >> 16) & 0xff) | (pixel & 0x00ff00); + } + + q++; + } + } + + } + img = img.mirrored(); +} + +} + +Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha) +{ + QImage img(size, (alpha_format && include_alpha) ? QImage::Format_ARGB32_Premultiplied + : QImage::Format_RGB32); + int w = size.width(); + int h = size.height(); + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); + convertFromGLImage(img, w, h, alpha_format, include_alpha); + return img; +} + +/*! + \fn QImage QOpenGLFramebufferObject::toImage() const + + Returns the contents of this framebuffer object as a QImage. +*/ +QImage QOpenGLFramebufferObject::toImage() const +{ + Q_D(const QOpenGLFramebufferObject); + if (!d->valid) + return QImage(); + + // qt_gl_read_framebuffer doesn't work on a multisample FBO + if (format().samples() != 0) { + QOpenGLFramebufferObject temp(size(), QOpenGLFramebufferObjectFormat()); + + QRect rect(QPoint(0, 0), size()); + blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect); + + return temp.toImage(); + } + + bool wasBound = isBound(); + if (!wasBound) + const_cast<QOpenGLFramebufferObject *>(this)->bind(); + QImage image = qt_gl_read_framebuffer(d->size, format().internalTextureFormat() != GL_RGB, true); + if (!wasBound) + const_cast<QOpenGLFramebufferObject *>(this)->release(); + + return image; +} + +Q_GLOBAL_STATIC(QOpenGLEngineThreadStorage<QOpenGL2PaintEngineEx>, qt_buffer_2_engine) + +/*! \reimp */ +QPaintEngine *QOpenGLFramebufferObject::paintEngine() const +{ + Q_D(const QOpenGLFramebufferObject); + if (d->engine) + return d->engine; + + QPaintEngine *engine = qt_buffer_2_engine()->engine(); + if (engine->isActive() && engine->paintDevice() != this) { + d->engine = new QOpenGL2PaintEngineEx; + return d->engine; + } + return engine; +} + +/*! + \fn bool QOpenGLFramebufferObject::bindDefault() + \internal + + Switches rendering back to the default, windowing system provided + framebuffer. + Returns true upon success, false otherwise. + + \sa bind(), release() +*/ +bool QOpenGLFramebufferObject::bindDefault() +{ + QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); + QOpenGLFunctions functions(ctx); + + if (ctx) { + ctx->d_func()->current_fbo = ctx->d_func()->default_fbo; + functions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->default_fbo); +#ifdef QT_DEBUG + } else { + qWarning("QOpenGLFramebufferObject::bindDefault() called without current context."); +#endif + } + + return ctx != 0; +} + +/*! + \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects() + + Returns true if the OpenGL \c{GL_EXT_framebuffer_object} extension + is present on this system; otherwise returns false. +*/ +bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects() +{ + return QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::Framebuffers); +} + +/*! \reimp */ +int QOpenGLFramebufferObject::metric(PaintDeviceMetric metric) const +{ + Q_D(const QOpenGLFramebufferObject); + + float dpmx = qt_defaultDpiX()*100./2.54; + float dpmy = qt_defaultDpiY()*100./2.54; + int w = d->size.width(); + int h = d->size.height(); + switch (metric) { + case PdmWidth: + return w; + + case PdmHeight: + return h; + + case PdmWidthMM: + return qRound(w * 1000 / dpmx); + + case PdmHeightMM: + return qRound(h * 1000 / dpmy); + + case PdmNumColors: + return 0; + + case PdmDepth: + return 32;//d->depth; + + case PdmDpiX: + return qRound(dpmx * 0.0254); + + case PdmDpiY: + return qRound(dpmy * 0.0254); + + case PdmPhysicalDpiX: + return qRound(dpmx * 0.0254); + + case PdmPhysicalDpiY: + return qRound(dpmy * 0.0254); + + default: + qWarning("QOpenGLFramebufferObject::metric(), Unhandled metric type: %d.\n", metric); + break; + } + return 0; +} + +/*! + \fn GLuint QOpenGLFramebufferObject::handle() const + + Returns the GL framebuffer object handle for this framebuffer + object (returned by the \c{glGenFrameBuffersEXT()} function). This + handle can be used to attach new images or buffers to the + framebuffer. The user is responsible for cleaning up and + destroying these objects. +*/ +GLuint QOpenGLFramebufferObject::handle() const +{ + Q_D(const QOpenGLFramebufferObject); + return d->fbo(); +} + +/*! \fn int QOpenGLFramebufferObject::devType() const + \internal +*/ + + +/*! + Returns the status of the depth and stencil buffers attached to + this framebuffer object. +*/ + +QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObject::attachment() const +{ + Q_D(const QOpenGLFramebufferObject); + if (d->valid) + return d->fbo_attachment; + return NoAttachment; +} + +/*! + Returns true if the framebuffer object is currently bound to a context, + otherwise false is returned. +*/ + +bool QOpenGLFramebufferObject::isBound() const +{ + Q_D(const QOpenGLFramebufferObject); + QOpenGLContext *current = QOpenGLContext::currentContext(); + return current ? current->d_func()->current_fbo == d->fbo() : false; +} + +/*! + \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit() + + Returns true if the OpenGL \c{GL_EXT_framebuffer_blit} extension + is present on this system; otherwise returns false. + + \sa blitFramebuffer() +*/ +bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit() +{ + return QOpenGLExtensions(QOpenGLContext::currentContext()).hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit); +} + +/*! + Blits from the \a sourceRect rectangle in the \a source framebuffer + object to the \a targetRect rectangle in the \a target framebuffer object. + + If \a source or \a target is 0, the default framebuffer will be used + instead of a framebuffer object as source or target respectively. + + The \a buffers parameter should be a mask consisting of any combination of + \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and + \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both + in the source and target buffers is ignored. + + The \a sourceRect and \a targetRect rectangles may have different sizes; + in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or + \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to + \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest + interpolation should be used when scaling is performed. + + If \a source equals \a target a copy is performed within the same buffer. + Results are undefined if the source and target rectangles overlap and + have different sizes. The sizes must also be the same if any of the + framebuffer objects are multisample framebuffers. + + Note that the scissor test will restrict the blit area if enabled. + + This function will have no effect unless hasOpenGLFramebufferBlit() returns + true. + + \sa hasOpenGLFramebufferBlit() +*/ +void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect, + QOpenGLFramebufferObject *source, const QRect &sourceRect, + GLbitfield buffers, + GLenum filter) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) + return; + + QOpenGLExtensions extensions(ctx); + if (!extensions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) + return; + + QSurface *surface = ctx->surface(); + + const int height = static_cast<QWindow *>(surface)->height(); + + const int sh = source ? source->height() : height; + const int th = target ? target->height() : height; + + const int sx0 = sourceRect.left(); + const int sx1 = sourceRect.left() + sourceRect.width(); + const int sy0 = sh - (sourceRect.top() + sourceRect.height()); + const int sy1 = sh - sourceRect.top(); + + const int tx0 = targetRect.left(); + const int tx1 = targetRect.left() + targetRect.width(); + const int ty0 = th - (targetRect.top() + targetRect.height()); + const int ty1 = th - targetRect.top(); + + extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, source ? source->handle() : 0); + extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target ? target->handle() : 0); + + extensions.glBlitFramebuffer(sx0, sy0, sx1, sy1, + tx0, ty0, tx1, ty1, + buffers, filter); + + extensions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo); +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglframebufferobject.h b/src/gui/opengl/qopenglframebufferobject.h new file mode 100644 index 0000000000..b7c38479ff --- /dev/null +++ b/src/gui/opengl/qopenglframebufferobject.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGLFRAMEBUFFEROBJECT_H +#define QOPENGLFRAMEBUFFEROBJECT_H + +#include <QtGui/qopengl.h> +#include <QtGui/qpaintdevice.h> + +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QOpenGLFramebufferObjectPrivate; +class QOpenGLFramebufferObjectFormat; + +class Q_GUI_EXPORT QOpenGLFramebufferObject : public QPaintDevice +{ + Q_DECLARE_PRIVATE(QOpenGLFramebufferObject) +public: + enum Attachment { + NoAttachment, + CombinedDepthStencil, + Depth + }; + + QOpenGLFramebufferObject(const QSize &size, GLenum target = GL_TEXTURE_2D); + QOpenGLFramebufferObject(int width, int height, GLenum target = GL_TEXTURE_2D); +#if !defined(QT_OPENGL_ES) || defined(Q_QDOC) + QOpenGLFramebufferObject(const QSize &size, Attachment attachment, + GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA8); + QOpenGLFramebufferObject(int width, int height, Attachment attachment, + GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA8); +#else + QOpenGLFramebufferObject(const QSize &size, Attachment attachment, + GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA); + QOpenGLFramebufferObject(int width, int height, Attachment attachment, + GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA); +#endif + + QOpenGLFramebufferObject(const QSize &size, const QOpenGLFramebufferObjectFormat &format); + QOpenGLFramebufferObject(int width, int height, const QOpenGLFramebufferObjectFormat &format); + + virtual ~QOpenGLFramebufferObject(); + + QOpenGLFramebufferObjectFormat format() const; + + bool isValid() const; + bool isBound() const; + bool bind(); + bool release(); + + GLuint texture() const; + QSize size() const; + QImage toImage() const; + Attachment attachment() const; + + QPaintEngine *paintEngine() const; + GLuint handle() const; + + static bool bindDefault(); + + static bool hasOpenGLFramebufferObjects(); + + static bool hasOpenGLFramebufferBlit(); + static void blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect, + QOpenGLFramebufferObject *source, const QRect &sourceRect, + GLbitfield buffers = GL_COLOR_BUFFER_BIT, + GLenum filter = GL_NEAREST); + +protected: + int metric(PaintDeviceMetric metric) const; + int devType() const { return QInternal::FramebufferObject; } + +private: + Q_DISABLE_COPY(QOpenGLFramebufferObject) + QScopedPointer<QOpenGLFramebufferObjectPrivate> d_ptr; + friend class QOpenGLPaintDevice; + friend class QOpenGLFBOGLPaintDevice; +}; + +class QOpenGLFramebufferObjectFormatPrivate; +class Q_GUI_EXPORT QOpenGLFramebufferObjectFormat +{ +public: + QOpenGLFramebufferObjectFormat(); + QOpenGLFramebufferObjectFormat(const QOpenGLFramebufferObjectFormat &other); + QOpenGLFramebufferObjectFormat &operator=(const QOpenGLFramebufferObjectFormat &other); + ~QOpenGLFramebufferObjectFormat(); + + void setSamples(int samples); + int samples() const; + + void setMipmap(bool enabled); + bool mipmap() const; + + void setAttachment(QOpenGLFramebufferObject::Attachment attachment); + QOpenGLFramebufferObject::Attachment attachment() const; + + void setTextureTarget(GLenum target); + GLenum textureTarget() const; + + void setInternalTextureFormat(GLenum internalTextureFormat); + GLenum internalTextureFormat() const; + + bool operator==(const QOpenGLFramebufferObjectFormat& other) const; + bool operator!=(const QOpenGLFramebufferObjectFormat& other) const; + +private: + QOpenGLFramebufferObjectFormatPrivate *d; + + void detach(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QOPENGLFRAMEBUFFEROBJECT_H diff --git a/src/gui/opengl/qopenglframebufferobject_p.h b/src/gui/opengl/qopenglframebufferobject_p.h new file mode 100644 index 0000000000..6ea6eb26ee --- /dev/null +++ b/src/gui/opengl/qopenglframebufferobject_p.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGLFRAMEBUFFEROBJECT_P_H +#define QOPENGLFRAMEBUFFEROBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +QT_BEGIN_INCLUDE_NAMESPACE + +#include <qopenglframebufferobject.h> +#include <private/qopenglpaintdevice_p.h> +#include <private/qopenglcontext_p.h> +#include <private/qopenglextensions_p.h> + +QT_END_INCLUDE_NAMESPACE + +#ifndef QT_OPENGL_ES +#define DEFAULT_FORMAT GL_RGBA8 +#else +#define DEFAULT_FORMAT GL_RGBA +#endif + +class QOpenGLFramebufferObjectFormatPrivate +{ +public: + QOpenGLFramebufferObjectFormatPrivate() + : ref(1), + samples(0), + attachment(QOpenGLFramebufferObject::NoAttachment), + target(GL_TEXTURE_2D), + internal_format(DEFAULT_FORMAT), + mipmap(false) + { + } + QOpenGLFramebufferObjectFormatPrivate + (const QOpenGLFramebufferObjectFormatPrivate *other) + : ref(1), + samples(other->samples), + attachment(other->attachment), + target(other->target), + internal_format(other->internal_format), + mipmap(other->mipmap) + { + } + bool equals(const QOpenGLFramebufferObjectFormatPrivate *other) + { + return samples == other->samples && + attachment == other->attachment && + target == other->target && + internal_format == other->internal_format && + mipmap == other->mipmap; + } + + QAtomicInt ref; + int samples; + QOpenGLFramebufferObject::Attachment attachment; + GLenum target; + GLenum internal_format; + uint mipmap : 1; +}; + +class QOpenGLFBOGLPaintDevice : public QOpenGLPaintDevice +{ +public: + virtual QPaintEngine* paintEngine() const {return fbo->paintEngine();} + virtual QSize size() const {return fbo->size();} + virtual QSurfaceFormat format() const {return fboFormat;} + virtual QOpenGLContextGroup *group() const; + virtual bool alphaRequested() const { return reqAlpha; } + + void setFBO(QOpenGLFramebufferObject* f, + QOpenGLFramebufferObject::Attachment attachment); + +private: + QOpenGLFramebufferObject* fbo; + QSurfaceFormat fboFormat; + bool wasBound; + bool reqAlpha; +}; + +class QOpenGLFramebufferObjectPrivate +{ +public: + QOpenGLFramebufferObjectPrivate() : fbo_guard(0), texture_guard(0), depth_buffer_guard(0) + , stencil_buffer_guard(0), color_buffer_guard(0) + , valid(false), engine(0) {} + ~QOpenGLFramebufferObjectPrivate() {} + + void init(QOpenGLFramebufferObject *q, const QSize& sz, + QOpenGLFramebufferObject::Attachment attachment, + GLenum internal_format, GLenum texture_target, + GLint samples = 0, bool mipmap = false); + bool checkFramebufferStatus() const; + QOpenGLSharedResourceGuard *fbo_guard; + QOpenGLSharedResourceGuard *texture_guard; + QOpenGLSharedResourceGuard *depth_buffer_guard; + QOpenGLSharedResourceGuard *stencil_buffer_guard; + QOpenGLSharedResourceGuard *color_buffer_guard; + GLenum target; + QSize size; + QOpenGLFramebufferObjectFormat format; + uint valid : 1; + QOpenGLFramebufferObject::Attachment fbo_attachment; + mutable QPaintEngine *engine; + QOpenGLFBOGLPaintDevice glDevice; + QOpenGLExtensions funcs; + + inline GLuint fbo() const { return fbo_guard ? fbo_guard->id() : 0; } +}; + + +QT_END_NAMESPACE + +#endif // QOPENGLFRAMEBUFFEROBJECT_P_H diff --git a/src/gui/opengl/qopenglfunctions.cpp b/src/gui/opengl/qopenglfunctions.cpp new file mode 100644 index 0000000000..8396a0e2d6 --- /dev/null +++ b/src/gui/opengl/qopenglfunctions.cpp @@ -0,0 +1,2452 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qopenglfunctions.h" +#include "qopenglextensions_p.h" +#include "qdebug.h" +#include "QtGui/private/qopenglcontext_p.h" +#include "QtGui/private/qopengl_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QOpenGLFunctions + \brief The QOpenGLFunctions class provides cross-platform access to the OpenGL/ES 2.0 API. + \since 5.0 + \ingroup painting-3D + + OpenGL/ES 2.0 defines a subset of the OpenGL specification that is + common across many desktop and embedded OpenGL implementations. + However, it can be difficult to use the functions from that subset + because they need to be resolved manually on desktop systems. + + QOpenGLFunctions provides a guaranteed API that is available on all + OpenGL systems and takes care of function resolution on systems + that need it. The recommended way to use QOpenGLFunctions is by + direct inheritance: + + \code + class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions + { + Q_OBJECT + public: + MyGLWidget(QWidget *parent = 0) : QOpenGLWidget(parent) {} + + protected: + void initializeGL(); + void paintGL(); + }; + + void MyGLWidget::initializeGL() + { + initializeGLFunctions(); + } + \endcode + + The \c{paintGL()} function can then use any of the OpenGL/ES 2.0 + functions without explicit resolution, such as glActiveTexture() + in the following example: + + \code + void MyGLWidget::paintGL() + { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, textureId); + ... + } + \endcode + + QOpenGLFunctions can also be used directly for ad-hoc invocation + of OpenGL/ES 2.0 functions on all platforms: + + \code + QOpenGLFunctions glFuncs(QOpenGLContext::currentContext()); + glFuncs.glActiveTexture(GL_TEXTURE1); + \endcode + + QOpenGLFunctions provides wrappers for all OpenGL/ES 2.0 functions, + except those like \c{glDrawArrays()}, \c{glViewport()}, and + \c{glBindTexture()} that don't have portability issues. + + Including the header for QOpenGLFunctions will also define all of + the OpenGL/ES 2.0 macro constants that are not already defined by + the system's OpenGL headers, such as \c{GL_TEXTURE1} above. + + The hasOpenGLFeature() and openGLFeatures() functions can be used + to determine if the OpenGL implementation has a major OpenGL/ES 2.0 + feature. For example, the following checks if non power of two + textures are available: + + \code + QOpenGLFunctions funcs(QOpenGLContext::currentContext()); + bool npot = funcs.hasOpenGLFeature(QOpenGLFunctions::NPOTTextures); + \endcode +*/ + +/*! + \enum QOpenGLFunctions::OpenGLFeature + This enum defines OpenGL/ES 2.0 features that may be optional + on other platforms. + + \value Multitexture glActiveTexture() function is available. + \value Shaders Shader functions are available. + \value Buffers Vertex and index buffer functions are available. + \value Framebuffers Framebuffer object functions are available. + \value BlendColor glBlendColor() is available. + \value BlendEquation glBlendEquation() is available. + \value BlendEquationSeparate glBlendEquationSeparate() is available. + \value BlendFuncSeparate glBlendFuncSeparate() is available. + \value BlendSubtract Blend subtract mode is available. + \value CompressedTextures Compressed texture functions are available. + \value Multisample glSampleCoverage() function is available. + \value StencilSeparate Separate stencil functions are available. + \value NPOTTextures Non power of two textures are available. +*/ + +// Hidden private fields for additional extension data. +struct QOpenGLFunctionsPrivateEx : public QOpenGLExtensionsPrivate, public QOpenGLSharedResource +{ + QOpenGLFunctionsPrivateEx(QOpenGLContext *context) + : QOpenGLExtensionsPrivate(context) + , QOpenGLSharedResource(context->shareGroup()) + , m_features(-1) + , m_extensions(-1) + {} + + void invalidateResource() + { + m_features = -1; + m_extensions = -1; + } + + void freeResource(QOpenGLContext *) + { + // no gl resources to free + } + + int m_features; + int m_extensions; +}; + +Q_GLOBAL_STATIC(QOpenGLMultiGroupSharedResource, qt_gl_functions_resource) + +static QOpenGLFunctionsPrivateEx *qt_gl_functions(QOpenGLContext *context = 0) +{ + if (!context) + context = QOpenGLContext::currentContext(); + Q_ASSERT(context); + QOpenGLFunctionsPrivateEx *funcs = + qt_gl_functions_resource()->value<QOpenGLFunctionsPrivateEx>(context); + return funcs; +} + +/*! + Constructs a default function resolver. The resolver cannot + be used until initializeGLFunctions() is called to specify + the context. + + \sa initializeGLFunctions() +*/ +QOpenGLFunctions::QOpenGLFunctions() + : d_ptr(0) +{ +} + +/*! + Constructs a function resolver for \a context. If \a context + is null, then the resolver will be created for the current QOpenGLContext. + + The context or another context in the group must be current. + + An object constructed in this way can only be used with \a context + and other contexts that share with it. Use initializeGLFunctions() + to change the object's context association. + + \sa initializeGLFunctions() +*/ +QOpenGLFunctions::QOpenGLFunctions(QOpenGLContext *context) + : d_ptr(0) +{ + if (context && QOpenGLContextGroup::currentContextGroup() == context->shareGroup()) + d_ptr = qt_gl_functions(); + else + qWarning() << "QOpenGLFunctions created with non-current context"; +} + +QOpenGLExtensions::QOpenGLExtensions() + : QOpenGLFunctions() +{ +} + +QOpenGLExtensions::QOpenGLExtensions(QOpenGLContext *context) + : QOpenGLFunctions(context) +{ +} + +/*! + \fn QOpenGLFunctions::~QOpenGLFunctions() + + Destroys this function resolver. +*/ + +static int qt_gl_resolve_features() +{ +#if defined(QT_OPENGL_ES_2) + int features = QOpenGLFunctions::Multitexture | + QOpenGLFunctions::Shaders | + QOpenGLFunctions::Buffers | + QOpenGLFunctions::Framebuffers | + QOpenGLFunctions::BlendColor | + QOpenGLFunctions::BlendEquation | + QOpenGLFunctions::BlendEquationSeparate | + QOpenGLFunctions::BlendFuncSeparate | + QOpenGLFunctions::BlendSubtract | + QOpenGLFunctions::CompressedTextures | + QOpenGLFunctions::Multisample | + QOpenGLFunctions::StencilSeparate; + QOpenGLExtensionMatcher extensions; + if (extensions.match("GL_OES_texture_npot")) + features |= QOpenGLFunctions::NPOTTextures; + if (extensions.match("GL_IMG_texture_npot")) + features |= QOpenGLFunctions::NPOTTextures; + return features; +#elif defined(QT_OPENGL_ES) + int features = QOpenGLFunctions::Multitexture | + QOpenGLFunctions::Buffers | + QOpenGLFunctions::CompressedTextures | + QOpenGLFunctions::Multisample; + QOpenGLExtensionMatcher extensions; + if (extensions.match("GL_OES_framebuffer_object")) + features |= QOpenGLFunctions::Framebuffers; + if (extensions.match("GL_OES_blend_equation_separate")) + features |= QOpenGLFunctions::BlendEquationSeparate; + if (extensions.match("GL_OES_blend_func_separate")) + features |= QOpenGLFunctions::BlendFuncSeparate; + if (extensions.match("GL_OES_blend_subtract")) + features |= QOpenGLFunctions::BlendSubtract; + if (extensions.match("GL_OES_texture_npot")) + features |= QOpenGLFunctions::NPOTTextures; + if (extensions.match("GL_IMG_texture_npot")) + features |= QOpenGLFunctions::NPOTTextures; + return features; +#else + int features = 0; + //QOpenGLFormat::OpenGLVersionFlags versions = QOpenGLFormat::openGLVersionFlags(); + QOpenGLExtensionMatcher extensions; + + // Recognize features by extension name. + if (extensions.match("GL_ARB_multitexture")) + features |= QOpenGLFunctions::Multitexture; + if (extensions.match("GL_ARB_shader_objects")) + features |= QOpenGLFunctions::Shaders; + if (extensions.match("GL_EXT_framebuffer_object") || + extensions.match("GL_ARB_framebuffer_object")) + features |= QOpenGLFunctions::Framebuffers; + if (extensions.match("GL_EXT_blend_color")) + features |= QOpenGLFunctions::BlendColor; + if (extensions.match("GL_EXT_blend_equation_separate")) + features |= QOpenGLFunctions::BlendEquationSeparate; + if (extensions.match("GL_EXT_blend_func_separate")) + features |= QOpenGLFunctions::BlendFuncSeparate; + if (extensions.match("GL_EXT_blend_subtract")) + features |= QOpenGLFunctions::BlendSubtract; + if (extensions.match("GL_ARB_texture_compression")) + features |= QOpenGLFunctions::CompressedTextures; + if (extensions.match("GL_ARB_multisample")) + features |= QOpenGLFunctions::Multisample; + if (extensions.match("GL_ARB_texture_non_power_of_two")) + features |= QOpenGLFunctions::NPOTTextures; + + // assume version 2.0 or higher + features |= QOpenGLFunctions::BlendColor | + QOpenGLFunctions::BlendEquation | + QOpenGLFunctions::Multitexture | + QOpenGLFunctions::CompressedTextures | + QOpenGLFunctions::Multisample | + QOpenGLFunctions::BlendFuncSeparate | + QOpenGLFunctions::Buffers | + QOpenGLFunctions::Shaders | + QOpenGLFunctions::StencilSeparate | + QOpenGLFunctions::BlendEquationSeparate | + QOpenGLFunctions::NPOTTextures; + return features; +#endif +} + +static int qt_gl_resolve_extensions() +{ + int extensions = 0; + QOpenGLExtensionMatcher extensionMatcher; +#if defined(QT_OPENGL_ES) + if (extensionMatcher.match("GL_OES_mapbuffer")) + extensions |= QOpenGLExtensions::MapBuffer; + if (extensionMatcher.match("GL_OES_packed_depth_stencil")) + extensions |= QOpenGLExtensions::PackedDepthStencil; + if (extensionMatcher.match("GL_OES_element_index_uint")) + extensions |= QOpenGLExtensions::ElementIndexUint; + if (extensionMatcher.match("GL_OES_depth24")) + extensions |= QOpenGLExtensions::Depth24; +#else + extensions |= QOpenGLExtensions::ElementIndexUint | QOpenGLExtensions::MapBuffer; + + // Recognize features by extension name. + if (extensionMatcher.match("GL_ARB_framebuffer_object")) { + extensions |= QOpenGLExtensions::FramebufferMultisample | + QOpenGLExtensions::FramebufferBlit | + QOpenGLExtensions::PackedDepthStencil; + } else { + if (extensionMatcher.match("GL_EXT_framebuffer_multisample")) + extensions |= QOpenGLExtensions::FramebufferMultisample; + if (extensionMatcher.match("GL_EXT_framebuffer_blit")) + extensions |= QOpenGLExtensions::FramebufferBlit; + if (extensionMatcher.match("GL_EXT_pakced_depth_stencil")) + extensions |= QOpenGLExtensions::PackedDepthStencil; + } +#endif + return extensions; +} + +/*! + Returns the set of features that are present on this system's + OpenGL implementation. + + It is assumed that the QOpenGLContext associated with this function + resolver is current. + + \sa hasOpenGLFeature() +*/ +QOpenGLFunctions::OpenGLFeatures QOpenGLFunctions::openGLFeatures() const +{ + QOpenGLFunctionsPrivateEx *d = static_cast<QOpenGLFunctionsPrivateEx *>(d_ptr); + if (!d) + return 0; + if (d->m_features == -1) + d->m_features = qt_gl_resolve_features(); + return QOpenGLFunctions::OpenGLFeatures(d->m_features); +} + +/*! + Returns true if \a feature is present on this system's OpenGL + implementation; false otherwise. + + It is assumed that the QOpenGLContext associated with this function + resolver is current. + + \sa openGLFeatures() +*/ +bool QOpenGLFunctions::hasOpenGLFeature(QOpenGLFunctions::OpenGLFeature feature) const +{ + QOpenGLFunctionsPrivateEx *d = static_cast<QOpenGLFunctionsPrivateEx *>(d_ptr); + if (!d) + return false; + if (d->m_features == -1) + d->m_features = qt_gl_resolve_features(); + return (d->m_features & int(feature)) != 0; +} + +/*! + Returns the set of extensions that are present on this system's + OpenGL implementation. + + It is assumed that the QOpenGLContext associated with this extension + resolver is current. + + \sa hasOpenGLExtensions() +*/ +QOpenGLExtensions::OpenGLExtensions QOpenGLExtensions::openGLExtensions() +{ + QOpenGLFunctionsPrivateEx *d = static_cast<QOpenGLFunctionsPrivateEx *>(d_ptr); + if (!d) + return 0; + if (d->m_extensions == -1) + d->m_extensions = qt_gl_resolve_extensions(); + return QOpenGLExtensions::OpenGLExtensions(d->m_extensions); +} + +/*! + Returns true if \a extension is present on this system's OpenGL + implementation; false otherwise. + + It is assumed that the QOpenGLContext associated with this extension + resolver is current. + + \sa openGLFeatures() +*/ +bool QOpenGLExtensions::hasOpenGLExtension(QOpenGLExtensions::OpenGLExtension extension) const +{ + QOpenGLFunctionsPrivateEx *d = static_cast<QOpenGLFunctionsPrivateEx *>(d_ptr); + if (!d) + return false; + if (d->m_extensions == -1) + d->m_extensions = qt_gl_resolve_extensions(); + return (d->m_extensions & int(extension)) != 0; +} + +/*! + Initializes GL function resolution for the current context. + + After calling this function, the QOpenGLFunctions object can only be + used with the current context and other contexts that share with it. + Call initializeGLFunctions() again to change the object's context + association. +*/ +void QOpenGLFunctions::initializeGLFunctions() +{ + d_ptr = qt_gl_functions(); +} + +/*! + \fn void QOpenGLFunctions::glActiveTexture(GLenum texture) + + Convenience function that calls glActiveTexture(\a texture). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glActiveTexture.xml}{glActiveTexture()}. +*/ + +/*! + \fn void QOpenGLFunctions::glAttachShader(GLuint program, GLuint shader) + + Convenience function that calls glAttachShader(\a program, \a shader). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glAttachShader.xml}{glAttachShader()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glBindAttribLocation(GLuint program, GLuint index, const char* name) + + Convenience function that calls glBindAttribLocation(\a program, \a index, \a name). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBindAttribLocation.xml}{glBindAttribLocation()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glBindBuffer(GLenum target, GLuint buffer) + + Convenience function that calls glBindBuffer(\a target, \a buffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBindBuffer.xml}{glBindBuffer()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBindFramebuffer(GLenum target, GLuint framebuffer) + + Convenience function that calls glBindFramebuffer(\a target, \a framebuffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBindFramebuffer.xml}{glBindFramebuffer()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBindRenderbuffer(GLenum target, GLuint renderbuffer) + + Convenience function that calls glBindRenderbuffer(\a target, \a renderbuffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBindRenderbuffer.xml}{glBindRenderbuffer()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) + + Convenience function that calls glBlendColor(\a red, \a green, \a blue, \a alpha). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBlendColor.xml}{glBlendColor()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBlendEquation(GLenum mode) + + Convenience function that calls glBlendEquation(\a mode). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBlendEquation.xml}{glBlendEquation()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) + + Convenience function that calls glBlendEquationSeparate(\a modeRGB, \a modeAlpha). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBlendEquationSeparate.xml}{glBlendEquationSeparate()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) + + Convenience function that calls glBlendFuncSeparate(\a srcRGB, \a dstRGB, \a srcAlpha, \a dstAlpha). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBlendFuncSeparate.xml}{glBlendFuncSeparate()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBufferData(GLenum target, qopengl_GLsizeiptr size, const void* data, GLenum usage) + + Convenience function that calls glBufferData(\a target, \a size, \a data, \a usage). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBufferData.xml}{glBufferData()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, const void* data) + + Convenience function that calls glBufferSubData(\a target, \a offset, \a size, \a data). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBufferSubData.xml}{glBufferSubData()}. +*/ + +/*! + \fn GLenum QOpenGLFunctions::glCheckFramebufferStatus(GLenum target) + + Convenience function that calls glCheckFramebufferStatus(\a target). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glCheckFramebufferStatus.xml}{glCheckFramebufferStatus()}. +*/ + +/*! + \fn void QOpenGLFunctions::glClearDepthf(GLclampf depth) + + Convenience function that calls glClearDepth(\a depth) on + desktop OpenGL systems and glClearDepthf(\a depth) on + embedded OpenGL/ES systems. + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glClearDepthf.xml}{glClearDepthf()}. +*/ + +/*! + \fn void QOpenGLFunctions::glCompileShader(GLuint shader) + + Convenience function that calls glCompileShader(\a shader). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glCompileShader.xml}{glCompileShader()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) + + Convenience function that calls glCompressedTexImage2D(\a target, \a level, \a internalformat, \a width, \a height, \a border, \a imageSize, \a data). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glCompressedTexImage2D.xml}{glCompressedTexImage2D()}. +*/ + +/*! + \fn void QOpenGLFunctions::glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) + + Convenience function that calls glCompressedTexSubImage2D(\a target, \a level, \a xoffset, \a yoffset, \a width, \a height, \a format, \a imageSize, \a data). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glCompressedTexSubImage2D.xml}{glCompressedTexSubImage2D()}. +*/ + +/*! + \fn GLuint QOpenGLFunctions::glCreateProgram() + + Convenience function that calls glCreateProgram(). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glCreateProgram.xml}{glCreateProgram()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn GLuint QOpenGLFunctions::glCreateShader(GLenum type) + + Convenience function that calls glCreateShader(\a type). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glCreateShader.xml}{glCreateShader()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glDeleteBuffers(GLsizei n, const GLuint* buffers) + + Convenience function that calls glDeleteBuffers(\a n, \a buffers). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteBuffers.xml}{glDeleteBuffers()}. +*/ + +/*! + \fn void QOpenGLFunctions::glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) + + Convenience function that calls glDeleteFramebuffers(\a n, \a framebuffers). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteFramebuffers.xml}{glDeleteFramebuffers()}. +*/ + +/*! + \fn void QOpenGLFunctions::glDeleteProgram(GLuint program) + + Convenience function that calls glDeleteProgram(\a program). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteProgram.xml}{glDeleteProgram()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) + + Convenience function that calls glDeleteRenderbuffers(\a n, \a renderbuffers). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteRenderbuffers.xml}{glDeleteRenderbuffers()}. +*/ + +/*! + \fn void QOpenGLFunctions::glDeleteShader(GLuint shader) + + Convenience function that calls glDeleteShader(\a shader). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteShader.xml}{glDeleteShader()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glDepthRangef(GLclampf zNear, GLclampf zFar) + + Convenience function that calls glDepthRange(\a zNear, \a zFar) on + desktop OpenGL systems and glDepthRangef(\a zNear, \a zFar) on + embedded OpenGL/ES systems. + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDepthRangef.xml}{glDepthRangef()}. +*/ + +/*! + \fn void QOpenGLFunctions::glDetachShader(GLuint program, GLuint shader) + + Convenience function that calls glDetachShader(\a program, \a shader). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDetachShader.xml}{glDetachShader()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glDisableVertexAttribArray(GLuint index) + + Convenience function that calls glDisableVertexAttribArray(\a index). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDisableVertexAttribArray.xml}{glDisableVertexAttribArray()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glEnableVertexAttribArray(GLuint index) + + Convenience function that calls glEnableVertexAttribArray(\a index). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glEnableVertexAttribArray.xml}{glEnableVertexAttribArray()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) + + Convenience function that calls glFramebufferRenderbuffer(\a target, \a attachment, \a renderbuffertarget, \a renderbuffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glFramebufferRenderbuffer.xml}{glFramebufferRenderbuffer()}. +*/ + +/*! + \fn void QOpenGLFunctions::glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) + + Convenience function that calls glFramebufferTexture2D(\a target, \a attachment, \a textarget, \a texture, \a level). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glFramebufferTexture2D.xml}{glFramebufferTexture2D()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGenBuffers(GLsizei n, GLuint* buffers) + + Convenience function that calls glGenBuffers(\a n, \a buffers). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGenBuffers.xml}{glGenBuffers()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGenerateMipmap(GLenum target) + + Convenience function that calls glGenerateMipmap(\a target). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGenerateMipmap.xml}{glGenerateMipmap()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGenFramebuffers(GLsizei n, GLuint* framebuffers) + + Convenience function that calls glGenFramebuffers(\a n, \a framebuffers). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGenFramebuffers.xml}{glGenFramebuffers()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGenRenderbuffers(GLsizei n, GLuint* renderbuffers) + + Convenience function that calls glGenRenderbuffers(\a n, \a renderbuffers). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGenRenderbuffers.xml}{glGenRenderbuffers()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) + + Convenience function that calls glGetActiveAttrib(\a program, \a index, \a bufsize, \a length, \a size, \a type, \a name). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetActiveAttrib.xml}{glGetActiveAttrib()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) + + Convenience function that calls glGetActiveUniform(\a program, \a index, \a bufsize, \a length, \a size, \a type, \a name). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetActiveUniform.xml}{glGetActiveUniform()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) + + Convenience function that calls glGetAttachedShaders(\a program, \a maxcount, \a count, \a shaders). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetAttachedShaders.xml}{glGetAttachedShaders()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn GLint QOpenGLFunctions::glGetAttribLocation(GLuint program, const char* name) + + Convenience function that calls glGetAttribLocation(\a program, \a name). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetAttribLocation.xml}{glGetAttribLocation()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) + + Convenience function that calls glGetBufferParameteriv(\a target, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetBufferParameteriv.xml}{glGetBufferParameteriv()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params) + + Convenience function that calls glGetFramebufferAttachmentParameteriv(\a target, \a attachment, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetFramebufferAttachmentParameteriv.xml}{glGetFramebufferAttachmentParameteriv()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGetProgramiv(GLuint program, GLenum pname, GLint* params) + + Convenience function that calls glGetProgramiv(\a program, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetProgramiv.xml}{glGetProgramiv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) + + Convenience function that calls glGetProgramInfoLog(\a program, \a bufsize, \a length, \a infolog). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetProgramInfoLog.xml}{glGetProgramInfoLog()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) + + Convenience function that calls glGetRenderbufferParameteriv(\a target, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetRenderbufferParameteriv.xml}{glGetRenderbufferParameteriv()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGetShaderiv(GLuint shader, GLenum pname, GLint* params) + + Convenience function that calls glGetShaderiv(\a shader, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetShaderiv.xml}{glGetShaderiv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) + + Convenience function that calls glGetShaderInfoLog(\a shader, \a bufsize, \a length, \a infolog). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetShaderInfoLog.xml}{glGetShaderInfoLog()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) + + Convenience function that calls glGetShaderPrecisionFormat(\a shadertype, \a precisiontype, \a range, \a precision). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetShaderPrecisionFormat.xml}{glGetShaderPrecisionFormat()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) + + Convenience function that calls glGetShaderSource(\a shader, \a bufsize, \a length, \a source). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetShaderSource.xml}{glGetShaderSource()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetUniformfv(GLuint program, GLint location, GLfloat* params) + + Convenience function that calls glGetUniformfv(\a program, \a location, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetUniformfv.xml}{glGetUniformfv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetUniformiv(GLuint program, GLint location, GLint* params) + + Convenience function that calls glGetUniformiv(\a program, \a location, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetUniformiv.xml}{glGetUniformiv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn GLint QOpenGLFunctions::glGetUniformLocation(GLuint program, const char* name) + + Convenience function that calls glGetUniformLocation(\a program, \a name). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetUniformLocation.xml}{glGetUniformLocation()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) + + Convenience function that calls glGetVertexAttribfv(\a index, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetVertexAttribfv.xml}{glGetVertexAttribfv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params) + + Convenience function that calls glGetVertexAttribiv(\a index, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetVertexAttribiv.xml}{glGetVertexAttribiv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer) + + Convenience function that calls glGetVertexAttribPointerv(\a index, \a pname, \a pointer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetVertexAttribPointerv.xml}{glGetVertexAttribPointerv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn GLboolean QOpenGLFunctions::glIsBuffer(GLuint buffer) + + Convenience function that calls glIsBuffer(\a buffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glIsBuffer.xml}{glIsBuffer()}. +*/ + +/*! + \fn GLboolean QOpenGLFunctions::glIsFramebuffer(GLuint framebuffer) + + Convenience function that calls glIsFramebuffer(\a framebuffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glIsFramebuffer.xml}{glIsFramebuffer()}. +*/ + +/*! + \fn GLboolean QOpenGLFunctions::glIsProgram(GLuint program) + + Convenience function that calls glIsProgram(\a program). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glIsProgram.xml}{glIsProgram()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn GLboolean QOpenGLFunctions::glIsRenderbuffer(GLuint renderbuffer) + + Convenience function that calls glIsRenderbuffer(\a renderbuffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glIsRenderbuffer.xml}{glIsRenderbuffer()}. +*/ + +/*! + \fn GLboolean QOpenGLFunctions::glIsShader(GLuint shader) + + Convenience function that calls glIsShader(\a shader). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glIsShader.xml}{glIsShader()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glLinkProgram(GLuint program) + + Convenience function that calls glLinkProgram(\a program). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glLinkProgram.xml}{glLinkProgram()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glReleaseShaderCompiler() + + Convenience function that calls glReleaseShaderCompiler(). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glReleaseShaderCompiler.xml}{glReleaseShaderCompiler()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) + + Convenience function that calls glRenderbufferStorage(\a target, \a internalformat, \a width, \a height). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glRenderbufferStorage.xml}{glRenderbufferStorage()}. +*/ + +/*! + \fn void QOpenGLFunctions::glSampleCoverage(GLclampf value, GLboolean invert) + + Convenience function that calls glSampleCoverage(\a value, \a invert). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glSampleCoverage.xml}{glSampleCoverage()}. +*/ + +/*! + \fn void QOpenGLFunctions::glShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length) + + Convenience function that calls glShaderBinary(\a n, \a shaders, \a binaryformat, \a binary, \a length). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glShaderBinary.xml}{glShaderBinary()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length) + + Convenience function that calls glShaderSource(\a shader, \a count, \a string, \a length). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glShaderSource.xml}{glShaderSource()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) + + Convenience function that calls glStencilFuncSeparate(\a face, \a func, \a ref, \a mask). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glStencilFuncSeparate.xml}{glStencilFuncSeparate()}. +*/ + +/*! + \fn void QOpenGLFunctions::glStencilMaskSeparate(GLenum face, GLuint mask) + + Convenience function that calls glStencilMaskSeparate(\a face, \a mask). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glStencilMaskSeparate.xml}{glStencilMaskSeparate()}. +*/ + +/*! + \fn void QOpenGLFunctions::glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) + + Convenience function that calls glStencilOpSeparate(\a face, \a fail, \a zfail, \a zpass). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glStencilOpSeparate.xml}{glStencilOpSeparate()}. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform1f(GLint location, GLfloat x) + + Convenience function that calls glUniform1f(\a location, \a x). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform1f.xml}{glUniform1f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform1fv(GLint location, GLsizei count, const GLfloat* v) + + Convenience function that calls glUniform1fv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform1fv.xml}{glUniform1fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform1i(GLint location, GLint x) + + Convenience function that calls glUniform1i(\a location, \a x). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform1i.xml}{glUniform1i()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform1iv(GLint location, GLsizei count, const GLint* v) + + Convenience function that calls glUniform1iv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform1iv.xml}{glUniform1iv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform2f(GLint location, GLfloat x, GLfloat y) + + Convenience function that calls glUniform2f(\a location, \a x, \a y). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform2f.xml}{glUniform2f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform2fv(GLint location, GLsizei count, const GLfloat* v) + + Convenience function that calls glUniform2fv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform2fv.xml}{glUniform2fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform2i(GLint location, GLint x, GLint y) + + Convenience function that calls glUniform2i(\a location, \a x, \a y). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform2i.xml}{glUniform2i()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform2iv(GLint location, GLsizei count, const GLint* v) + + Convenience function that calls glUniform2iv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform2iv.xml}{glUniform2iv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) + + Convenience function that calls glUniform3f(\a location, \a x, \a y, \a z). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform3f.xml}{glUniform3f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform3fv(GLint location, GLsizei count, const GLfloat* v) + + Convenience function that calls glUniform3fv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform3fv.xml}{glUniform3fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform3i(GLint location, GLint x, GLint y, GLint z) + + Convenience function that calls glUniform3i(\a location, \a x, \a y, \a z). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform3i.xml}{glUniform3i()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform3iv(GLint location, GLsizei count, const GLint* v) + + Convenience function that calls glUniform3iv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform3iv.xml}{glUniform3iv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) + + Convenience function that calls glUniform4f(\a location, \a x, \a y, \a z, \a w). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform4f.xml}{glUniform4f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform4fv(GLint location, GLsizei count, const GLfloat* v) + + Convenience function that calls glUniform4fv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform4fv.xml}{glUniform4fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) + + Convenience function that calls glUniform4i(\a location, \a x, \a y, \a z, \a w). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform4i.xml}{glUniform4i()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform4iv(GLint location, GLsizei count, const GLint* v) + + Convenience function that calls glUniform4iv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform4iv.xml}{glUniform4iv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) + + Convenience function that calls glUniformMatrix2fv(\a location, \a count, \a transpose, \a value). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniformMatrix2fv.xml}{glUniformMatrix2fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) + + Convenience function that calls glUniformMatrix3fv(\a location, \a count, \a transpose, \a value). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniformMatrix3fv.xml}{glUniformMatrix3fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) + + Convenience function that calls glUniformMatrix4fv(\a location, \a count, \a transpose, \a value). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniformMatrix4fv.xml}{glUniformMatrix4fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUseProgram(GLuint program) + + Convenience function that calls glUseProgram(\a program). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUseProgram.xml}{glUseProgram()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glValidateProgram(GLuint program) + + Convenience function that calls glValidateProgram(\a program). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glValidateProgram.xml}{glValidateProgram()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib1f(GLuint indx, GLfloat x) + + Convenience function that calls glVertexAttrib1f(\a indx, \a x). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib1f.xml}{glVertexAttrib1f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib1fv(GLuint indx, const GLfloat* values) + + Convenience function that calls glVertexAttrib1fv(\a indx, \a values). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib1fv.xml}{glVertexAttrib1fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) + + Convenience function that calls glVertexAttrib2f(\a indx, \a x, \a y). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib2f.xml}{glVertexAttrib2f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib2fv(GLuint indx, const GLfloat* values) + + Convenience function that calls glVertexAttrib2fv(\a indx, \a values). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib2fv.xml}{glVertexAttrib2fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) + + Convenience function that calls glVertexAttrib3f(\a indx, \a x, \a y, \a z). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib3f.xml}{glVertexAttrib3f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib3fv(GLuint indx, const GLfloat* values) + + Convenience function that calls glVertexAttrib3fv(\a indx, \a values). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib3fv.xml}{glVertexAttrib3fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) + + Convenience function that calls glVertexAttrib4f(\a indx, \a x, \a y, \a z, \a w). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib4f.xml}{glVertexAttrib4f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib4fv(GLuint indx, const GLfloat* values) + + Convenience function that calls glVertexAttrib4fv(\a indx, \a values). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib4fv.xml}{glVertexAttrib4fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) + + Convenience function that calls glVertexAttribPointer(\a indx, \a size, \a type, \a normalized, \a stride, \a ptr). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttribPointer.xml}{glVertexAttribPointer()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +namespace { + +enum ResolvePolicy +{ + ResolveOES = 0x1, + ResolveEXT = 0x2 +}; + +template <typename Base, typename FuncType, int Policy, typename ReturnType> +class Resolver +{ +public: + Resolver(FuncType Base::*func, FuncType fallback, const char *name, const char *alternateName = 0) + : funcPointerName(func) + , fallbackFuncPointer(fallback) + , funcName(name) + , alternateFuncName(alternateName) + { + } + + ReturnType operator()(); + + template <typename P1> + ReturnType operator()(P1 p1); + + template <typename P1, typename P2> + ReturnType operator()(P1 p1, P2 p2); + + template <typename P1, typename P2, typename P3> + ReturnType operator()(P1 p1, P2 p2, P3 p3); + + template <typename P1, typename P2, typename P3, typename P4> + ReturnType operator()(P1 p1, P2 p2, P3 p3, P4 p4); + + template <typename P1, typename P2, typename P3, typename P4, typename P5> + ReturnType operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5); + + template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6> + ReturnType operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6); + + template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7> + ReturnType operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7); + + template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8> + ReturnType operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8); + + template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9> + ReturnType operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9); + + template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9, typename P10> + ReturnType operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10); + +private: + FuncType Base::*funcPointerName; + FuncType fallbackFuncPointer; + QByteArray funcName; + QByteArray alternateFuncName; +}; + +template <typename Base, typename FuncType, int Policy> +class Resolver<Base, FuncType, Policy, void> +{ +public: + Resolver(FuncType Base::*func, FuncType fallback, const char *name, const char *alternateName = 0) + : funcPointerName(func) + , fallbackFuncPointer(fallback) + , funcName(name) + , alternateFuncName(alternateName) + { + } + + void operator()(); + + template <typename P1> + void operator()(P1 p1); + + template <typename P1, typename P2> + void operator()(P1 p1, P2 p2); + + template <typename P1, typename P2, typename P3> + void operator()(P1 p1, P2 p2, P3 p3); + + template <typename P1, typename P2, typename P3, typename P4> + void operator()(P1 p1, P2 p2, P3 p3, P4 p4); + + template <typename P1, typename P2, typename P3, typename P4, typename P5> + void operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5); + + template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6> + void operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6); + + template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7> + void operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7); + + template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8> + void operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8); + + template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9> + void operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9); + + template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9, typename P10> + void operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10); + +private: + FuncType Base::*funcPointerName; + FuncType fallbackFuncPointer; + QByteArray funcName; + QByteArray alternateFuncName; +}; + +#define RESOLVER_COMMON \ + QOpenGLContext *context = QOpenGLContext::currentContext(); \ + Base *funcs = qt_gl_functions(context); \ + \ + FuncType old = funcs->*funcPointerName; \ + \ + funcs->*funcPointerName = (FuncType)context->getProcAddress(funcName); \ + \ + if ((Policy & ResolveOES) && !(funcs->*funcPointerName)) \ + funcs->*funcPointerName = (FuncType)context->getProcAddress(funcName + "OES"); \ + \ + if (!(funcs->*funcPointerName)) \ + funcs->*funcPointerName = (FuncType)context->getProcAddress(funcName + "ARB"); \ + \ + if ((Policy & ResolveEXT) && !(funcs->*funcPointerName)) \ + funcs->*funcPointerName = (FuncType)context->getProcAddress(funcName + "EXT"); \ + \ + if (!alternateFuncName.isEmpty() && !(funcs->*funcPointerName)) { \ + funcs->*funcPointerName = (FuncType)context->getProcAddress(alternateFuncName); \ + \ + if ((Policy & ResolveOES) && !(funcs->*funcPointerName)) \ + funcs->*funcPointerName = (FuncType)context->getProcAddress(alternateFuncName + "OES"); \ + \ + if (!(funcs->*funcPointerName)) \ + funcs->*funcPointerName = (FuncType)context->getProcAddress(alternateFuncName + "ARB"); \ + \ + if ((Policy & ResolveEXT) && !(funcs->*funcPointerName)) \ + funcs->*funcPointerName = (FuncType)context->getProcAddress(alternateFuncName + "EXT"); \ + } + +#define RESOLVER_COMMON_NON_VOID \ + RESOLVER_COMMON \ + \ + if (!(funcs->*funcPointerName)) { \ + if (fallbackFuncPointer) { \ + funcs->*funcPointerName = fallbackFuncPointer; \ + } else { \ + funcs->*funcPointerName = old; \ + return ReturnType(); \ + } \ + } + +#define RESOLVER_COMMON_VOID \ + RESOLVER_COMMON \ + \ + if (!(funcs->*funcPointerName)) { \ + if (fallbackFuncPointer) { \ + funcs->*funcPointerName = fallbackFuncPointer; \ + } else { \ + funcs->*funcPointerName = old; \ + return; \ + } \ + } + +template <typename Base, typename FuncType, int Policy, typename ReturnType> +ReturnType Resolver<Base, FuncType, Policy, ReturnType>::operator()() +{ + RESOLVER_COMMON_NON_VOID + + return (funcs->*funcPointerName)(); +} + +template <typename Base, typename FuncType, int Policy, typename ReturnType> template <typename P1> +ReturnType Resolver<Base, FuncType, Policy, ReturnType>::operator()(P1 p1) +{ + RESOLVER_COMMON_NON_VOID + + return (funcs->*funcPointerName)(p1); +} + +template <typename Base, typename FuncType, int Policy, typename ReturnType> template <typename P1, typename P2> +ReturnType Resolver<Base, FuncType, Policy, ReturnType>::operator()(P1 p1, P2 p2) +{ + RESOLVER_COMMON_NON_VOID + + return (funcs->*funcPointerName)(p1, p2); +} + +template <typename Base, typename FuncType, int Policy, typename ReturnType> template <typename P1, typename P2, typename P3> +ReturnType Resolver<Base, FuncType, Policy, ReturnType>::operator()(P1 p1, P2 p2, P3 p3) +{ + RESOLVER_COMMON_NON_VOID + + return (funcs->*funcPointerName)(p1, p2, p3); +} + +template <typename Base, typename FuncType, int Policy, typename ReturnType> template <typename P1, typename P2, typename P3, typename P4> +ReturnType Resolver<Base, FuncType, Policy, ReturnType>::operator()(P1 p1, P2 p2, P3 p3, P4 p4) +{ + RESOLVER_COMMON_NON_VOID + + return (funcs->*funcPointerName)(p1, p2, p3, p4); +} + +template <typename Base, typename FuncType, int Policy, typename ReturnType> template <typename P1, typename P2, typename P3, typename P4, typename P5> +ReturnType Resolver<Base, FuncType, Policy, ReturnType>::operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) +{ + RESOLVER_COMMON_NON_VOID + + return (funcs->*funcPointerName)(p1, p2, p3, p4, p5); +} + +template <typename Base, typename FuncType, int Policy, typename ReturnType> template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6> +ReturnType Resolver<Base, FuncType, Policy, ReturnType>::operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) +{ + RESOLVER_COMMON_NON_VOID + + return (funcs->*funcPointerName)(p1, p2, p3, p4, p5, p6); +} + +template <typename Base, typename FuncType, int Policy, typename ReturnType> template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7> +ReturnType Resolver<Base, FuncType, Policy, ReturnType>::operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) +{ + RESOLVER_COMMON_NON_VOID + + return (funcs->*funcPointerName)(p1, p2, p3, p4, p5, p6, p7); +} + +template <typename Base, typename FuncType, int Policy, typename ReturnType> template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8> +ReturnType Resolver<Base, FuncType, Policy, ReturnType>::operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) +{ + RESOLVER_COMMON_NON_VOID + + return (funcs->*funcPointerName)(p1, p2, p3, p4, p5, p6, p7, p8); +} + +template <typename Base, typename FuncType, int Policy, typename ReturnType> template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9> +ReturnType Resolver<Base, FuncType, Policy, ReturnType>::operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9) +{ + RESOLVER_COMMON_NON_VOID + + return (funcs->*funcPointerName)(p1, p2, p3, p4, p5, p6, p7, p8, p9); +} + +template <typename Base, typename FuncType, int Policy, typename ReturnType> template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9, typename P10> +ReturnType Resolver<Base, FuncType, Policy, ReturnType>::operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10) +{ + RESOLVER_COMMON_NON_VOID + + return (funcs->*funcPointerName)(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); +} + +template <typename Base, typename FuncType, int Policy> +void Resolver<Base, FuncType, Policy, void>::operator()() +{ + RESOLVER_COMMON_VOID + + (funcs->*funcPointerName)(); +} + +template <typename Base, typename FuncType, int Policy> template <typename P1> +void Resolver<Base, FuncType, Policy, void>::operator()(P1 p1) +{ + RESOLVER_COMMON_VOID + + (funcs->*funcPointerName)(p1); +} + +template <typename Base, typename FuncType, int Policy> template <typename P1, typename P2> +void Resolver<Base, FuncType, Policy, void>::operator()(P1 p1, P2 p2) +{ + RESOLVER_COMMON_VOID + + (funcs->*funcPointerName)(p1, p2); +} + +template <typename Base, typename FuncType, int Policy> template <typename P1, typename P2, typename P3> +void Resolver<Base, FuncType, Policy, void>::operator()(P1 p1, P2 p2, P3 p3) +{ + RESOLVER_COMMON_VOID + + (funcs->*funcPointerName)(p1, p2, p3); +} + +template <typename Base, typename FuncType, int Policy> template <typename P1, typename P2, typename P3, typename P4> +void Resolver<Base, FuncType, Policy, void>::operator()(P1 p1, P2 p2, P3 p3, P4 p4) +{ + RESOLVER_COMMON_VOID + + (funcs->*funcPointerName)(p1, p2, p3, p4); +} + +template <typename Base, typename FuncType, int Policy> template <typename P1, typename P2, typename P3, typename P4, typename P5> +void Resolver<Base, FuncType, Policy, void>::operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) +{ + RESOLVER_COMMON_VOID + + (funcs->*funcPointerName)(p1, p2, p3, p4, p5); +} + +template <typename Base, typename FuncType, int Policy> template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6> +void Resolver<Base, FuncType, Policy, void>::operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) +{ + RESOLVER_COMMON_VOID + + (funcs->*funcPointerName)(p1, p2, p3, p4, p5, p6); +} + +template <typename Base, typename FuncType, int Policy> template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7> +void Resolver<Base, FuncType, Policy, void>::operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) +{ + RESOLVER_COMMON_VOID + + (funcs->*funcPointerName)(p1, p2, p3, p4, p5, p6, p7); +} + +template <typename Base, typename FuncType, int Policy> template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8> +void Resolver<Base, FuncType, Policy, void>::operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) +{ + RESOLVER_COMMON_VOID + + (funcs->*funcPointerName)(p1, p2, p3, p4, p5, p6, p7, p8); +} + +template <typename Base, typename FuncType, int Policy> template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9> +void Resolver<Base, FuncType, Policy, void>::operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9) +{ + RESOLVER_COMMON_VOID + + (funcs->*funcPointerName)(p1, p2, p3, p4, p5, p6, p7, p8, p9); +} + +template <typename Base, typename FuncType, int Policy> template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9, typename P10> +void Resolver<Base, FuncType, Policy, void>::operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10) +{ + RESOLVER_COMMON_VOID + + (funcs->*funcPointerName)(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); +} + +template <typename ReturnType, int Policy, typename Base, typename FuncType> +Resolver<Base, FuncType, Policy, ReturnType> functionResolverWithFallback(FuncType Base::*func, FuncType fallback, const char *name, const char *alternate = 0) +{ + return Resolver<Base, FuncType, Policy, ReturnType>(func, fallback, name, alternate); +} + +template <typename ReturnType, int Policy, typename Base, typename FuncType> +Resolver<Base, FuncType, Policy, ReturnType> functionResolver(FuncType Base::*func, const char *name, const char *alternate = 0) +{ + return Resolver<Base, FuncType, Policy, ReturnType>(func, 0, name, alternate); +} + +} + +#define RESOLVE_FUNC(RETURN_TYPE, POLICY, NAME) \ + return functionResolver<RETURN_TYPE, POLICY>(&QOpenGLExtensionsPrivate::NAME, "gl" #NAME) + +#define RESOLVE_FUNC_VOID(POLICY, NAME) \ + functionResolver<void, POLICY>(&QOpenGLExtensionsPrivate::NAME, "gl" #NAME) + +#define RESOLVE_FUNC_SPECIAL(RETURN_TYPE, POLICY, NAME) \ + return functionResolverWithFallback<RETURN_TYPE, POLICY>(&QOpenGLExtensionsPrivate::NAME, qopenglfSpecial##NAME, "gl" #NAME) + +#define RESOLVE_FUNC_SPECIAL_VOID(POLICY, NAME) \ + functionResolverWithFallback<void, POLICY>(&QOpenGLExtensionsPrivate::NAME, qopenglfSpecial##NAME, "gl" #NAME) + +#define RESOLVE_FUNC_WITH_ALTERNATE(RETURN_TYPE, POLICY, NAME, ALTERNATE) \ + return functionResolver<RETURN_TYPE, POLICY>(&QOpenGLExtensionsPrivate::NAME, "gl" #NAME, "gl" #ALTERNATE) + +#define RESOLVE_FUNC_VOID_WITH_ALTERNATE(POLICY, NAME, ALTERNATE) \ + functionResolver<void, POLICY>(&QOpenGLExtensionsPrivate::NAME, "gl" #NAME, "gl" #ALTERNATE) + +#ifndef QT_OPENGL_ES_2 + +static void QOPENGLF_APIENTRY qopenglfResolveActiveTexture(GLenum texture) +{ + RESOLVE_FUNC_VOID(0, ActiveTexture)(texture); +} + +static void QOPENGLF_APIENTRY qopenglfResolveAttachShader(GLuint program, GLuint shader) +{ + RESOLVE_FUNC_VOID_WITH_ALTERNATE(0, AttachShader, AttachObject)(program, shader); +} + +static void QOPENGLF_APIENTRY qopenglfResolveBindAttribLocation(GLuint program, GLuint index, const char* name) +{ + RESOLVE_FUNC_VOID(0, BindAttribLocation)(program, index, name); +} + +static void QOPENGLF_APIENTRY qopenglfResolveBindBuffer(GLenum target, GLuint buffer) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, BindBuffer)(target, buffer); +} + +static void QOPENGLF_APIENTRY qopenglfResolveBindFramebuffer(GLenum target, GLuint framebuffer) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, BindFramebuffer)(target, framebuffer); +} + +static void QOPENGLF_APIENTRY qopenglfResolveBindRenderbuffer(GLenum target, GLuint renderbuffer) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, BindRenderbuffer)(target, renderbuffer); +} + +static void QOPENGLF_APIENTRY qopenglfResolveBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, BlendColor)(red, green, blue, alpha); +} + +static void QOPENGLF_APIENTRY qopenglfResolveBlendEquation(GLenum mode) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, BlendEquation)(mode); +} + +static void QOPENGLF_APIENTRY qopenglfResolveBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, BlendEquationSeparate)(modeRGB, modeAlpha); +} + +static void QOPENGLF_APIENTRY qopenglfResolveBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, BlendFuncSeparate)(srcRGB, dstRGB, srcAlpha, dstAlpha); +} + +static void QOPENGLF_APIENTRY qopenglfResolveBufferData(GLenum target, qopengl_GLsizeiptr size, const void* data, GLenum usage) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, BufferData)(target, size, data, usage); +} + +static void QOPENGLF_APIENTRY qopenglfResolveBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, const void* data) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, BufferSubData)(target, offset, size, data); +} + +static GLenum QOPENGLF_APIENTRY qopenglfResolveCheckFramebufferStatus(GLenum target) +{ + RESOLVE_FUNC(GLenum, ResolveOES | ResolveEXT, CheckFramebufferStatus)(target); +} + +static void QOPENGLF_APIENTRY qopenglfResolveCompileShader(GLuint shader) +{ + RESOLVE_FUNC_VOID(0, CompileShader)(shader); +} + +static void QOPENGLF_APIENTRY qopenglfResolveCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, CompressedTexImage2D)(target, level, internalformat, width, height, border, imageSize, data); +} + +static void QOPENGLF_APIENTRY qopenglfResolveCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, CompressedTexSubImage2D)(target, level, xoffset, yoffset, width, height, format, imageSize, data); +} + +static GLuint QOPENGLF_APIENTRY qopenglfResolveCreateProgram() +{ + RESOLVE_FUNC_WITH_ALTERNATE(GLuint, 0, CreateProgram, CreateProgramObject)(); +} + +static GLuint QOPENGLF_APIENTRY qopenglfResolveCreateShader(GLenum type) +{ + RESOLVE_FUNC_WITH_ALTERNATE(GLuint, 0, CreateShader, CreateShaderObject)(type); +} + +static void QOPENGLF_APIENTRY qopenglfResolveDeleteBuffers(GLsizei n, const GLuint* buffers) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, DeleteBuffers)(n, buffers); +} + +static void QOPENGLF_APIENTRY qopenglfResolveDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, DeleteFramebuffers)(n, framebuffers); +} + +static void QOPENGLF_APIENTRY qopenglfResolveDeleteProgram(GLuint program) +{ + RESOLVE_FUNC_VOID(0, DeleteProgram)(program); +} + +static void QOPENGLF_APIENTRY qopenglfResolveDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, DeleteRenderbuffers)(n, renderbuffers); +} + +static void QOPENGLF_APIENTRY qopenglfResolveDeleteShader(GLuint shader) +{ + RESOLVE_FUNC_VOID_WITH_ALTERNATE(0, DeleteShader, DeleteObject)(shader); +} + +static void QOPENGLF_APIENTRY qopenglfResolveDetachShader(GLuint program, GLuint shader) +{ + RESOLVE_FUNC_VOID_WITH_ALTERNATE(0, DetachShader, DetachObject)(program, shader); +} + +static void QOPENGLF_APIENTRY qopenglfResolveDisableVertexAttribArray(GLuint index) +{ + RESOLVE_FUNC_VOID(0, DisableVertexAttribArray)(index); +} + +static void QOPENGLF_APIENTRY qopenglfResolveEnableVertexAttribArray(GLuint index) +{ + RESOLVE_FUNC_VOID(0, EnableVertexAttribArray)(index); +} + +static void QOPENGLF_APIENTRY qopenglfResolveFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, FramebufferRenderbuffer)(target, attachment, renderbuffertarget, renderbuffer); +} + +static void QOPENGLF_APIENTRY qopenglfResolveFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, FramebufferTexture2D)(target, attachment, textarget, texture, level); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGenBuffers(GLsizei n, GLuint* buffers) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, GenBuffers)(n, buffers); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGenerateMipmap(GLenum target) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, GenerateMipmap)(target); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGenFramebuffers(GLsizei n, GLuint* framebuffers) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, GenFramebuffers)(n, framebuffers); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGenRenderbuffers(GLsizei n, GLuint* renderbuffers) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, GenRenderbuffers)(n, renderbuffers); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) +{ + RESOLVE_FUNC_VOID(0, GetActiveAttrib)(program, index, bufsize, length, size, type, name); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) +{ + RESOLVE_FUNC_VOID(0, GetActiveUniform)(program, index, bufsize, length, size, type, name); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) +{ + RESOLVE_FUNC_VOID_WITH_ALTERNATE(0, GetAttachedShaders, GetAttachedObjects)(program, maxcount, count, shaders); +} + +static GLint QOPENGLF_APIENTRY qopenglfResolveGetAttribLocation(GLuint program, const char* name) +{ + RESOLVE_FUNC(GLint, 0, GetAttribLocation)(program, name); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, GetBufferParameteriv)(target, pname, params); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, GetFramebufferAttachmentParameteriv)(target, attachment, pname, params); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetProgramiv(GLuint program, GLenum pname, GLint* params) +{ + RESOLVE_FUNC_VOID_WITH_ALTERNATE(0, GetProgramiv, GetObjectParameteriv)(program, pname, params); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) +{ + RESOLVE_FUNC_VOID_WITH_ALTERNATE(0, GetProgramInfoLog, GetInfoLog)(program, bufsize, length, infolog); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, GetRenderbufferParameteriv)(target, pname, params); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetShaderiv(GLuint shader, GLenum pname, GLint* params) +{ + RESOLVE_FUNC_VOID_WITH_ALTERNATE(0, GetShaderiv, GetObjectParameteriv)(shader, pname, params); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) +{ + RESOLVE_FUNC_VOID_WITH_ALTERNATE(0, GetShaderInfoLog, GetInfoLog)(shader, bufsize, length, infolog); +} + +static void QOPENGLF_APIENTRY qopenglfSpecialGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) +{ + Q_UNUSED(shadertype); + Q_UNUSED(precisiontype); + range[0] = range[1] = precision[0] = 0; +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) +{ + RESOLVE_FUNC_SPECIAL_VOID(ResolveOES | ResolveEXT, GetShaderPrecisionFormat)(shadertype, precisiontype, range, precision); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) +{ + RESOLVE_FUNC_VOID(0, GetShaderSource)(shader, bufsize, length, source); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetUniformfv(GLuint program, GLint location, GLfloat* params) +{ + RESOLVE_FUNC_VOID(0, GetUniformfv)(program, location, params); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetUniformiv(GLuint program, GLint location, GLint* params) +{ + RESOLVE_FUNC_VOID(0, GetUniformiv)(program, location, params); +} + +static GLint QOPENGLF_APIENTRY qopenglfResolveGetUniformLocation(GLuint program, const char* name) +{ + RESOLVE_FUNC(GLint, 0, GetUniformLocation)(program, name); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) +{ + RESOLVE_FUNC_VOID(0, GetVertexAttribfv)(index, pname, params); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetVertexAttribiv(GLuint index, GLenum pname, GLint* params) +{ + RESOLVE_FUNC_VOID(0, GetVertexAttribiv)(index, pname, params); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer) +{ + RESOLVE_FUNC_VOID(0, GetVertexAttribPointerv)(index, pname, pointer); +} + +static GLboolean QOPENGLF_APIENTRY qopenglfResolveIsBuffer(GLuint buffer) +{ + RESOLVE_FUNC(GLboolean, ResolveOES | ResolveEXT, IsBuffer)(buffer); +} + +static GLboolean QOPENGLF_APIENTRY qopenglfResolveIsFramebuffer(GLuint framebuffer) +{ + RESOLVE_FUNC(GLboolean, ResolveOES | ResolveEXT, IsFramebuffer)(framebuffer); +} + +static GLboolean QOPENGLF_APIENTRY qopenglfSpecialIsProgram(GLuint program) +{ + return program != 0; +} + +static GLboolean QOPENGLF_APIENTRY qopenglfResolveIsProgram(GLuint program) +{ + RESOLVE_FUNC_SPECIAL(GLboolean, 0, IsProgram)(program); +} + +static GLboolean QOPENGLF_APIENTRY qopenglfResolveIsRenderbuffer(GLuint renderbuffer) +{ + RESOLVE_FUNC(GLboolean, ResolveOES | ResolveEXT, IsRenderbuffer)(renderbuffer); +} + +static GLboolean QOPENGLF_APIENTRY qopenglfSpecialIsShader(GLuint shader) +{ + return shader != 0; +} + +static GLboolean QOPENGLF_APIENTRY qopenglfResolveIsShader(GLuint shader) +{ + RESOLVE_FUNC_SPECIAL(GLboolean, 0, IsShader)(shader); +} + +static void QOPENGLF_APIENTRY qopenglfResolveLinkProgram(GLuint program) +{ + RESOLVE_FUNC_VOID(0, LinkProgram)(program); +} + +static void QOPENGLF_APIENTRY qopenglfSpecialReleaseShaderCompiler() +{ +} + +static void QOPENGLF_APIENTRY qopenglfResolveReleaseShaderCompiler() +{ + RESOLVE_FUNC_SPECIAL_VOID(0, ReleaseShaderCompiler)(); +} + +static void QOPENGLF_APIENTRY qopenglfResolveRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, RenderbufferStorage)(target, internalformat, width, height); +} + +static void QOPENGLF_APIENTRY qopenglfResolveSampleCoverage(GLclampf value, GLboolean invert) +{ + RESOLVE_FUNC_VOID(ResolveOES | ResolveEXT, SampleCoverage)(value, invert); +} + +static void QOPENGLF_APIENTRY qopenglfResolveShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length) +{ + RESOLVE_FUNC_VOID(0, ShaderBinary)(n, shaders, binaryformat, binary, length); +} + +static void QOPENGLF_APIENTRY qopenglfResolveShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length) +{ + RESOLVE_FUNC_VOID(0, ShaderSource)(shader, count, string, length); +} + +static void QOPENGLF_APIENTRY qopenglfResolveStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) +{ + RESOLVE_FUNC_VOID(ResolveEXT, StencilFuncSeparate)(face, func, ref, mask); +} + +static void QOPENGLF_APIENTRY qopenglfResolveStencilMaskSeparate(GLenum face, GLuint mask) +{ + RESOLVE_FUNC_VOID(ResolveEXT, StencilMaskSeparate)(face, mask); +} + +static void QOPENGLF_APIENTRY qopenglfResolveStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) +{ + RESOLVE_FUNC_VOID(ResolveEXT, StencilOpSeparate)(face, fail, zfail, zpass); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform1f(GLint location, GLfloat x) +{ + RESOLVE_FUNC_VOID(0, Uniform1f)(location, x); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform1fv(GLint location, GLsizei count, const GLfloat* v) +{ + RESOLVE_FUNC_VOID(0, Uniform1fv)(location, count, v); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform1i(GLint location, GLint x) +{ + RESOLVE_FUNC_VOID(0, Uniform1i)(location, x); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform1iv(GLint location, GLsizei count, const GLint* v) +{ + RESOLVE_FUNC_VOID(0, Uniform1iv)(location, count, v); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform2f(GLint location, GLfloat x, GLfloat y) +{ + RESOLVE_FUNC_VOID(0, Uniform2f)(location, x, y); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform2fv(GLint location, GLsizei count, const GLfloat* v) +{ + RESOLVE_FUNC_VOID(0, Uniform2fv)(location, count, v); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform2i(GLint location, GLint x, GLint y) +{ + RESOLVE_FUNC_VOID(0, Uniform2i)(location, x, y); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform2iv(GLint location, GLsizei count, const GLint* v) +{ + RESOLVE_FUNC_VOID(0, Uniform2iv)(location, count, v); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) +{ + RESOLVE_FUNC_VOID(0, Uniform3f)(location, x, y, z); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform3fv(GLint location, GLsizei count, const GLfloat* v) +{ + RESOLVE_FUNC_VOID(0, Uniform3fv)(location, count, v); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform3i(GLint location, GLint x, GLint y, GLint z) +{ + RESOLVE_FUNC_VOID(0, Uniform3i)(location, x, y, z); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform3iv(GLint location, GLsizei count, const GLint* v) +{ + RESOLVE_FUNC_VOID(0, Uniform3iv)(location, count, v); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + RESOLVE_FUNC_VOID(0, Uniform4f)(location, x, y, z, w); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform4fv(GLint location, GLsizei count, const GLfloat* v) +{ + RESOLVE_FUNC_VOID(0, Uniform4fv)(location, count, v); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) +{ + RESOLVE_FUNC_VOID(0, Uniform4i)(location, x, y, z, w); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniform4iv(GLint location, GLsizei count, const GLint* v) +{ + RESOLVE_FUNC_VOID(0, Uniform4iv)(location, count, v); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ + RESOLVE_FUNC_VOID(0, UniformMatrix2fv)(location, count, transpose, value); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ + RESOLVE_FUNC_VOID(0, UniformMatrix3fv)(location, count, transpose, value); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ + RESOLVE_FUNC_VOID(0, UniformMatrix4fv)(location, count, transpose, value); +} + +static void QOPENGLF_APIENTRY qopenglfResolveUseProgram(GLuint program) +{ + RESOLVE_FUNC_VOID(0, UseProgram)(program); +} + +static void QOPENGLF_APIENTRY qopenglfResolveValidateProgram(GLuint program) +{ + RESOLVE_FUNC_VOID(0, ValidateProgram)(program); +} + +static void QOPENGLF_APIENTRY qopenglfResolveVertexAttrib1f(GLuint indx, GLfloat x) +{ + RESOLVE_FUNC_VOID(0, VertexAttrib1f)(indx, x); +} + +static void QOPENGLF_APIENTRY qopenglfResolveVertexAttrib1fv(GLuint indx, const GLfloat* values) +{ + RESOLVE_FUNC_VOID(0, VertexAttrib1fv)(indx, values); +} + +static void QOPENGLF_APIENTRY qopenglfResolveVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) +{ + RESOLVE_FUNC_VOID(0, VertexAttrib2f)(indx, x, y); +} + +static void QOPENGLF_APIENTRY qopenglfResolveVertexAttrib2fv(GLuint indx, const GLfloat* values) +{ + RESOLVE_FUNC_VOID(0, VertexAttrib2fv)(indx, values); +} + +static void QOPENGLF_APIENTRY qopenglfResolveVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) +{ + RESOLVE_FUNC_VOID(0, VertexAttrib3f)(indx, x, y, z); +} + +static void QOPENGLF_APIENTRY qopenglfResolveVertexAttrib3fv(GLuint indx, const GLfloat* values) +{ + RESOLVE_FUNC_VOID(0, VertexAttrib3fv)(indx, values); +} + +static void QOPENGLF_APIENTRY qopenglfResolveVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + RESOLVE_FUNC_VOID(0, VertexAttrib4f)(indx, x, y, z, w); +} + +static void QOPENGLF_APIENTRY qopenglfResolveVertexAttrib4fv(GLuint indx, const GLfloat* values) +{ + RESOLVE_FUNC_VOID(0, VertexAttrib4fv)(indx, values); +} + +static void QOPENGLF_APIENTRY qopenglfResolveVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) +{ + RESOLVE_FUNC_VOID(0, VertexAttribPointer)(indx, size, type, normalized, stride, ptr); +} + +#endif // !QT_OPENGL_ES_2 + +static GLvoid *QOPENGLF_APIENTRY qopenglfResolveMapBuffer(GLenum target, GLenum access) +{ + RESOLVE_FUNC(GLvoid *, ResolveOES, MapBuffer)(target, access); +} + +static GLboolean QOPENGLF_APIENTRY qopenglfResolveUnmapBuffer(GLenum target) +{ + RESOLVE_FUNC(GLboolean, ResolveOES, UnmapBuffer)(target); +} + +static void QOPENGLF_APIENTRY qopenglfResolveBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) +{ + RESOLVE_FUNC_VOID(ResolveEXT, BlitFramebuffer) + (srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); +} + +static void QOPENGLF_APIENTRY qopenglfResolveRenderbufferStorageMultisample(GLenum target, GLsizei samples, + GLenum internalFormat, + GLsizei width, GLsizei height) +{ + RESOLVE_FUNC_VOID(ResolveEXT, RenderbufferStorageMultisample) + (target, samples, internalFormat, width, height); +} + +static void QOPENGLF_APIENTRY qopenglfResolveGetBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, GLvoid *data) +{ + RESOLVE_FUNC_VOID(ResolveEXT, GetBufferSubData) + (target, offset, size, data); +} + +QOpenGLFunctionsPrivate::QOpenGLFunctionsPrivate(QOpenGLContext *) +{ +#ifndef QT_OPENGL_ES_2 + ActiveTexture = qopenglfResolveActiveTexture; + AttachShader = qopenglfResolveAttachShader; + BindAttribLocation = qopenglfResolveBindAttribLocation; + BindBuffer = qopenglfResolveBindBuffer; + BindFramebuffer = qopenglfResolveBindFramebuffer; + BindRenderbuffer = qopenglfResolveBindRenderbuffer; + BlendColor = qopenglfResolveBlendColor; + BlendEquation = qopenglfResolveBlendEquation; + BlendEquationSeparate = qopenglfResolveBlendEquationSeparate; + BlendFuncSeparate = qopenglfResolveBlendFuncSeparate; + BufferData = qopenglfResolveBufferData; + BufferSubData = qopenglfResolveBufferSubData; + CheckFramebufferStatus = qopenglfResolveCheckFramebufferStatus; + CompileShader = qopenglfResolveCompileShader; + CompressedTexImage2D = qopenglfResolveCompressedTexImage2D; + CompressedTexSubImage2D = qopenglfResolveCompressedTexSubImage2D; + CreateProgram = qopenglfResolveCreateProgram; + CreateShader = qopenglfResolveCreateShader; + DeleteBuffers = qopenglfResolveDeleteBuffers; + DeleteFramebuffers = qopenglfResolveDeleteFramebuffers; + DeleteProgram = qopenglfResolveDeleteProgram; + DeleteRenderbuffers = qopenglfResolveDeleteRenderbuffers; + DeleteShader = qopenglfResolveDeleteShader; + DetachShader = qopenglfResolveDetachShader; + DisableVertexAttribArray = qopenglfResolveDisableVertexAttribArray; + EnableVertexAttribArray = qopenglfResolveEnableVertexAttribArray; + FramebufferRenderbuffer = qopenglfResolveFramebufferRenderbuffer; + FramebufferTexture2D = qopenglfResolveFramebufferTexture2D; + GenBuffers = qopenglfResolveGenBuffers; + GenerateMipmap = qopenglfResolveGenerateMipmap; + GenFramebuffers = qopenglfResolveGenFramebuffers; + GenRenderbuffers = qopenglfResolveGenRenderbuffers; + GetActiveAttrib = qopenglfResolveGetActiveAttrib; + GetActiveUniform = qopenglfResolveGetActiveUniform; + GetAttachedShaders = qopenglfResolveGetAttachedShaders; + GetAttribLocation = qopenglfResolveGetAttribLocation; + GetBufferParameteriv = qopenglfResolveGetBufferParameteriv; + GetFramebufferAttachmentParameteriv = qopenglfResolveGetFramebufferAttachmentParameteriv; + GetProgramiv = qopenglfResolveGetProgramiv; + GetProgramInfoLog = qopenglfResolveGetProgramInfoLog; + GetRenderbufferParameteriv = qopenglfResolveGetRenderbufferParameteriv; + GetShaderiv = qopenglfResolveGetShaderiv; + GetShaderInfoLog = qopenglfResolveGetShaderInfoLog; + GetShaderPrecisionFormat = qopenglfResolveGetShaderPrecisionFormat; + GetShaderSource = qopenglfResolveGetShaderSource; + GetUniformfv = qopenglfResolveGetUniformfv; + GetUniformiv = qopenglfResolveGetUniformiv; + GetUniformLocation = qopenglfResolveGetUniformLocation; + GetVertexAttribfv = qopenglfResolveGetVertexAttribfv; + GetVertexAttribiv = qopenglfResolveGetVertexAttribiv; + GetVertexAttribPointerv = qopenglfResolveGetVertexAttribPointerv; + IsBuffer = qopenglfResolveIsBuffer; + IsFramebuffer = qopenglfResolveIsFramebuffer; + IsProgram = qopenglfResolveIsProgram; + IsRenderbuffer = qopenglfResolveIsRenderbuffer; + IsShader = qopenglfResolveIsShader; + LinkProgram = qopenglfResolveLinkProgram; + ReleaseShaderCompiler = qopenglfResolveReleaseShaderCompiler; + RenderbufferStorage = qopenglfResolveRenderbufferStorage; + SampleCoverage = qopenglfResolveSampleCoverage; + ShaderBinary = qopenglfResolveShaderBinary; + ShaderSource = qopenglfResolveShaderSource; + StencilFuncSeparate = qopenglfResolveStencilFuncSeparate; + StencilMaskSeparate = qopenglfResolveStencilMaskSeparate; + StencilOpSeparate = qopenglfResolveStencilOpSeparate; + Uniform1f = qopenglfResolveUniform1f; + Uniform1fv = qopenglfResolveUniform1fv; + Uniform1i = qopenglfResolveUniform1i; + Uniform1iv = qopenglfResolveUniform1iv; + Uniform2f = qopenglfResolveUniform2f; + Uniform2fv = qopenglfResolveUniform2fv; + Uniform2i = qopenglfResolveUniform2i; + Uniform2iv = qopenglfResolveUniform2iv; + Uniform3f = qopenglfResolveUniform3f; + Uniform3fv = qopenglfResolveUniform3fv; + Uniform3i = qopenglfResolveUniform3i; + Uniform3iv = qopenglfResolveUniform3iv; + Uniform4f = qopenglfResolveUniform4f; + Uniform4fv = qopenglfResolveUniform4fv; + Uniform4i = qopenglfResolveUniform4i; + Uniform4iv = qopenglfResolveUniform4iv; + UniformMatrix2fv = qopenglfResolveUniformMatrix2fv; + UniformMatrix3fv = qopenglfResolveUniformMatrix3fv; + UniformMatrix4fv = qopenglfResolveUniformMatrix4fv; + UseProgram = qopenglfResolveUseProgram; + ValidateProgram = qopenglfResolveValidateProgram; + VertexAttrib1f = qopenglfResolveVertexAttrib1f; + VertexAttrib1fv = qopenglfResolveVertexAttrib1fv; + VertexAttrib2f = qopenglfResolveVertexAttrib2f; + VertexAttrib2fv = qopenglfResolveVertexAttrib2fv; + VertexAttrib3f = qopenglfResolveVertexAttrib3f; + VertexAttrib3fv = qopenglfResolveVertexAttrib3fv; + VertexAttrib4f = qopenglfResolveVertexAttrib4f; + VertexAttrib4fv = qopenglfResolveVertexAttrib4fv; + VertexAttribPointer = qopenglfResolveVertexAttribPointer; +#endif // !QT_OPENGL_ES_2 +} + +QOpenGLExtensionsPrivate::QOpenGLExtensionsPrivate(QOpenGLContext *ctx) + : QOpenGLFunctionsPrivate(ctx) +{ + MapBuffer = qopenglfResolveMapBuffer; + UnmapBuffer = qopenglfResolveUnmapBuffer; + BlitFramebuffer = qopenglfResolveBlitFramebuffer; + RenderbufferStorageMultisample = qopenglfResolveRenderbufferStorageMultisample; + GetBufferSubData = qopenglfResolveGetBufferSubData; +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglfunctions.h b/src/gui/opengl/qopenglfunctions.h new file mode 100644 index 0000000000..d3bd580fc7 --- /dev/null +++ b/src/gui/opengl/qopenglfunctions.h @@ -0,0 +1,2427 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGLFUNCTIONS_H +#define QOPENGLFUNCTIONS_H + +#ifdef __GLEW_H__ +#warning qopenglfunctions.h is not compatible with GLEW, GLEW defines will be undefined +#warning To use GLEW with Qt, do not include <qopengl.h> or <QOpenGLFunctions> after glew.h +#endif + +#include <QtGui/qopengl.h> +#include <QtGui/qopenglcontext.h> + +//#define Q_ENABLE_OPENGL_FUNCTIONS_DEBUG + +#ifdef Q_ENABLE_OPENGL_FUNCTIONS_DEBUG +#include <stdio.h> +#define Q_OPENGL_FUNCTIONS_DEBUG \ + GLenum error = glGetError(); \ + if (error != GL_NO_ERROR) { \ + unsigned clamped = qMin(unsigned(error - GL_INVALID_ENUM), 4U); \ + const char *errors[] = { "GL_INVALID_ENUM", "GL_INVALID_VALUE", "GL_INVALID_OPERATION", "Unknown" }; \ + printf("GL error at %s:%d: %s\n", __FILE__, __LINE__, errors[clamped]); \ + int *value = 0; \ + *value = 0; \ + } +#else +#define Q_OPENGL_FUNCTIONS_DEBUG +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +// Types that aren't defined in all system's gl.h files. +typedef ptrdiff_t qopengl_GLintptr; +typedef ptrdiff_t qopengl_GLsizeiptr; + +#ifdef Q_WS_WIN +# define QOPENGLF_APIENTRY APIENTRY +#endif + +#ifndef Q_WS_MAC +# ifndef QOPENGLF_APIENTRYP +# ifdef QOPENGLF_APIENTRY +# define QOPENGLF_APIENTRYP QOPENGLF_APIENTRY * +# else +# define QOPENGLF_APIENTRY +# define QOPENGLF_APIENTRYP * +# endif +# endif +#else +# define QOPENGLF_APIENTRY +# define QOPENGLF_APIENTRYP * +#endif + +struct QOpenGLFunctionsPrivate; + +// Undefine any macros from GLEW, qopenglextensions_p.h, etc that +// may interfere with the definition of QOpenGLFunctions. +#undef glActiveTexture +#undef glAttachShader +#undef glBindAttribLocation +#undef glBindBuffer +#undef glBindFramebuffer +#undef glBindRenderbuffer +#undef glBlendColor +#undef glBlendEquation +#undef glBlendEquationSeparate +#undef glBlendFuncSeparate +#undef glBufferData +#undef glBufferSubData +#undef glCheckFramebufferStatus +#undef glClearDepthf +#undef glCompileShader +#undef glCompressedTexImage2D +#undef glCompressedTexSubImage2D +#undef glCreateProgram +#undef glCreateShader +#undef glDeleteBuffers +#undef glDeleteFramebuffers +#undef glDeleteProgram +#undef glDeleteRenderbuffers +#undef glDeleteShader +#undef glDepthRangef +#undef glDetachShader +#undef glDisableVertexAttribArray +#undef glEnableVertexAttribArray +#undef glFramebufferRenderbuffer +#undef glFramebufferTexture2D +#undef glGenBuffers +#undef glGenerateMipmap +#undef glGenFramebuffers +#undef glGenRenderbuffers +#undef glGetActiveAttrib +#undef glGetActiveUniform +#undef glGetAttachedShaders +#undef glGetAttribLocation +#undef glGetBufferParameteriv +#undef glGetFramebufferAttachmentParameteriv +#undef glGetProgramiv +#undef glGetProgramInfoLog +#undef glGetRenderbufferParameteriv +#undef glGetShaderiv +#undef glGetShaderInfoLog +#undef glGetShaderPrecisionFormat +#undef glGetShaderSource +#undef glGetUniformfv +#undef glGetUniformiv +#undef glGetUniformLocation +#undef glGetVertexAttribfv +#undef glGetVertexAttribiv +#undef glGetVertexAttribPointerv +#undef glIsBuffer +#undef glIsFramebuffer +#undef glIsProgram +#undef glIsRenderbuffer +#undef glIsShader +#undef glLinkProgram +#undef glReleaseShaderCompiler +#undef glRenderbufferStorage +#undef glSampleCoverage +#undef glShaderBinary +#undef glShaderSource +#undef glStencilFuncSeparate +#undef glStencilMaskSeparate +#undef glStencilOpSeparate +#undef glUniform1f +#undef glUniform1fv +#undef glUniform1i +#undef glUniform1iv +#undef glUniform2f +#undef glUniform2fv +#undef glUniform2i +#undef glUniform2iv +#undef glUniform3f +#undef glUniform3fv +#undef glUniform3i +#undef glUniform3iv +#undef glUniform4f +#undef glUniform4fv +#undef glUniform4i +#undef glUniform4iv +#undef glUniformMatrix2fv +#undef glUniformMatrix3fv +#undef glUniformMatrix4fv +#undef glUseProgram +#undef glValidateProgram +#undef glVertexAttrib1f +#undef glVertexAttrib1fv +#undef glVertexAttrib2f +#undef glVertexAttrib2fv +#undef glVertexAttrib3f +#undef glVertexAttrib3fv +#undef glVertexAttrib4f +#undef glVertexAttrib4fv +#undef glVertexAttribPointer + +class Q_GUI_EXPORT QOpenGLFunctions +{ +public: + QOpenGLFunctions(); + QOpenGLFunctions(QOpenGLContext *context); + ~QOpenGLFunctions() {} + + enum OpenGLFeature + { + Multitexture = 0x0001, + Shaders = 0x0002, + Buffers = 0x0004, + Framebuffers = 0x0008, + BlendColor = 0x0010, + BlendEquation = 0x0020, + BlendEquationSeparate = 0x0040, + BlendFuncSeparate = 0x0080, + BlendSubtract = 0x0100, + CompressedTextures = 0x0200, + Multisample = 0x0400, + StencilSeparate = 0x0800, + NPOTTextures = 0x1000 + }; + Q_DECLARE_FLAGS(OpenGLFeatures, OpenGLFeature) + + QOpenGLFunctions::OpenGLFeatures openGLFeatures() const; + bool hasOpenGLFeature(QOpenGLFunctions::OpenGLFeature feature) const; + + void initializeGLFunctions(); + + void glActiveTexture(GLenum texture); + void glAttachShader(GLuint program, GLuint shader); + void glBindAttribLocation(GLuint program, GLuint index, const char* name); + void glBindBuffer(GLenum target, GLuint buffer); + void glBindFramebuffer(GLenum target, GLuint framebuffer); + void glBindRenderbuffer(GLenum target, GLuint renderbuffer); + void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); + void glBlendEquation(GLenum mode); + void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha); + void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + void glBufferData(GLenum target, qopengl_GLsizeiptr size, const void* data, GLenum usage); + void glBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, const void* data); + GLenum glCheckFramebufferStatus(GLenum target); + void glClearDepthf(GLclampf depth); + void glCompileShader(GLuint shader); + void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data); + void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data); + GLuint glCreateProgram(); + GLuint glCreateShader(GLenum type); + void glDeleteBuffers(GLsizei n, const GLuint* buffers); + void glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers); + void glDeleteProgram(GLuint program); + void glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers); + void glDeleteShader(GLuint shader); + void glDepthRangef(GLclampf zNear, GLclampf zFar); + void glDetachShader(GLuint program, GLuint shader); + void glDisableVertexAttribArray(GLuint index); + void glEnableVertexAttribArray(GLuint index); + void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + void glGenBuffers(GLsizei n, GLuint* buffers); + void glGenerateMipmap(GLenum target); + void glGenFramebuffers(GLsizei n, GLuint* framebuffers); + void glGenRenderbuffers(GLsizei n, GLuint* renderbuffers); + void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); + void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); + void glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders); + GLint glGetAttribLocation(GLuint program, const char* name); + void glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params); + void glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params); + void glGetProgramiv(GLuint program, GLenum pname, GLint* params); + void glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog); + void glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params); + void glGetShaderiv(GLuint shader, GLenum pname, GLint* params); + void glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog); + void glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); + void glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source); + void glGetUniformfv(GLuint program, GLint location, GLfloat* params); + void glGetUniformiv(GLuint program, GLint location, GLint* params); + GLint glGetUniformLocation(GLuint program, const char* name); + void glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params); + void glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params); + void glGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer); + GLboolean glIsBuffer(GLuint buffer); + GLboolean glIsFramebuffer(GLuint framebuffer); + GLboolean glIsProgram(GLuint program); + GLboolean glIsRenderbuffer(GLuint renderbuffer); + GLboolean glIsShader(GLuint shader); + void glLinkProgram(GLuint program); + void glReleaseShaderCompiler(); + void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + void glSampleCoverage(GLclampf value, GLboolean invert); + void glShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length); + void glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length); + void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask); + void glStencilMaskSeparate(GLenum face, GLuint mask); + void glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass); + void glUniform1f(GLint location, GLfloat x); + void glUniform1fv(GLint location, GLsizei count, const GLfloat* v); + void glUniform1i(GLint location, GLint x); + void glUniform1iv(GLint location, GLsizei count, const GLint* v); + void glUniform2f(GLint location, GLfloat x, GLfloat y); + void glUniform2fv(GLint location, GLsizei count, const GLfloat* v); + void glUniform2i(GLint location, GLint x, GLint y); + void glUniform2iv(GLint location, GLsizei count, const GLint* v); + void glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z); + void glUniform3fv(GLint location, GLsizei count, const GLfloat* v); + void glUniform3i(GLint location, GLint x, GLint y, GLint z); + void glUniform3iv(GLint location, GLsizei count, const GLint* v); + void glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void glUniform4fv(GLint location, GLsizei count, const GLfloat* v); + void glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w); + void glUniform4iv(GLint location, GLsizei count, const GLint* v); + void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + void glUseProgram(GLuint program); + void glValidateProgram(GLuint program); + void glVertexAttrib1f(GLuint indx, GLfloat x); + void glVertexAttrib1fv(GLuint indx, const GLfloat* values); + void glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y); + void glVertexAttrib2fv(GLuint indx, const GLfloat* values); + void glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z); + void glVertexAttrib3fv(GLuint indx, const GLfloat* values); + void glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void glVertexAttrib4fv(GLuint indx, const GLfloat* values); + void glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr); + +protected: + QOpenGLFunctionsPrivate *d_ptr; + static bool isInitialized(const QOpenGLFunctionsPrivate *d) { return d != 0; } +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLFunctions::OpenGLFeatures) + +struct QOpenGLFunctionsPrivate +{ + QOpenGLFunctionsPrivate(QOpenGLContext *ctx); + +#ifndef QT_OPENGL_ES_2 + void (QOPENGLF_APIENTRYP ActiveTexture)(GLenum texture); + void (QOPENGLF_APIENTRYP AttachShader)(GLuint program, GLuint shader); + void (QOPENGLF_APIENTRYP BindAttribLocation)(GLuint program, GLuint index, const char* name); + void (QOPENGLF_APIENTRYP BindBuffer)(GLenum target, GLuint buffer); + void (QOPENGLF_APIENTRYP BindFramebuffer)(GLenum target, GLuint framebuffer); + void (QOPENGLF_APIENTRYP BindRenderbuffer)(GLenum target, GLuint renderbuffer); + void (QOPENGLF_APIENTRYP BlendColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); + void (QOPENGLF_APIENTRYP BlendEquation)(GLenum mode); + void (QOPENGLF_APIENTRYP BlendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha); + void (QOPENGLF_APIENTRYP BlendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + void (QOPENGLF_APIENTRYP BufferData)(GLenum target, qopengl_GLsizeiptr size, const void* data, GLenum usage); + void (QOPENGLF_APIENTRYP BufferSubData)(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, const void* data); + GLenum (QOPENGLF_APIENTRYP CheckFramebufferStatus)(GLenum target); + void (QOPENGLF_APIENTRYP CompileShader)(GLuint shader); + void (QOPENGLF_APIENTRYP CompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data); + void (QOPENGLF_APIENTRYP CompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data); + GLuint (QOPENGLF_APIENTRYP CreateProgram)(); + GLuint (QOPENGLF_APIENTRYP CreateShader)(GLenum type); + void (QOPENGLF_APIENTRYP DeleteBuffers)(GLsizei n, const GLuint* buffers); + void (QOPENGLF_APIENTRYP DeleteFramebuffers)(GLsizei n, const GLuint* framebuffers); + void (QOPENGLF_APIENTRYP DeleteProgram)(GLuint program); + void (QOPENGLF_APIENTRYP DeleteRenderbuffers)(GLsizei n, const GLuint* renderbuffers); + void (QOPENGLF_APIENTRYP DeleteShader)(GLuint shader); + void (QOPENGLF_APIENTRYP DetachShader)(GLuint program, GLuint shader); + void (QOPENGLF_APIENTRYP DisableVertexAttribArray)(GLuint index); + void (QOPENGLF_APIENTRYP EnableVertexAttribArray)(GLuint index); + void (QOPENGLF_APIENTRYP FramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + void (QOPENGLF_APIENTRYP FramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + void (QOPENGLF_APIENTRYP GenBuffers)(GLsizei n, GLuint* buffers); + void (QOPENGLF_APIENTRYP GenerateMipmap)(GLenum target); + void (QOPENGLF_APIENTRYP GenFramebuffers)(GLsizei n, GLuint* framebuffers); + void (QOPENGLF_APIENTRYP GenRenderbuffers)(GLsizei n, GLuint* renderbuffers); + void (QOPENGLF_APIENTRYP GetActiveAttrib)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); + void (QOPENGLF_APIENTRYP GetActiveUniform)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); + void (QOPENGLF_APIENTRYP GetAttachedShaders)(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders); + GLint (QOPENGLF_APIENTRYP GetAttribLocation)(GLuint program, const char* name); + void (QOPENGLF_APIENTRYP GetBufferParameteriv)(GLenum target, GLenum pname, GLint* params); + void (QOPENGLF_APIENTRYP GetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint* params); + void (QOPENGLF_APIENTRYP GetProgramiv)(GLuint program, GLenum pname, GLint* params); + void (QOPENGLF_APIENTRYP GetProgramInfoLog)(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog); + void (QOPENGLF_APIENTRYP GetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint* params); + void (QOPENGLF_APIENTRYP GetShaderiv)(GLuint shader, GLenum pname, GLint* params); + void (QOPENGLF_APIENTRYP GetShaderInfoLog)(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog); + void (QOPENGLF_APIENTRYP GetShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); + void (QOPENGLF_APIENTRYP GetShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, char* source); + void (QOPENGLF_APIENTRYP GetUniformfv)(GLuint program, GLint location, GLfloat* params); + void (QOPENGLF_APIENTRYP GetUniformiv)(GLuint program, GLint location, GLint* params); + GLint (QOPENGLF_APIENTRYP GetUniformLocation)(GLuint program, const char* name); + void (QOPENGLF_APIENTRYP GetVertexAttribfv)(GLuint index, GLenum pname, GLfloat* params); + void (QOPENGLF_APIENTRYP GetVertexAttribiv)(GLuint index, GLenum pname, GLint* params); + void (QOPENGLF_APIENTRYP GetVertexAttribPointerv)(GLuint index, GLenum pname, void** pointer); + GLboolean (QOPENGLF_APIENTRYP IsBuffer)(GLuint buffer); + GLboolean (QOPENGLF_APIENTRYP IsFramebuffer)(GLuint framebuffer); + GLboolean (QOPENGLF_APIENTRYP IsProgram)(GLuint program); + GLboolean (QOPENGLF_APIENTRYP IsRenderbuffer)(GLuint renderbuffer); + GLboolean (QOPENGLF_APIENTRYP IsShader)(GLuint shader); + void (QOPENGLF_APIENTRYP LinkProgram)(GLuint program); + void (QOPENGLF_APIENTRYP ReleaseShaderCompiler)(); + void (QOPENGLF_APIENTRYP RenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + void (QOPENGLF_APIENTRYP SampleCoverage)(GLclampf value, GLboolean invert); + void (QOPENGLF_APIENTRYP ShaderBinary)(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length); + void (QOPENGLF_APIENTRYP ShaderSource)(GLuint shader, GLsizei count, const char** string, const GLint* length); + void (QOPENGLF_APIENTRYP StencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask); + void (QOPENGLF_APIENTRYP StencilMaskSeparate)(GLenum face, GLuint mask); + void (QOPENGLF_APIENTRYP StencilOpSeparate)(GLenum face, GLenum fail, GLenum zfail, GLenum zpass); + void (QOPENGLF_APIENTRYP Uniform1f)(GLint location, GLfloat x); + void (QOPENGLF_APIENTRYP Uniform1fv)(GLint location, GLsizei count, const GLfloat* v); + void (QOPENGLF_APIENTRYP Uniform1i)(GLint location, GLint x); + void (QOPENGLF_APIENTRYP Uniform1iv)(GLint location, GLsizei count, const GLint* v); + void (QOPENGLF_APIENTRYP Uniform2f)(GLint location, GLfloat x, GLfloat y); + void (QOPENGLF_APIENTRYP Uniform2fv)(GLint location, GLsizei count, const GLfloat* v); + void (QOPENGLF_APIENTRYP Uniform2i)(GLint location, GLint x, GLint y); + void (QOPENGLF_APIENTRYP Uniform2iv)(GLint location, GLsizei count, const GLint* v); + void (QOPENGLF_APIENTRYP Uniform3f)(GLint location, GLfloat x, GLfloat y, GLfloat z); + void (QOPENGLF_APIENTRYP Uniform3fv)(GLint location, GLsizei count, const GLfloat* v); + void (QOPENGLF_APIENTRYP Uniform3i)(GLint location, GLint x, GLint y, GLint z); + void (QOPENGLF_APIENTRYP Uniform3iv)(GLint location, GLsizei count, const GLint* v); + void (QOPENGLF_APIENTRYP Uniform4f)(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void (QOPENGLF_APIENTRYP Uniform4fv)(GLint location, GLsizei count, const GLfloat* v); + void (QOPENGLF_APIENTRYP Uniform4i)(GLint location, GLint x, GLint y, GLint z, GLint w); + void (QOPENGLF_APIENTRYP Uniform4iv)(GLint location, GLsizei count, const GLint* v); + void (QOPENGLF_APIENTRYP UniformMatrix2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + void (QOPENGLF_APIENTRYP UniformMatrix3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + void (QOPENGLF_APIENTRYP UniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + void (QOPENGLF_APIENTRYP UseProgram)(GLuint program); + void (QOPENGLF_APIENTRYP ValidateProgram)(GLuint program); + void (QOPENGLF_APIENTRYP VertexAttrib1f)(GLuint indx, GLfloat x); + void (QOPENGLF_APIENTRYP VertexAttrib1fv)(GLuint indx, const GLfloat* values); + void (QOPENGLF_APIENTRYP VertexAttrib2f)(GLuint indx, GLfloat x, GLfloat y); + void (QOPENGLF_APIENTRYP VertexAttrib2fv)(GLuint indx, const GLfloat* values); + void (QOPENGLF_APIENTRYP VertexAttrib3f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z); + void (QOPENGLF_APIENTRYP VertexAttrib3fv)(GLuint indx, const GLfloat* values); + void (QOPENGLF_APIENTRYP VertexAttrib4f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void (QOPENGLF_APIENTRYP VertexAttrib4fv)(GLuint indx, const GLfloat* values); + void (QOPENGLF_APIENTRYP VertexAttribPointer)(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr); +#endif +}; + +inline void QOpenGLFunctions::glActiveTexture(GLenum texture) +{ +#if defined(QT_OPENGL_ES_2) + ::glActiveTexture(texture); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->ActiveTexture(texture); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glAttachShader(GLuint program, GLuint shader) +{ +#if defined(QT_OPENGL_ES_2) + ::glAttachShader(program, shader); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->AttachShader(program, shader); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glBindAttribLocation(GLuint program, GLuint index, const char* name) +{ +#if defined(QT_OPENGL_ES_2) + ::glBindAttribLocation(program, index, name); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->BindAttribLocation(program, index, name); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glBindBuffer(GLenum target, GLuint buffer) +{ +#if defined(QT_OPENGL_ES_2) + ::glBindBuffer(target, buffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->BindBuffer(target, buffer); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glBindFramebuffer(GLenum target, GLuint framebuffer) +{ +#if defined(QT_OPENGL_ES_2) + ::glBindFramebuffer(target, framebuffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->BindFramebuffer(target, framebuffer); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glBindRenderbuffer(GLenum target, GLuint renderbuffer) +{ +#if defined(QT_OPENGL_ES_2) + ::glBindRenderbuffer(target, renderbuffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->BindRenderbuffer(target, renderbuffer); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) +{ +#if defined(QT_OPENGL_ES_2) + ::glBlendColor(red, green, blue, alpha); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->BlendColor(red, green, blue, alpha); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glBlendEquation(GLenum mode) +{ +#if defined(QT_OPENGL_ES_2) + ::glBlendEquation(mode); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->BlendEquation(mode); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) +{ +#if defined(QT_OPENGL_ES_2) + ::glBlendEquationSeparate(modeRGB, modeAlpha); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->BlendEquationSeparate(modeRGB, modeAlpha); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +{ +#if defined(QT_OPENGL_ES_2) + ::glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->BlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glBufferData(GLenum target, qopengl_GLsizeiptr size, const void* data, GLenum usage) +{ +#if defined(QT_OPENGL_ES_2) + ::glBufferData(target, size, data, usage); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->BufferData(target, size, data, usage); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, const void* data) +{ +#if defined(QT_OPENGL_ES_2) + ::glBufferSubData(target, offset, size, data); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->BufferSubData(target, offset, size, data); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline GLenum QOpenGLFunctions::glCheckFramebufferStatus(GLenum target) +{ +#if defined(QT_OPENGL_ES_2) + GLenum result = ::glCheckFramebufferStatus(target); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + GLenum result = d_ptr->CheckFramebufferStatus(target); +#endif + Q_OPENGL_FUNCTIONS_DEBUG + return result; +} + +inline void QOpenGLFunctions::glClearDepthf(GLclampf depth) +{ +#ifndef QT_OPENGL_ES + ::glClearDepth(depth); +#else + ::glClearDepthf(depth); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glCompileShader(GLuint shader) +{ +#if defined(QT_OPENGL_ES_2) + ::glCompileShader(shader); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->CompileShader(shader); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) +{ +#if defined(QT_OPENGL_ES_2) + ::glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->CompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) +{ +#if defined(QT_OPENGL_ES_2) + ::glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->CompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline GLuint QOpenGLFunctions::glCreateProgram() +{ +#if defined(QT_OPENGL_ES_2) + GLuint result = ::glCreateProgram(); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + GLuint result = d_ptr->CreateProgram(); +#endif + Q_OPENGL_FUNCTIONS_DEBUG + return result; +} + +inline GLuint QOpenGLFunctions::glCreateShader(GLenum type) +{ +#if defined(QT_OPENGL_ES_2) + GLuint result = ::glCreateShader(type); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + GLuint result = d_ptr->CreateShader(type); +#endif + Q_OPENGL_FUNCTIONS_DEBUG + return result; +} + +inline void QOpenGLFunctions::glDeleteBuffers(GLsizei n, const GLuint* buffers) +{ +#if defined(QT_OPENGL_ES_2) + ::glDeleteBuffers(n, buffers); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->DeleteBuffers(n, buffers); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) +{ +#if defined(QT_OPENGL_ES_2) + ::glDeleteFramebuffers(n, framebuffers); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->DeleteFramebuffers(n, framebuffers); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glDeleteProgram(GLuint program) +{ +#if defined(QT_OPENGL_ES_2) + ::glDeleteProgram(program); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->DeleteProgram(program); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) +{ +#if defined(QT_OPENGL_ES_2) + ::glDeleteRenderbuffers(n, renderbuffers); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->DeleteRenderbuffers(n, renderbuffers); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glDeleteShader(GLuint shader) +{ +#if defined(QT_OPENGL_ES_2) + ::glDeleteShader(shader); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->DeleteShader(shader); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glDepthRangef(GLclampf zNear, GLclampf zFar) +{ +#ifndef QT_OPENGL_ES + ::glDepthRange(zNear, zFar); +#else + ::glDepthRangef(zNear, zFar); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glDetachShader(GLuint program, GLuint shader) +{ +#if defined(QT_OPENGL_ES_2) + ::glDetachShader(program, shader); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->DetachShader(program, shader); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glDisableVertexAttribArray(GLuint index) +{ +#if defined(QT_OPENGL_ES_2) + ::glDisableVertexAttribArray(index); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->DisableVertexAttribArray(index); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glEnableVertexAttribArray(GLuint index) +{ +#if defined(QT_OPENGL_ES_2) + ::glEnableVertexAttribArray(index); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->EnableVertexAttribArray(index); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +{ +#if defined(QT_OPENGL_ES_2) + ::glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->FramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +{ +#if defined(QT_OPENGL_ES_2) + ::glFramebufferTexture2D(target, attachment, textarget, texture, level); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->FramebufferTexture2D(target, attachment, textarget, texture, level); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGenBuffers(GLsizei n, GLuint* buffers) +{ +#if defined(QT_OPENGL_ES_2) + ::glGenBuffers(n, buffers); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GenBuffers(n, buffers); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGenerateMipmap(GLenum target) +{ +#if defined(QT_OPENGL_ES_2) + ::glGenerateMipmap(target); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GenerateMipmap(target); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGenFramebuffers(GLsizei n, GLuint* framebuffers) +{ +#if defined(QT_OPENGL_ES_2) + ::glGenFramebuffers(n, framebuffers); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GenFramebuffers(n, framebuffers); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGenRenderbuffers(GLsizei n, GLuint* renderbuffers) +{ +#if defined(QT_OPENGL_ES_2) + ::glGenRenderbuffers(n, renderbuffers); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GenRenderbuffers(n, renderbuffers); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetActiveAttrib(program, index, bufsize, length, size, type, name); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetActiveAttrib(program, index, bufsize, length, size, type, name); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetActiveUniform(program, index, bufsize, length, size, type, name); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetActiveUniform(program, index, bufsize, length, size, type, name); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetAttachedShaders(program, maxcount, count, shaders); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetAttachedShaders(program, maxcount, count, shaders); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline GLint QOpenGLFunctions::glGetAttribLocation(GLuint program, const char* name) +{ +#if defined(QT_OPENGL_ES_2) + GLint result = ::glGetAttribLocation(program, name); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + GLint result = d_ptr->GetAttribLocation(program, name); +#endif + Q_OPENGL_FUNCTIONS_DEBUG + return result; +} + +inline void QOpenGLFunctions::glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetBufferParameteriv(target, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetBufferParameteriv(target, pname, params); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetFramebufferAttachmentParameteriv(target, attachment, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetFramebufferAttachmentParameteriv(target, attachment, pname, params); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetProgramiv(GLuint program, GLenum pname, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetProgramiv(program, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetProgramiv(program, pname, params); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetProgramInfoLog(program, bufsize, length, infolog); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetProgramInfoLog(program, bufsize, length, infolog); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetRenderbufferParameteriv(target, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetRenderbufferParameteriv(target, pname, params); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetShaderiv(GLuint shader, GLenum pname, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetShaderiv(shader, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetShaderiv(shader, pname, params); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetShaderInfoLog(shader, bufsize, length, infolog); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetShaderInfoLog(shader, bufsize, length, infolog); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetShaderPrecisionFormat(shadertype, precisiontype, range, precision); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetShaderSource(shader, bufsize, length, source); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetShaderSource(shader, bufsize, length, source); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetUniformfv(GLuint program, GLint location, GLfloat* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetUniformfv(program, location, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetUniformfv(program, location, params); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetUniformiv(GLuint program, GLint location, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetUniformiv(program, location, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetUniformiv(program, location, params); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline GLint QOpenGLFunctions::glGetUniformLocation(GLuint program, const char* name) +{ +#if defined(QT_OPENGL_ES_2) + GLint result = ::glGetUniformLocation(program, name); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + GLint result = d_ptr->GetUniformLocation(program, name); +#endif + Q_OPENGL_FUNCTIONS_DEBUG + return result; +} + +inline void QOpenGLFunctions::glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetVertexAttribfv(index, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetVertexAttribfv(index, pname, params); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetVertexAttribiv(index, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetVertexAttribiv(index, pname, params); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetVertexAttribPointerv(index, pname, pointer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->GetVertexAttribPointerv(index, pname, pointer); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline GLboolean QOpenGLFunctions::glIsBuffer(GLuint buffer) +{ +#if defined(QT_OPENGL_ES_2) + GLboolean result = ::glIsBuffer(buffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + GLboolean result = d_ptr->IsBuffer(buffer); +#endif + Q_OPENGL_FUNCTIONS_DEBUG + return result; +} + +inline GLboolean QOpenGLFunctions::glIsFramebuffer(GLuint framebuffer) +{ +#if defined(QT_OPENGL_ES_2) + GLboolean result = ::glIsFramebuffer(framebuffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + GLboolean result = d_ptr->IsFramebuffer(framebuffer); +#endif + Q_OPENGL_FUNCTIONS_DEBUG + return result; +} + +inline GLboolean QOpenGLFunctions::glIsProgram(GLuint program) +{ +#if defined(QT_OPENGL_ES_2) + GLboolean result = ::glIsProgram(program); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + GLboolean result = d_ptr->IsProgram(program); +#endif + Q_OPENGL_FUNCTIONS_DEBUG + return result; +} + +inline GLboolean QOpenGLFunctions::glIsRenderbuffer(GLuint renderbuffer) +{ +#if defined(QT_OPENGL_ES_2) + GLboolean result = ::glIsRenderbuffer(renderbuffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + GLboolean result = d_ptr->IsRenderbuffer(renderbuffer); +#endif + Q_OPENGL_FUNCTIONS_DEBUG + return result; +} + +inline GLboolean QOpenGLFunctions::glIsShader(GLuint shader) +{ +#if defined(QT_OPENGL_ES_2) + GLboolean result = ::glIsShader(shader); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + GLboolean result = d_ptr->IsShader(shader); +#endif + Q_OPENGL_FUNCTIONS_DEBUG + return result; +} + +inline void QOpenGLFunctions::glLinkProgram(GLuint program) +{ +#if defined(QT_OPENGL_ES_2) + ::glLinkProgram(program); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->LinkProgram(program); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glReleaseShaderCompiler() +{ +#if defined(QT_OPENGL_ES_2) + ::glReleaseShaderCompiler(); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->ReleaseShaderCompiler(); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) +{ +#if defined(QT_OPENGL_ES_2) + ::glRenderbufferStorage(target, internalformat, width, height); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->RenderbufferStorage(target, internalformat, width, height); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glSampleCoverage(GLclampf value, GLboolean invert) +{ +#if defined(QT_OPENGL_ES_2) + ::glSampleCoverage(value, invert); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->SampleCoverage(value, invert); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length) +{ +#if defined(QT_OPENGL_ES_2) + ::glShaderBinary(n, shaders, binaryformat, binary, length); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->ShaderBinary(n, shaders, binaryformat, binary, length); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length) +{ +#if defined(QT_OPENGL_ES_2) + ::glShaderSource(shader, count, string, length); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->ShaderSource(shader, count, string, length); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) +{ +#if defined(QT_OPENGL_ES_2) + ::glStencilFuncSeparate(face, func, ref, mask); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->StencilFuncSeparate(face, func, ref, mask); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glStencilMaskSeparate(GLenum face, GLuint mask) +{ +#if defined(QT_OPENGL_ES_2) + ::glStencilMaskSeparate(face, mask); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->StencilMaskSeparate(face, mask); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) +{ +#if defined(QT_OPENGL_ES_2) + ::glStencilOpSeparate(face, fail, zfail, zpass); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->StencilOpSeparate(face, fail, zfail, zpass); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform1f(GLint location, GLfloat x) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform1f(location, x); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform1f(location, x); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform1fv(GLint location, GLsizei count, const GLfloat* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform1fv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform1fv(location, count, v); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform1i(GLint location, GLint x) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform1i(location, x); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform1i(location, x); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform1iv(GLint location, GLsizei count, const GLint* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform1iv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform1iv(location, count, v); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform2f(GLint location, GLfloat x, GLfloat y) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform2f(location, x, y); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform2f(location, x, y); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform2fv(GLint location, GLsizei count, const GLfloat* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform2fv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform2fv(location, count, v); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform2i(GLint location, GLint x, GLint y) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform2i(location, x, y); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform2i(location, x, y); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform2iv(GLint location, GLsizei count, const GLint* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform2iv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform2iv(location, count, v); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform3f(location, x, y, z); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform3f(location, x, y, z); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform3fv(GLint location, GLsizei count, const GLfloat* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform3fv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform3fv(location, count, v); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform3i(GLint location, GLint x, GLint y, GLint z) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform3i(location, x, y, z); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform3i(location, x, y, z); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform3iv(GLint location, GLsizei count, const GLint* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform3iv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform3iv(location, count, v); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform4f(location, x, y, z, w); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform4f(location, x, y, z, w); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform4fv(GLint location, GLsizei count, const GLfloat* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform4fv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform4fv(location, count, v); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform4i(location, x, y, z, w); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform4i(location, x, y, z, w); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniform4iv(GLint location, GLsizei count, const GLint* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform4iv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->Uniform4iv(location, count, v); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniformMatrix2fv(location, count, transpose, value); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->UniformMatrix2fv(location, count, transpose, value); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniformMatrix3fv(location, count, transpose, value); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->UniformMatrix3fv(location, count, transpose, value); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniformMatrix4fv(location, count, transpose, value); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->UniformMatrix4fv(location, count, transpose, value); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glUseProgram(GLuint program) +{ +#if defined(QT_OPENGL_ES_2) + ::glUseProgram(program); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->UseProgram(program); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glValidateProgram(GLuint program) +{ +#if defined(QT_OPENGL_ES_2) + ::glValidateProgram(program); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->ValidateProgram(program); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glVertexAttrib1f(GLuint indx, GLfloat x) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib1f(indx, x); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->VertexAttrib1f(indx, x); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glVertexAttrib1fv(GLuint indx, const GLfloat* values) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib1fv(indx, values); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->VertexAttrib1fv(indx, values); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib2f(indx, x, y); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->VertexAttrib2f(indx, x, y); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glVertexAttrib2fv(GLuint indx, const GLfloat* values) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib2fv(indx, values); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->VertexAttrib2fv(indx, values); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib3f(indx, x, y, z); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->VertexAttrib3f(indx, x, y, z); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glVertexAttrib3fv(GLuint indx, const GLfloat* values) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib3fv(indx, values); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->VertexAttrib3fv(indx, values); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib4f(indx, x, y, z, w); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->VertexAttrib4f(indx, x, y, z, w); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glVertexAttrib4fv(GLuint indx, const GLfloat* values) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib4fv(indx, values); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->VertexAttrib4fv(indx, values); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +inline void QOpenGLFunctions::glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttribPointer(indx, size, type, normalized, stride, ptr); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->VertexAttribPointer(indx, size, type, normalized, stride, ptr); +#endif + Q_OPENGL_FUNCTIONS_DEBUG +} + +#ifndef GL_ACTIVE_ATTRIBUTE_MAX_LENGTH +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#endif +#ifndef GL_ACTIVE_ATTRIBUTES +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#endif +#ifndef GL_ACTIVE_TEXTURE +#define GL_ACTIVE_TEXTURE 0x84E0 +#endif +#ifndef GL_ACTIVE_UNIFORM_MAX_LENGTH +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#endif +#ifndef GL_ACTIVE_UNIFORMS +#define GL_ACTIVE_UNIFORMS 0x8B86 +#endif +#ifndef GL_ALIASED_LINE_WIDTH_RANGE +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#endif +#ifndef GL_ALIASED_POINT_SIZE_RANGE +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#endif +#ifndef GL_ALPHA +#define GL_ALPHA 0x1906 +#endif +#ifndef GL_ALPHA_BITS +#define GL_ALPHA_BITS 0x0D55 +#endif +#ifndef GL_ALWAYS +#define GL_ALWAYS 0x0207 +#endif +#ifndef GL_ARRAY_BUFFER +#define GL_ARRAY_BUFFER 0x8892 +#endif +#ifndef GL_ARRAY_BUFFER_BINDING +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#endif +#ifndef GL_ATTACHED_SHADERS +#define GL_ATTACHED_SHADERS 0x8B85 +#endif +#ifndef GL_BACK +#define GL_BACK 0x0405 +#endif +#ifndef GL_BLEND +#define GL_BLEND 0x0BE2 +#endif +#ifndef GL_BLEND_COLOR +#define GL_BLEND_COLOR 0x8005 +#endif +#ifndef GL_BLEND_DST_ALPHA +#define GL_BLEND_DST_ALPHA 0x80CA +#endif +#ifndef GL_BLEND_DST_RGB +#define GL_BLEND_DST_RGB 0x80C8 +#endif +#ifndef GL_BLEND_EQUATION +#define GL_BLEND_EQUATION 0x8009 +#endif +#ifndef GL_BLEND_EQUATION_ALPHA +#define GL_BLEND_EQUATION_ALPHA 0x883D +#endif +#ifndef GL_BLEND_EQUATION_RGB +#define GL_BLEND_EQUATION_RGB 0x8009 +#endif +#ifndef GL_BLEND_SRC_ALPHA +#define GL_BLEND_SRC_ALPHA 0x80CB +#endif +#ifndef GL_BLEND_SRC_RGB +#define GL_BLEND_SRC_RGB 0x80C9 +#endif +#ifndef GL_BLUE_BITS +#define GL_BLUE_BITS 0x0D54 +#endif +#ifndef GL_BOOL +#define GL_BOOL 0x8B56 +#endif +#ifndef GL_BOOL_VEC2 +#define GL_BOOL_VEC2 0x8B57 +#endif +#ifndef GL_BOOL_VEC3 +#define GL_BOOL_VEC3 0x8B58 +#endif +#ifndef GL_BOOL_VEC4 +#define GL_BOOL_VEC4 0x8B59 +#endif +#ifndef GL_BUFFER_SIZE +#define GL_BUFFER_SIZE 0x8764 +#endif +#ifndef GL_BUFFER_USAGE +#define GL_BUFFER_USAGE 0x8765 +#endif +#ifndef GL_BYTE +#define GL_BYTE 0x1400 +#endif +#ifndef GL_CCW +#define GL_CCW 0x0901 +#endif +#ifndef GL_CLAMP_TO_EDGE +#define GL_CLAMP_TO_EDGE 0x812F +#endif +#ifndef GL_COLOR_ATTACHMENT0 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#endif +#ifndef GL_COLOR_BUFFER_BIT +#define GL_COLOR_BUFFER_BIT 0x00004000 +#endif +#ifndef GL_COLOR_CLEAR_VALUE +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#endif +#ifndef GL_COLOR_WRITEMASK +#define GL_COLOR_WRITEMASK 0x0C23 +#endif +#ifndef GL_COMPILE_STATUS +#define GL_COMPILE_STATUS 0x8B81 +#endif +#ifndef GL_COMPRESSED_TEXTURE_FORMATS +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#endif +#ifndef GL_CONSTANT_ALPHA +#define GL_CONSTANT_ALPHA 0x8003 +#endif +#ifndef GL_CONSTANT_COLOR +#define GL_CONSTANT_COLOR 0x8001 +#endif +#ifndef GL_CULL_FACE +#define GL_CULL_FACE 0x0B44 +#endif +#ifndef GL_CULL_FACE_MODE +#define GL_CULL_FACE_MODE 0x0B45 +#endif +#ifndef GL_CURRENT_PROGRAM +#define GL_CURRENT_PROGRAM 0x8B8D +#endif +#ifndef GL_CURRENT_VERTEX_ATTRIB +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#endif +#ifndef GL_CW +#define GL_CW 0x0900 +#endif +#ifndef GL_DECR +#define GL_DECR 0x1E03 +#endif +#ifndef GL_DECR_WRAP +#define GL_DECR_WRAP 0x8508 +#endif +#ifndef GL_DELETE_STATUS +#define GL_DELETE_STATUS 0x8B80 +#endif +#ifndef GL_DEPTH_ATTACHMENT +#define GL_DEPTH_ATTACHMENT 0x8D00 +#endif +#ifndef GL_DEPTH_BITS +#define GL_DEPTH_BITS 0x0D56 +#endif +#ifndef GL_DEPTH_BUFFER_BIT +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#endif +#ifndef GL_DEPTH_CLEAR_VALUE +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#endif +#ifndef GL_DEPTH_COMPONENT +#define GL_DEPTH_COMPONENT 0x1902 +#endif +#ifndef GL_DEPTH_COMPONENT16 +#define GL_DEPTH_COMPONENT16 0x81A5 +#endif +#ifndef GL_DEPTH_FUNC +#define GL_DEPTH_FUNC 0x0B74 +#endif +#ifndef GL_DEPTH_RANGE +#define GL_DEPTH_RANGE 0x0B70 +#endif +#ifndef GL_DEPTH_TEST +#define GL_DEPTH_TEST 0x0B71 +#endif +#ifndef GL_DEPTH_WRITEMASK +#define GL_DEPTH_WRITEMASK 0x0B72 +#endif +#ifndef GL_DITHER +#define GL_DITHER 0x0BD0 +#endif +#ifndef GL_DONT_CARE +#define GL_DONT_CARE 0x1100 +#endif +#ifndef GL_DST_ALPHA +#define GL_DST_ALPHA 0x0304 +#endif +#ifndef GL_DST_COLOR +#define GL_DST_COLOR 0x0306 +#endif +#ifndef GL_DYNAMIC_DRAW +#define GL_DYNAMIC_DRAW 0x88E8 +#endif +#ifndef GL_ELEMENT_ARRAY_BUFFER +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#endif +#ifndef GL_ELEMENT_ARRAY_BUFFER_BINDING +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#endif +#ifndef GL_EQUAL +#define GL_EQUAL 0x0202 +#endif +#ifndef GL_EXTENSIONS +#define GL_EXTENSIONS 0x1F03 +#endif +#ifndef GL_FALSE +#define GL_FALSE 0 +#endif +#ifndef GL_FASTEST +#define GL_FASTEST 0x1101 +#endif +#ifndef GL_FIXED +#define GL_FIXED 0x140C +#endif +#ifndef GL_FLOAT +#define GL_FLOAT 0x1406 +#endif +#ifndef GL_FLOAT_MAT2 +#define GL_FLOAT_MAT2 0x8B5A +#endif +#ifndef GL_FLOAT_MAT3 +#define GL_FLOAT_MAT3 0x8B5B +#endif +#ifndef GL_FLOAT_MAT4 +#define GL_FLOAT_MAT4 0x8B5C +#endif +#ifndef GL_FLOAT_VEC2 +#define GL_FLOAT_VEC2 0x8B50 +#endif +#ifndef GL_FLOAT_VEC3 +#define GL_FLOAT_VEC3 0x8B51 +#endif +#ifndef GL_FLOAT_VEC4 +#define GL_FLOAT_VEC4 0x8B52 +#endif +#ifndef GL_FRAGMENT_SHADER +#define GL_FRAGMENT_SHADER 0x8B30 +#endif +#ifndef GL_FRAMEBUFFER +#define GL_FRAMEBUFFER 0x8D40 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#endif +#ifndef GL_FRAMEBUFFER_BINDING +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#endif +#ifndef GL_FRAMEBUFFER_COMPLETE +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#endif +#ifndef GL_FRAMEBUFFER_UNSUPPORTED +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#endif +#ifndef GL_FRONT +#define GL_FRONT 0x0404 +#endif +#ifndef GL_FRONT_AND_BACK +#define GL_FRONT_AND_BACK 0x0408 +#endif +#ifndef GL_FRONT_FACE +#define GL_FRONT_FACE 0x0B46 +#endif +#ifndef GL_FUNC_ADD +#define GL_FUNC_ADD 0x8006 +#endif +#ifndef GL_FUNC_REVERSE_SUBTRACT +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#endif +#ifndef GL_FUNC_SUBTRACT +#define GL_FUNC_SUBTRACT 0x800A +#endif +#ifndef GL_GENERATE_MIPMAP_HINT +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#endif +#ifndef GL_GEQUAL +#define GL_GEQUAL 0x0206 +#endif +#ifndef GL_GREATER +#define GL_GREATER 0x0204 +#endif +#ifndef GL_GREEN_BITS +#define GL_GREEN_BITS 0x0D53 +#endif +#ifndef GL_HIGH_FLOAT +#define GL_HIGH_FLOAT 0x8DF2 +#endif +#ifndef GL_HIGH_INT +#define GL_HIGH_INT 0x8DF5 +#endif +#ifndef GL_IMPLEMENTATION_COLOR_READ_FORMAT +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#endif +#ifndef GL_IMPLEMENTATION_COLOR_READ_TYPE +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#endif +#ifndef GL_INCR +#define GL_INCR 0x1E02 +#endif +#ifndef GL_INCR_WRAP +#define GL_INCR_WRAP 0x8507 +#endif +#ifndef GL_INFO_LOG_LENGTH +#define GL_INFO_LOG_LENGTH 0x8B84 +#endif +#ifndef GL_INT +#define GL_INT 0x1404 +#endif +#ifndef GL_INT_VEC2 +#define GL_INT_VEC2 0x8B53 +#endif +#ifndef GL_INT_VEC3 +#define GL_INT_VEC3 0x8B54 +#endif +#ifndef GL_INT_VEC4 +#define GL_INT_VEC4 0x8B55 +#endif +#ifndef GL_INVALID_ENUM +#define GL_INVALID_ENUM 0x0500 +#endif +#ifndef GL_INVALID_FRAMEBUFFER_OPERATION +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#endif +#ifndef GL_INVALID_OPERATION +#define GL_INVALID_OPERATION 0x0502 +#endif +#ifndef GL_INVALID_VALUE +#define GL_INVALID_VALUE 0x0501 +#endif +#ifndef GL_INVERT +#define GL_INVERT 0x150A +#endif +#ifndef GL_KEEP +#define GL_KEEP 0x1E00 +#endif +#ifndef GL_LEQUAL +#define GL_LEQUAL 0x0203 +#endif +#ifndef GL_LESS +#define GL_LESS 0x0201 +#endif +#ifndef GL_LINEAR +#define GL_LINEAR 0x2601 +#endif +#ifndef GL_LINEAR_MIPMAP_LINEAR +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#endif +#ifndef GL_LINEAR_MIPMAP_NEAREST +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#endif +#ifndef GL_LINE_LOOP +#define GL_LINE_LOOP 0x0002 +#endif +#ifndef GL_LINES +#define GL_LINES 0x0001 +#endif +#ifndef GL_LINE_STRIP +#define GL_LINE_STRIP 0x0003 +#endif +#ifndef GL_LINE_WIDTH +#define GL_LINE_WIDTH 0x0B21 +#endif +#ifndef GL_LINK_STATUS +#define GL_LINK_STATUS 0x8B82 +#endif +#ifndef GL_LOW_FLOAT +#define GL_LOW_FLOAT 0x8DF0 +#endif +#ifndef GL_LOW_INT +#define GL_LOW_INT 0x8DF3 +#endif +#ifndef GL_LUMINANCE +#define GL_LUMINANCE 0x1909 +#endif +#ifndef GL_LUMINANCE_ALPHA +#define GL_LUMINANCE_ALPHA 0x190A +#endif +#ifndef GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#endif +#ifndef GL_MAX_CUBE_MAP_TEXTURE_SIZE +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#endif +#ifndef GL_MAX_FRAGMENT_UNIFORM_VECTORS +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#endif +#ifndef GL_MAX_RENDERBUFFER_SIZE +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#endif +#ifndef GL_MAX_TEXTURE_IMAGE_UNITS +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#endif +#ifndef GL_MAX_TEXTURE_SIZE +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#endif +#ifndef GL_MAX_VARYING_VECTORS +#define GL_MAX_VARYING_VECTORS 0x8DFC +#endif +#ifndef GL_MAX_VERTEX_ATTRIBS +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#endif +#ifndef GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#endif +#ifndef GL_MAX_VERTEX_UNIFORM_VECTORS +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#endif +#ifndef GL_MAX_VIEWPORT_DIMS +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#endif +#ifndef GL_MEDIUM_FLOAT +#define GL_MEDIUM_FLOAT 0x8DF1 +#endif +#ifndef GL_MEDIUM_INT +#define GL_MEDIUM_INT 0x8DF4 +#endif +#ifndef GL_MIRRORED_REPEAT +#define GL_MIRRORED_REPEAT 0x8370 +#endif +#ifndef GL_NEAREST +#define GL_NEAREST 0x2600 +#endif +#ifndef GL_NEAREST_MIPMAP_LINEAR +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#endif +#ifndef GL_NEAREST_MIPMAP_NEAREST +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#endif +#ifndef GL_NEVER +#define GL_NEVER 0x0200 +#endif +#ifndef GL_NICEST +#define GL_NICEST 0x1102 +#endif +#ifndef GL_NO_ERROR +#define GL_NO_ERROR 0 +#endif +#ifndef GL_NONE +#define GL_NONE 0 +#endif +#ifndef GL_NOTEQUAL +#define GL_NOTEQUAL 0x0205 +#endif +#ifndef GL_NUM_COMPRESSED_TEXTURE_FORMATS +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#endif +#ifndef GL_NUM_SHADER_BINARY_FORMATS +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#endif +#ifndef GL_ONE +#define GL_ONE 1 +#endif +#ifndef GL_ONE_MINUS_CONSTANT_ALPHA +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#endif +#ifndef GL_ONE_MINUS_CONSTANT_COLOR +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#endif +#ifndef GL_ONE_MINUS_DST_ALPHA +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#endif +#ifndef GL_ONE_MINUS_DST_COLOR +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#endif +#ifndef GL_ONE_MINUS_SRC_ALPHA +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#endif +#ifndef GL_ONE_MINUS_SRC_COLOR +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#endif +#ifndef GL_OUT_OF_MEMORY +#define GL_OUT_OF_MEMORY 0x0505 +#endif +#ifndef GL_PACK_ALIGNMENT +#define GL_PACK_ALIGNMENT 0x0D05 +#endif +#ifndef GL_POINTS +#define GL_POINTS 0x0000 +#endif +#ifndef GL_POLYGON_OFFSET_FACTOR +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#endif +#ifndef GL_POLYGON_OFFSET_FILL +#define GL_POLYGON_OFFSET_FILL 0x8037 +#endif +#ifndef GL_POLYGON_OFFSET_UNITS +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#endif +#ifndef GL_RED_BITS +#define GL_RED_BITS 0x0D52 +#endif +#ifndef GL_RENDERBUFFER +#define GL_RENDERBUFFER 0x8D41 +#endif +#ifndef GL_RENDERBUFFER_ALPHA_SIZE +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#endif +#ifndef GL_RENDERBUFFER_BINDING +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#endif +#ifndef GL_RENDERBUFFER_BLUE_SIZE +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#endif +#ifndef GL_RENDERBUFFER_DEPTH_SIZE +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#endif +#ifndef GL_RENDERBUFFER_GREEN_SIZE +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#endif +#ifndef GL_RENDERBUFFER_HEIGHT +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#endif +#ifndef GL_RENDERBUFFER_INTERNAL_FORMAT +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#endif +#ifndef GL_RENDERBUFFER_RED_SIZE +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#endif +#ifndef GL_RENDERBUFFER_STENCIL_SIZE +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#endif +#ifndef GL_RENDERBUFFER_WIDTH +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#endif +#ifndef GL_RENDERER +#define GL_RENDERER 0x1F01 +#endif +#ifndef GL_REPEAT +#define GL_REPEAT 0x2901 +#endif +#ifndef GL_REPLACE +#define GL_REPLACE 0x1E01 +#endif +#ifndef GL_RGB +#define GL_RGB 0x1907 +#endif +#ifndef GL_RGB565 +#define GL_RGB565 0x8D62 +#endif +#ifndef GL_RGB5_A1 +#define GL_RGB5_A1 0x8057 +#endif +#ifndef GL_RGBA +#define GL_RGBA 0x1908 +#endif +#ifndef GL_RGBA4 +#define GL_RGBA4 0x8056 +#endif +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif +#ifndef GL_SAMPLE_ALPHA_TO_COVERAGE +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#endif +#ifndef GL_SAMPLE_BUFFERS +#define GL_SAMPLE_BUFFERS 0x80A8 +#endif +#ifndef GL_SAMPLE_COVERAGE +#define GL_SAMPLE_COVERAGE 0x80A0 +#endif +#ifndef GL_SAMPLE_COVERAGE_INVERT +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#endif +#ifndef GL_SAMPLE_COVERAGE_VALUE +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#endif +#ifndef GL_SAMPLER_2D +#define GL_SAMPLER_2D 0x8B5E +#endif +#ifndef GL_SAMPLER_CUBE +#define GL_SAMPLER_CUBE 0x8B60 +#endif +#ifndef GL_SAMPLES +#define GL_SAMPLES 0x80A9 +#endif +#ifndef GL_SCISSOR_BOX +#define GL_SCISSOR_BOX 0x0C10 +#endif +#ifndef GL_SCISSOR_TEST +#define GL_SCISSOR_TEST 0x0C11 +#endif +#ifndef GL_SHADER_BINARY_FORMATS +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#endif +#ifndef GL_SHADER_COMPILER +#define GL_SHADER_COMPILER 0x8DFA +#endif +#ifndef GL_SHADER_SOURCE_LENGTH +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#endif +#ifndef GL_SHADER_TYPE +#define GL_SHADER_TYPE 0x8B4F +#endif +#ifndef GL_SHADING_LANGUAGE_VERSION +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#endif +#ifndef GL_SHORT +#define GL_SHORT 0x1402 +#endif +#ifndef GL_SRC_ALPHA +#define GL_SRC_ALPHA 0x0302 +#endif +#ifndef GL_SRC_ALPHA_SATURATE +#define GL_SRC_ALPHA_SATURATE 0x0308 +#endif +#ifndef GL_SRC_COLOR +#define GL_SRC_COLOR 0x0300 +#endif +#ifndef GL_STATIC_DRAW +#define GL_STATIC_DRAW 0x88E4 +#endif +#ifndef GL_STENCIL_ATTACHMENT +#define GL_STENCIL_ATTACHMENT 0x8D20 +#endif +#ifndef GL_STENCIL_BACK_FAIL +#define GL_STENCIL_BACK_FAIL 0x8801 +#endif +#ifndef GL_STENCIL_BACK_FUNC +#define GL_STENCIL_BACK_FUNC 0x8800 +#endif +#ifndef GL_STENCIL_BACK_PASS_DEPTH_FAIL +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#endif +#ifndef GL_STENCIL_BACK_PASS_DEPTH_PASS +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#endif +#ifndef GL_STENCIL_BACK_REF +#define GL_STENCIL_BACK_REF 0x8CA3 +#endif +#ifndef GL_STENCIL_BACK_VALUE_MASK +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#endif +#ifndef GL_STENCIL_BACK_WRITEMASK +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#endif +#ifndef GL_STENCIL_BITS +#define GL_STENCIL_BITS 0x0D57 +#endif +#ifndef GL_STENCIL_BUFFER_BIT +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#endif +#ifndef GL_STENCIL_CLEAR_VALUE +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#endif +#ifndef GL_STENCIL_FAIL +#define GL_STENCIL_FAIL 0x0B94 +#endif +#ifndef GL_STENCIL_FUNC +#define GL_STENCIL_FUNC 0x0B92 +#endif +#ifndef GL_STENCIL_INDEX +#define GL_STENCIL_INDEX 0x1901 +#endif +#ifndef GL_STENCIL_INDEX8 +#define GL_STENCIL_INDEX8 0x8D48 +#endif +#ifndef GL_STENCIL_PASS_DEPTH_FAIL +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#endif +#ifndef GL_STENCIL_PASS_DEPTH_PASS +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#endif +#ifndef GL_STENCIL_REF +#define GL_STENCIL_REF 0x0B97 +#endif +#ifndef GL_STENCIL_TEST +#define GL_STENCIL_TEST 0x0B90 +#endif +#ifndef GL_STENCIL_VALUE_MASK +#define GL_STENCIL_VALUE_MASK 0x0B93 +#endif +#ifndef GL_STENCIL_WRITEMASK +#define GL_STENCIL_WRITEMASK 0x0B98 +#endif +#ifndef GL_STREAM_DRAW +#define GL_STREAM_DRAW 0x88E0 +#endif +#ifndef GL_SUBPIXEL_BITS +#define GL_SUBPIXEL_BITS 0x0D50 +#endif +#ifndef GL_TEXTURE0 +#define GL_TEXTURE0 0x84C0 +#endif +#ifndef GL_TEXTURE +#define GL_TEXTURE 0x1702 +#endif +#ifndef GL_TEXTURE10 +#define GL_TEXTURE10 0x84CA +#endif +#ifndef GL_TEXTURE1 +#define GL_TEXTURE1 0x84C1 +#endif +#ifndef GL_TEXTURE11 +#define GL_TEXTURE11 0x84CB +#endif +#ifndef GL_TEXTURE12 +#define GL_TEXTURE12 0x84CC +#endif +#ifndef GL_TEXTURE13 +#define GL_TEXTURE13 0x84CD +#endif +#ifndef GL_TEXTURE14 +#define GL_TEXTURE14 0x84CE +#endif +#ifndef GL_TEXTURE15 +#define GL_TEXTURE15 0x84CF +#endif +#ifndef GL_TEXTURE16 +#define GL_TEXTURE16 0x84D0 +#endif +#ifndef GL_TEXTURE17 +#define GL_TEXTURE17 0x84D1 +#endif +#ifndef GL_TEXTURE18 +#define GL_TEXTURE18 0x84D2 +#endif +#ifndef GL_TEXTURE19 +#define GL_TEXTURE19 0x84D3 +#endif +#ifndef GL_TEXTURE20 +#define GL_TEXTURE20 0x84D4 +#endif +#ifndef GL_TEXTURE2 +#define GL_TEXTURE2 0x84C2 +#endif +#ifndef GL_TEXTURE21 +#define GL_TEXTURE21 0x84D5 +#endif +#ifndef GL_TEXTURE22 +#define GL_TEXTURE22 0x84D6 +#endif +#ifndef GL_TEXTURE23 +#define GL_TEXTURE23 0x84D7 +#endif +#ifndef GL_TEXTURE24 +#define GL_TEXTURE24 0x84D8 +#endif +#ifndef GL_TEXTURE25 +#define GL_TEXTURE25 0x84D9 +#endif +#ifndef GL_TEXTURE26 +#define GL_TEXTURE26 0x84DA +#endif +#ifndef GL_TEXTURE27 +#define GL_TEXTURE27 0x84DB +#endif +#ifndef GL_TEXTURE28 +#define GL_TEXTURE28 0x84DC +#endif +#ifndef GL_TEXTURE29 +#define GL_TEXTURE29 0x84DD +#endif +#ifndef GL_TEXTURE_2D +#define GL_TEXTURE_2D 0x0DE1 +#endif +#ifndef GL_TEXTURE30 +#define GL_TEXTURE30 0x84DE +#endif +#ifndef GL_TEXTURE3 +#define GL_TEXTURE3 0x84C3 +#endif +#ifndef GL_TEXTURE31 +#define GL_TEXTURE31 0x84DF +#endif +#ifndef GL_TEXTURE4 +#define GL_TEXTURE4 0x84C4 +#endif +#ifndef GL_TEXTURE5 +#define GL_TEXTURE5 0x84C5 +#endif +#ifndef GL_TEXTURE6 +#define GL_TEXTURE6 0x84C6 +#endif +#ifndef GL_TEXTURE7 +#define GL_TEXTURE7 0x84C7 +#endif +#ifndef GL_TEXTURE8 +#define GL_TEXTURE8 0x84C8 +#endif +#ifndef GL_TEXTURE9 +#define GL_TEXTURE9 0x84C9 +#endif +#ifndef GL_TEXTURE_BINDING_2D +#define GL_TEXTURE_BINDING_2D 0x8069 +#endif +#ifndef GL_TEXTURE_BINDING_CUBE_MAP +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#endif +#ifndef GL_TEXTURE_CUBE_MAP +#define GL_TEXTURE_CUBE_MAP 0x8513 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_NEGATIVE_X +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_NEGATIVE_Y +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#endif +#ifndef GL_TEXTURE_CUBE_MAP_POSITIVE_X +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_POSITIVE_Y +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_POSITIVE_Z +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#endif +#ifndef GL_TEXTURE_MAG_FILTER +#define GL_TEXTURE_MAG_FILTER 0x2800 +#endif +#ifndef GL_TEXTURE_MIN_FILTER +#define GL_TEXTURE_MIN_FILTER 0x2801 +#endif +#ifndef GL_TEXTURE_WRAP_S +#define GL_TEXTURE_WRAP_S 0x2802 +#endif +#ifndef GL_TEXTURE_WRAP_T +#define GL_TEXTURE_WRAP_T 0x2803 +#endif +#ifndef GL_TRIANGLE_FAN +#define GL_TRIANGLE_FAN 0x0006 +#endif +#ifndef GL_TRIANGLES +#define GL_TRIANGLES 0x0004 +#endif +#ifndef GL_TRIANGLE_STRIP +#define GL_TRIANGLE_STRIP 0x0005 +#endif +#ifndef GL_TRUE +#define GL_TRUE 1 +#endif +#ifndef GL_UNPACK_ALIGNMENT +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#endif +#ifndef GL_UNSIGNED_BYTE +#define GL_UNSIGNED_BYTE 0x1401 +#endif +#ifndef GL_UNSIGNED_INT +#define GL_UNSIGNED_INT 0x1405 +#endif +#ifndef GL_UNSIGNED_SHORT +#define GL_UNSIGNED_SHORT 0x1403 +#endif +#ifndef GL_UNSIGNED_SHORT_4_4_4_4 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#endif +#ifndef GL_UNSIGNED_SHORT_5_5_5_1 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#endif +#ifndef GL_UNSIGNED_SHORT_5_6_5 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#endif +#ifndef GL_VALIDATE_STATUS +#define GL_VALIDATE_STATUS 0x8B83 +#endif +#ifndef GL_VENDOR +#define GL_VENDOR 0x1F00 +#endif +#ifndef GL_VERSION +#define GL_VERSION 0x1F02 +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_ENABLED +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_NORMALIZED +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_POINTER +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_SIZE +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_STRIDE +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_TYPE +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#endif +#ifndef GL_VERTEX_SHADER +#define GL_VERTEX_SHADER 0x8B31 +#endif +#ifndef GL_VIEWPORT +#define GL_VIEWPORT 0x0BA2 +#endif +#ifndef GL_ZERO +#define GL_ZERO 0 +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/opengl/qopenglgradientcache.cpp b/src/gui/opengl/qopenglgradientcache.cpp new file mode 100644 index 0000000000..f8d61cd620 --- /dev/null +++ b/src/gui/opengl/qopenglgradientcache.cpp @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qopenglgradientcache_p.h" +#include <private/qdrawhelper_p.h> +#include <private/qopenglcontext_p.h> +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +class QOpenGL2GradientCacheWrapper +{ +public: + QOpenGL2GradientCache *cacheForContext(QOpenGLContext *context) { + QMutexLocker lock(&m_mutex); + return m_resource.value<QOpenGL2GradientCache>(context); + } + +private: + QOpenGLMultiGroupSharedResource m_resource; + QMutex m_mutex; +}; + +Q_GLOBAL_STATIC(QOpenGL2GradientCacheWrapper, qt_gradient_caches) + +QOpenGL2GradientCache::QOpenGL2GradientCache(QOpenGLContext *ctx) + : QOpenGLSharedResource(ctx->shareGroup()) +{ +} + +QOpenGL2GradientCache::~QOpenGL2GradientCache() +{ + cache.clear(); +} + +QOpenGL2GradientCache *QOpenGL2GradientCache::cacheForContext(QOpenGLContext *context) +{ + return qt_gradient_caches()->cacheForContext(context); +} + +void QOpenGL2GradientCache::invalidateResource() +{ + QMutexLocker lock(&m_mutex); + cache.clear(); +} + +void QOpenGL2GradientCache::freeResource(QOpenGLContext *) +{ + cleanCache(); +} + +void QOpenGL2GradientCache::cleanCache() +{ + QMutexLocker lock(&m_mutex); + QOpenGLGradientColorTableHash::const_iterator it = cache.constBegin(); + for (; it != cache.constEnd(); ++it) { + const CacheInfo &cache_info = it.value(); + glDeleteTextures(1, &cache_info.texId); + } + cache.clear(); +} + +GLuint QOpenGL2GradientCache::getBuffer(const QGradient &gradient, qreal opacity) +{ + QMutexLocker lock(&m_mutex); + quint64 hash_val = 0; + + QGradientStops stops = gradient.stops(); + for (int i = 0; i < stops.size() && i <= 2; i++) + hash_val += stops[i].second.rgba(); + + QOpenGLGradientColorTableHash::const_iterator it = cache.constFind(hash_val); + + if (it == cache.constEnd()) + return addCacheElement(hash_val, gradient, opacity); + else { + do { + const CacheInfo &cache_info = it.value(); + if (cache_info.stops == stops && cache_info.opacity == opacity + && cache_info.interpolationMode == gradient.interpolationMode()) + { + return cache_info.texId; + } + ++it; + } while (it != cache.constEnd() && it.key() == hash_val); + // an exact match for these stops and opacity was not found, create new cache + return addCacheElement(hash_val, gradient, opacity); + } +} + + +GLuint QOpenGL2GradientCache::addCacheElement(quint64 hash_val, const QGradient &gradient, qreal opacity) +{ + if (cache.size() == maxCacheSize()) { + int elem_to_remove = qrand() % maxCacheSize(); + quint64 key = cache.keys()[elem_to_remove]; + + // need to call glDeleteTextures on each removed cache entry: + QOpenGLGradientColorTableHash::const_iterator it = cache.constFind(key); + do { + glDeleteTextures(1, &it.value().texId); + } while (++it != cache.constEnd() && it.key() == key); + cache.remove(key); // may remove more than 1, but OK + } + + CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode()); + uint buffer[1024]; + generateGradientColorTable(gradient, buffer, paletteSize(), opacity); + glGenTextures(1, &cache_entry.texId); + glBindTexture(GL_TEXTURE_2D, cache_entry.texId); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, paletteSize(), 1, + 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + return cache.insert(hash_val, cache_entry).value().texId; +} + + +// GL's expects pixels in RGBA (when using GL_RGBA), bin-endian (ABGR on x86). +// Qt always stores in ARGB reguardless of the byte-order the mancine uses. +static inline uint qtToGlColor(uint c) +{ + uint o; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + o = (c & 0xff00ff00) // alpha & green already in the right place + | ((c >> 16) & 0x000000ff) // red + | ((c << 16) & 0x00ff0000); // blue +#else //Q_BIG_ENDIAN + o = (c << 8) + | ((c >> 24) & 0x000000ff); +#endif // Q_BYTE_ORDER + return o; +} + +//TODO: Let GL generate the texture using an FBO +void QOpenGL2GradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, qreal opacity) const +{ + int pos = 0; + QGradientStops s = gradient.stops(); + QVector<uint> colors(s.size()); + + for (int i = 0; i < s.size(); ++i) + colors[i] = s[i].second.rgba(); // Qt LIES! It returns ARGB (on little-endian AND on big-endian) + + bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation); + + uint alpha = qRound(opacity * 256); + uint current_color = ARGB_COMBINE_ALPHA(colors[0], alpha); + qreal incr = 1.0 / qreal(size); + qreal fpos = 1.5 * incr; + colorTable[pos++] = qtToGlColor(PREMUL(current_color)); + + while (fpos <= s.first().first) { + colorTable[pos] = colorTable[pos - 1]; + pos++; + fpos += incr; + } + + if (colorInterpolation) + current_color = PREMUL(current_color); + + for (int i = 0; i < s.size() - 1; ++i) { + qreal delta = 1/(s[i+1].first - s[i].first); + uint next_color = ARGB_COMBINE_ALPHA(colors[i+1], alpha); + if (colorInterpolation) + next_color = PREMUL(next_color); + + while (fpos < s[i+1].first && pos < size) { + int dist = int(256 * ((fpos - s[i].first) * delta)); + int idist = 256 - dist; + if (colorInterpolation) + colorTable[pos] = qtToGlColor(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); + else + colorTable[pos] = qtToGlColor(PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); + ++pos; + fpos += incr; + } + current_color = next_color; + } + + Q_ASSERT(s.size() > 0); + + uint last_color = qtToGlColor(PREMUL(ARGB_COMBINE_ALPHA(colors[s.size() - 1], alpha))); + for (;pos < size; ++pos) + colorTable[pos] = last_color; + + // Make sure the last color stop is represented at the end of the table + colorTable[size-1] = last_color; +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglgradientcache_p.h b/src/gui/opengl/qopenglgradientcache_p.h new file mode 100644 index 0000000000..53abf221d2 --- /dev/null +++ b/src/gui/opengl/qopenglgradientcache_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** 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 QtGui 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$ +** +****************************************************************************/ + +// +// 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 <QMultiHash> +#include <QObject> +#include <QtGui/QtGui> +#include <private/qopenglcontext_p.h> +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +class QOpenGL2GradientCache : public QOpenGLSharedResource +{ + struct CacheInfo + { + inline CacheInfo(QGradientStops s, qreal op, QGradient::InterpolationMode mode) : + stops(s), opacity(op), interpolationMode(mode) {} + + GLuint texId; + QGradientStops stops; + qreal opacity; + QGradient::InterpolationMode interpolationMode; + }; + + typedef QMultiHash<quint64, CacheInfo> QOpenGLGradientColorTableHash; + +public: + static QOpenGL2GradientCache *cacheForContext(QOpenGLContext *context); + + QOpenGL2GradientCache(QOpenGLContext *); + ~QOpenGL2GradientCache(); + + GLuint getBuffer(const QGradient &gradient, qreal opacity); + inline int paletteSize() const { return 1024; } + + void invalidateResource(); + void freeResource(QOpenGLContext *ctx); + +private: + inline int maxCacheSize() const { return 60; } + inline void generateGradientColorTable(const QGradient& gradient, + uint *colorTable, + int size, qreal opacity) const; + GLuint addCacheElement(quint64 hash_val, const QGradient &gradient, qreal opacity); + void cleanCache(); + + QOpenGLGradientColorTableHash cache; + QMutex m_mutex; +}; + +QT_END_NAMESPACE + diff --git a/src/gui/opengl/qopenglpaintdevice.cpp b/src/gui/opengl/qopenglpaintdevice.cpp new file mode 100644 index 0000000000..e3ff5ae1f9 --- /dev/null +++ b/src/gui/opengl/qopenglpaintdevice.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** 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 QtGui 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 <private/qopenglpaintdevice_p.h> +#include <private/qopenglcontext_p.h> +#include <private/qopenglframebufferobject_p.h> + +#include <qopenglfunctions.h> + +QT_BEGIN_NAMESPACE + +QOpenGLPaintDevice::QOpenGLPaintDevice() + : m_thisFBO(0) +{ +} + +QOpenGLPaintDevice::~QOpenGLPaintDevice() +{ +} + +int QOpenGLPaintDevice::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + switch(metric) { + case PdmWidth: + return size().width(); + case PdmHeight: + return size().height(); + case PdmDepth: { + const QSurfaceFormat f = format(); + return f.redBufferSize() + f.greenBufferSize() + f.blueBufferSize() + f.alphaBufferSize(); + } + default: + qWarning("QOpenGLPaintDevice::metric() - metric %d not known", metric); + return 0; + } +} + +void QOpenGLPaintDevice::beginPaint() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + // Record the currently bound FBO so we can restore it again + // in endPaint() and bind this device's FBO + // + // Note: m_thisFBO could be zero if the paint device is not + // backed by an FBO (e.g. window back buffer). But there could + // be a previous FBO bound to the context which we need to + // explicitly unbind. Otherwise the painting will go into + // the previous FBO instead of to the window. + m_previousFBO = ctx->d_func()->current_fbo; + + if (m_previousFBO != m_thisFBO) { + ctx->d_func()->current_fbo = m_thisFBO; + QOpenGLFunctions(ctx).glBindFramebuffer(GL_FRAMEBUFFER, m_thisFBO); + } + + // Set the default fbo for the context to m_thisFBO so that + // if some raw GL code between beginNativePainting() and + // endNativePainting() calls QOpenGLFramebufferObject::release(), + // painting will revert to the window surface's fbo. + ctx->d_func()->default_fbo = m_thisFBO; +} + +void QOpenGLPaintDevice::ensureActiveTarget() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + if (ctx->d_func()->current_fbo != m_thisFBO) { + ctx->d_func()->current_fbo = m_thisFBO; + QOpenGLFunctions(ctx).glBindFramebuffer(GL_FRAMEBUFFER, m_thisFBO); + } + + ctx->d_func()->default_fbo = m_thisFBO; +} + +void QOpenGLPaintDevice::endPaint() +{ + // Make sure the FBO bound at beginPaint is re-bound again here: + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + if (m_previousFBO != ctx->d_func()->current_fbo) { + ctx->d_func()->current_fbo = m_previousFBO; + QOpenGLFunctions(ctx).glBindFramebuffer(GL_FRAMEBUFFER, m_previousFBO); + } + + ctx->d_func()->default_fbo = 0; +} + +bool QOpenGLPaintDevice::isFlipped() const +{ + return false; +} + +// returns the QOpenGLPaintDevice for the given QPaintDevice +QOpenGLPaintDevice* QOpenGLPaintDevice::getDevice(QPaintDevice* pd) +{ + QOpenGLPaintDevice* glpd = 0; + + switch(pd->devType()) { + case QInternal::FramebufferObject: + glpd = &(static_cast<QOpenGLFramebufferObject*>(pd)->d_func()->glDevice); + break; + case QInternal::Pixmap: { + qWarning("Pixmap type not supported for GL rendering"); + break; + } + default: + qWarning("QOpenGLPaintDevice::getDevice() - Unknown device type %d", pd->devType()); + break; + } + + return glpd; +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglpaintdevice_p.h b/src/gui/opengl/qopenglpaintdevice_p.h new file mode 100644 index 0000000000..492bc1649a --- /dev/null +++ b/src/gui/opengl/qopenglpaintdevice_p.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGLPAINTDEVICE_P_H +#define QOPENGLPAINTDEVICE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QtGui module. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + + +#include <qpaintdevice.h> +#include <QtGui/qopengl.h> +#include <QtGui/qopenglcontext.h> + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QOpenGLPaintDevice : public QPaintDevice +{ +public: + QOpenGLPaintDevice(); + virtual ~QOpenGLPaintDevice(); + + int devType() const {return QInternal::OpenGL;} + + virtual void beginPaint(); + virtual void ensureActiveTarget(); + virtual void endPaint(); + + virtual QOpenGLContextGroup *group() const = 0; + + virtual QSurfaceFormat format() const = 0; + virtual QSize size() const = 0; + + virtual bool alphaRequested() const = 0; + virtual bool isFlipped() const; + + // returns the QOpenGLPaintDevice for the given QPaintDevice + static QOpenGLPaintDevice* getDevice(QPaintDevice*); + +protected: + int metric(QPaintDevice::PaintDeviceMetric metric) const; + GLuint m_previousFBO; + GLuint m_thisFBO; +}; + + +#if 0 +// Wraps a QOpenGLWidget +class QOpenGLWidget; +class Q_GUI_EXPORT QOpenGLWidgetGLPaintDevice : public QOpenGLPaintDevice +{ +public: + QOpenGLWidgetGLPaintDevice(); + + virtual QPaintEngine* paintEngine() const; + + // QOpenGLWidgets need to do swapBufers in endPaint: + virtual void beginPaint(); + virtual void endPaint(); + virtual QSize size() const; + virtual QOpenGLContext* context() const; + + void setWidget(QOpenGLWidget*); + +private: + friend class QOpenGLWidget; + QOpenGLWidget *glWidget; +}; +#endif + +QT_END_NAMESPACE + +#endif // QOPENGLPAINTDEVICE_P_H diff --git a/src/gui/opengl/qopenglpaintengine.cpp b/src/gui/opengl/qopenglpaintengine.cpp new file mode 100644 index 0000000000..6b9878edf3 --- /dev/null +++ b/src/gui/opengl/qopenglpaintengine.cpp @@ -0,0 +1,2454 @@ +/**************************************************************************** +** +** 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 QtGui 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$ +** +****************************************************************************/ + +/* + When the active program changes, we need to update it's uniforms. + We could track state for each program and only update stale uniforms + - Could lead to lots of overhead if there's a lot of programs + We could update all the uniforms when the program changes + - Could end up updating lots of uniforms which don't need updating + + Updating uniforms should be cheap, so the overhead of updating up-to-date + uniforms should be minimal. It's also less complex. + + Things which _may_ cause a different program to be used: + - Change in brush/pen style + - Change in painter opacity + - Change in composition mode + + Whenever we set a mode on the shader manager - it needs to tell us if it had + to switch to a different program. + + The shader manager should only switch when we tell it to. E.g. if we set a new + brush style and then switch to transparent painter, we only want it to compile + and use the correct program when we really need it. +*/ + +// #define QT_OPENGL_CACHE_AS_VBOS + +#include "qopenglgradientcache_p.h" +#include "qopenglpaintengine_p.h" + +#include <string.h> //for memcpy +#include <qmath.h> + +#include <private/qopengl_p.h> +#include <private/qopenglcontext_p.h> +#include <private/qopenglextensions_p.h> +#include <private/qmath_p.h> +#include <private/qpaintengineex_p.h> +#include <QPaintEngine> +#include <private/qpainter_p.h> +#include <private/qfontengine_p.h> +#include <private/qdatabuffer_p.h> +#include <private/qstatictext_p.h> +#include <private/qtriangulator_p.h> + +#include "qopenglengineshadermanager_p.h" +#include "qopengl2pexvertexarray_p.h" +#include "qopengltriangulatingstroker_p.h" +#include "qopengltextureglyphcache_p.h" + +#include <QDebug> + +QT_BEGIN_NAMESPACE + +#if defined(Q_WS_WIN) +extern Q_GUI_EXPORT bool qt_cleartype_enabled; +#endif + +#ifdef Q_WS_MAC +extern bool qt_applefontsmoothing_enabled; +#endif + +Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert); + +////////////////////////////////// Private Methods ////////////////////////////////////////// + +QOpenGL2PaintEngineExPrivate::~QOpenGL2PaintEngineExPrivate() +{ + delete shaderManager; + + while (pathCaches.size()) { + QVectorPath::CacheEntry *e = *(pathCaches.constBegin()); + e->cleanup(e->engine, e->data); + e->data = 0; + e->engine = 0; + } + + if (elementIndicesVBOId != 0) { + funcs.glDeleteBuffers(1, &elementIndicesVBOId); + elementIndicesVBOId = 0; + } +} + +void QOpenGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id) +{ +// funcs.glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); //### Is it always this texture unit? + if (id != GLuint(-1) && id == lastTextureUsed) + return; + + lastTextureUsed = id; + + if (smoothPixmapTransform) { + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + glTexParameterf(target, GL_TEXTURE_WRAP_S, wrapMode); + glTexParameterf(target, GL_TEXTURE_WRAP_T, wrapMode); +} + + +inline QColor qt_premultiplyColor(QColor c, GLfloat opacity) +{ + qreal alpha = c.alphaF() * opacity; + c.setAlphaF(alpha); + c.setRedF(c.redF() * alpha); + c.setGreenF(c.greenF() * alpha); + c.setBlueF(c.blueF() * alpha); + return c; +} + + +void QOpenGL2PaintEngineExPrivate::setBrush(const QBrush& brush) +{ + if (qbrush_fast_equals(currentBrush, brush)) + return; + + const Qt::BrushStyle newStyle = qbrush_style(brush); + Q_ASSERT(newStyle != Qt::NoBrush); + + currentBrush = brush; + if (!currentBrushPixmap.isNull()) + currentBrushPixmap = QPixmap(); + brushUniformsDirty = true; // All brushes have at least one uniform + + if (newStyle > Qt::SolidPattern) + brushTextureDirty = true; + + if (currentBrush.style() == Qt::TexturePattern + && qHasPixmapTexture(brush) && brush.texture().isQBitmap()) + { + shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::TextureSrcWithPattern); + } else { + shaderManager->setSrcPixelType(newStyle); + } + shaderManager->optimiseForBrushTransform(currentBrush.transform().type()); +} + + +void QOpenGL2PaintEngineExPrivate::useSimpleShader() +{ + shaderManager->useSimpleProgram(); + + if (matrixDirty) + updateMatrix(); +} + +void QOpenGL2PaintEngineExPrivate::updateBrushTexture() +{ + Q_Q(QOpenGL2PaintEngineEx); +// qDebug("QOpenGL2PaintEngineExPrivate::updateBrushTexture()"); + Qt::BrushStyle style = currentBrush.style(); + + if ( (style >= Qt::Dense1Pattern) && (style <= Qt::DiagCrossPattern) ) { + // Get the image data for the pattern + QImage texImage = qt_imageForBrush(style, false); + + funcs.glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); + //ctx->d_func()->bindTexture(texImage, GL_TEXTURE_2D, GL_RGBA, QOpenGLContext::InternalBindOption); + updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform); + } + else if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) { + // Gradiant brush: All the gradiants use the same texture + + const QGradient* g = currentBrush.gradient(); + + // We apply global opacity in the fragment shaders, so we always pass 1.0 + // for opacity to the cache. + GLuint texId = QOpenGL2GradientCache::cacheForContext(ctx)->getBuffer(*g, 1.0); + + funcs.glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); + glBindTexture(GL_TEXTURE_2D, texId); + + if (g->spread() == QGradient::RepeatSpread || g->type() == QGradient::ConicalGradient) + updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform); + else if (g->spread() == QGradient::ReflectSpread) + updateTextureFilter(GL_TEXTURE_2D, GL_MIRRORED_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform); + else + updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, q->state()->renderHints & QPainter::SmoothPixmapTransform); + } + else if (style == Qt::TexturePattern) { + currentBrushPixmap = currentBrush.texture(); + + int max_texture_size = ctx->d_func()->maxTextureSize(); + if (currentBrushPixmap.width() > max_texture_size || currentBrushPixmap.height() > max_texture_size) + currentBrushPixmap = currentBrushPixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio); + + funcs.glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); + QOpenGLTexture *tex = 0;//ctx->d_func()->bindTexture(currentBrushPixmap, GL_TEXTURE_2D, GL_RGBA, + // QOpenGLContext::InternalBindOption | + // QOpenGLContext::CanFlipNativePixmapBindOption); + updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform); + textureInvertedY = tex->invertedY(); + } + brushTextureDirty = false; +} + + +void QOpenGL2PaintEngineExPrivate::updateBrushUniforms() +{ +// qDebug("QOpenGL2PaintEngineExPrivate::updateBrushUniforms()"); + Qt::BrushStyle style = currentBrush.style(); + + if (style == Qt::NoBrush) + return; + + QTransform brushQTransform = currentBrush.transform(); + + if (style == Qt::SolidPattern) { + QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::FragmentColor), col); + } + else { + // All other brushes have a transform and thus need the translation point: + QPointF translationPoint; + + if (style <= Qt::DiagCrossPattern) { + QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity); + + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col); + + QVector2D halfViewportSize(width*0.5, height*0.5); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize); + } + else if (style == Qt::LinearGradientPattern) { + const QLinearGradient *g = static_cast<const QLinearGradient *>(currentBrush.gradient()); + + QPointF realStart = g->start(); + QPointF realFinal = g->finalStop(); + translationPoint = realStart; + + QPointF l = realFinal - realStart; + + QVector3D linearData( + l.x(), + l.y(), + 1.0f / (l.x() * l.x() + l.y() * l.y()) + ); + + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::LinearData), linearData); + + QVector2D halfViewportSize(width*0.5, height*0.5); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize); + } + else if (style == Qt::ConicalGradientPattern) { + const QConicalGradient *g = static_cast<const QConicalGradient *>(currentBrush.gradient()); + translationPoint = g->center(); + + GLfloat angle = -(g->angle() * 2 * Q_PI) / 360.0; + + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Angle), angle); + + QVector2D halfViewportSize(width*0.5, height*0.5); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize); + } + else if (style == Qt::RadialGradientPattern) { + const QRadialGradient *g = static_cast<const QRadialGradient *>(currentBrush.gradient()); + QPointF realCenter = g->center(); + QPointF realFocal = g->focalPoint(); + qreal realRadius = g->centerRadius() - g->focalRadius(); + translationPoint = realFocal; + + QPointF fmp = realCenter - realFocal; + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Fmp), fmp); + + GLfloat fmp2_m_radius2 = -fmp.x() * fmp.x() - fmp.y() * fmp.y() + realRadius*realRadius; + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Fmp2MRadius2), fmp2_m_radius2); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Inverse2Fmp2MRadius2), + GLfloat(1.0 / (2.0*fmp2_m_radius2))); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::SqrFr), + GLfloat(g->focalRadius() * g->focalRadius())); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::BRadius), + GLfloat(2 * (g->centerRadius() - g->focalRadius()) * g->focalRadius()), + g->focalRadius(), + g->centerRadius() - g->focalRadius()); + + QVector2D halfViewportSize(width*0.5, height*0.5); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize); + } + else if (style == Qt::TexturePattern) { + const QPixmap& texPixmap = currentBrush.texture(); + + if (qHasPixmapTexture(currentBrush) && currentBrush.texture().isQBitmap()) { + QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col); + } + + QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 / texPixmap.height()); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::InvertedTextureSize), invertedTextureSize); + + QVector2D halfViewportSize(width*0.5, height*0.5); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize); + } + else + qWarning("QOpenGL2PaintEngineEx: Unimplemented fill style"); + + const QPointF &brushOrigin = q->state()->brushOrigin; + QTransform matrix = q->state()->matrix; + matrix.translate(brushOrigin.x(), brushOrigin.y()); + + QTransform translate(1, 0, 0, 1, -translationPoint.x(), -translationPoint.y()); + qreal m22 = -1; + qreal dy = height; + if (device->isFlipped()) { + m22 = 1; + dy = 0; + } + QTransform gl_to_qt(1, 0, 0, m22, 0, dy); + QTransform inv_matrix; + if (style == Qt::TexturePattern && textureInvertedY == -1) + inv_matrix = gl_to_qt * (QTransform(1, 0, 0, -1, 0, currentBrush.texture().height()) * brushQTransform * matrix).inverted() * translate; + else + inv_matrix = gl_to_qt * (brushQTransform * matrix).inverted() * translate; + + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::BrushTransform), inv_matrix); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::BrushTexture), QT_BRUSH_TEXTURE_UNIT); + } + brushUniformsDirty = false; +} + + +// This assumes the shader manager has already setup the correct shader program +void QOpenGL2PaintEngineExPrivate::updateMatrix() +{ +// qDebug("QOpenGL2PaintEngineExPrivate::updateMatrix()"); + + const QTransform& transform = q->state()->matrix; + + // The projection matrix converts from Qt's coordinate system to GL's coordinate system + // * GL's viewport is 2x2, Qt's is width x height + // * GL has +y -> -y going from bottom -> top, Qt is the other way round + // * GL has [0,0] in the center, Qt has it in the top-left + // + // This results in the Projection matrix below, which is multiplied by the painter's + // transformation matrix, as shown below: + // + // Projection Matrix Painter Transform + // ------------------------------------------------ ------------------------ + // | 2.0 / width | 0.0 | -1.0 | | m11 | m21 | dx | + // | 0.0 | -2.0 / height | 1.0 | * | m12 | m22 | dy | + // | 0.0 | 0.0 | 1.0 | | m13 | m23 | m33 | + // ------------------------------------------------ ------------------------ + // + // NOTE: The resultant matrix is also transposed, as GL expects column-major matracies + + const GLfloat wfactor = 2.0f / width; + GLfloat hfactor = -2.0f / height; + + GLfloat dx = transform.dx(); + GLfloat dy = transform.dy(); + + if (device->isFlipped()) { + hfactor *= -1; + dy -= height; + } + + // Non-integer translates can have strange effects for some rendering operations such as + // anti-aliased text rendering. In such cases, we snap the translate to the pixel grid. + if (snapToPixelGrid && transform.type() == QTransform::TxTranslate) { + // 0.50 needs to rounded down to 0.0 for consistency with raster engine: + dx = ceilf(dx - 0.5f); + dy = ceilf(dy - 0.5f); + } + pmvMatrix[0][0] = (wfactor * transform.m11()) - transform.m13(); + pmvMatrix[1][0] = (wfactor * transform.m21()) - transform.m23(); + pmvMatrix[2][0] = (wfactor * dx) - transform.m33(); + pmvMatrix[0][1] = (hfactor * transform.m12()) + transform.m13(); + pmvMatrix[1][1] = (hfactor * transform.m22()) + transform.m23(); + pmvMatrix[2][1] = (hfactor * dy) + transform.m33(); + pmvMatrix[0][2] = transform.m13(); + pmvMatrix[1][2] = transform.m23(); + pmvMatrix[2][2] = transform.m33(); + + // 1/10000 == 0.0001, so we have good enough res to cover curves + // that span the entire widget... + inverseScale = qMax(1 / qMax( qMax(qAbs(transform.m11()), qAbs(transform.m22())), + qMax(qAbs(transform.m12()), qAbs(transform.m21())) ), + qreal(0.0001)); + + matrixDirty = false; + matrixUniformDirty = true; + + // Set the PMV matrix attribute. As we use an attributes rather than uniforms, we only + // need to do this once for every matrix change and persists across all shader programs. + funcs.glVertexAttrib3fv(QT_PMV_MATRIX_1_ATTR, pmvMatrix[0]); + funcs.glVertexAttrib3fv(QT_PMV_MATRIX_2_ATTR, pmvMatrix[1]); + funcs.glVertexAttrib3fv(QT_PMV_MATRIX_3_ATTR, pmvMatrix[2]); + + dasher.setInvScale(inverseScale); + stroker.setInvScale(inverseScale); +} + + +void QOpenGL2PaintEngineExPrivate::updateCompositionMode() +{ + // NOTE: The entire paint engine works on pre-multiplied data - which is why some of these + // composition modes look odd. +// qDebug() << "QOpenGL2PaintEngineExPrivate::updateCompositionMode() - Setting GL composition mode for " << q->state()->composition_mode; + switch(q->state()->composition_mode) { + case QPainter::CompositionMode_SourceOver: + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + break; + case QPainter::CompositionMode_DestinationOver: + glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE); + break; + case QPainter::CompositionMode_Clear: + glBlendFunc(GL_ZERO, GL_ZERO); + break; + case QPainter::CompositionMode_Source: + glBlendFunc(GL_ONE, GL_ZERO); + break; + case QPainter::CompositionMode_Destination: + glBlendFunc(GL_ZERO, GL_ONE); + break; + case QPainter::CompositionMode_SourceIn: + glBlendFunc(GL_DST_ALPHA, GL_ZERO); + break; + case QPainter::CompositionMode_DestinationIn: + glBlendFunc(GL_ZERO, GL_SRC_ALPHA); + break; + case QPainter::CompositionMode_SourceOut: + glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO); + break; + case QPainter::CompositionMode_DestinationOut: + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); + break; + case QPainter::CompositionMode_SourceAtop: + glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case QPainter::CompositionMode_DestinationAtop: + glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA); + break; + case QPainter::CompositionMode_Xor: + glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case QPainter::CompositionMode_Plus: + glBlendFunc(GL_ONE, GL_ONE); + break; + default: + qWarning("Unsupported composition mode"); + break; + } + + compositionModeDirty = false; +} + +static inline void setCoords(GLfloat *coords, const QOpenGLRect &rect) +{ + coords[0] = rect.left; + coords[1] = rect.top; + coords[2] = rect.right; + coords[3] = rect.top; + coords[4] = rect.right; + coords[5] = rect.bottom; + coords[6] = rect.left; + coords[7] = rect.bottom; +} + +void QOpenGL2PaintEngineExPrivate::drawTexture(const QOpenGLRect& dest, const QOpenGLRect& src, const QSize &textureSize, bool opaque, bool pattern) +{ + // Setup for texture drawing + currentBrush = noBrush; + shaderManager->setSrcPixelType(pattern ? QOpenGLEngineShaderManager::PatternSrc : QOpenGLEngineShaderManager::ImageSrc); + + if (snapToPixelGrid) { + snapToPixelGrid = false; + matrixDirty = true; + } + + if (prepareForDraw(opaque)) + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT); + + if (pattern) { + QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col); + } + + GLfloat dx = 1.0 / textureSize.width(); + GLfloat dy = 1.0 / textureSize.height(); + + QOpenGLRect srcTextureRect(src.left*dx, src.top*dy, src.right*dx, src.bottom*dy); + + setCoords(staticVertexCoordinateArray, dest); + setCoords(staticTextureCoordinateArray, srcTextureRect); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + +void QOpenGL2PaintEngineEx::beginNativePainting() +{ + Q_D(QOpenGL2PaintEngineEx); + ensureActive(); + d->transferMode(BrushDrawingMode); + + d->nativePaintingActive = true; + + d->funcs.glUseProgram(0); + + // Disable all the vertex attribute arrays: + for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i) + d->funcs.glDisableVertexAttribArray(i); + +#ifndef QT_OPENGL_ES_2 + const QSurfaceFormat &fmt = d->device->format(); + if (fmt.majorVersion() < 3 || (fmt.majorVersion() == 3 && fmt.minorVersion() < 1) + || fmt.profile() == QSurfaceFormat::CompatibilityProfile) + { + // be nice to people who mix OpenGL 1.x code with QPainter commands + // by setting modelview and projection matrices to mirror the GL 1 + // paint engine + const QTransform& mtx = state()->matrix; + + float mv_matrix[4][4] = + { + { float(mtx.m11()), float(mtx.m12()), 0, float(mtx.m13()) }, + { float(mtx.m21()), float(mtx.m22()), 0, float(mtx.m23()) }, + { 0, 0, 1, 0 }, + { float(mtx.dx()), float(mtx.dy()), 0, float(mtx.m33()) } + }; + + const QSize sz = d->device->size(); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999); + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(&mv_matrix[0][0]); + } +#endif + + d->lastTextureUsed = GLuint(-1); + d->dirtyStencilRegion = QRect(0, 0, d->width, d->height); + d->resetGLState(); + + d->shaderManager->setDirty(); + + d->needsSync = true; +} + +void QOpenGL2PaintEngineExPrivate::resetGLState() +{ + glDisable(GL_BLEND); + funcs.glActiveTexture(GL_TEXTURE0); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDepthMask(true); + glDepthFunc(GL_LESS); + funcs.glClearDepthf(1); + glStencilMask(0xff); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_ALWAYS, 0, 0xff); + setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false); + setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, false); + setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false); +#ifndef QT_OPENGL_ES_2 + // gl_Color, corresponding to vertex attribute 3, may have been changed + float color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; + funcs.glVertexAttrib4fv(3, color); +#endif +} + +void QOpenGL2PaintEngineEx::endNativePainting() +{ + Q_D(QOpenGL2PaintEngineEx); + d->needsSync = true; + d->nativePaintingActive = false; +} + +void QOpenGL2PaintEngineEx::invalidateState() +{ + Q_D(QOpenGL2PaintEngineEx); + d->needsSync = true; +} + +bool QOpenGL2PaintEngineEx::isNativePaintingActive() const { + Q_D(const QOpenGL2PaintEngineEx); + return d->nativePaintingActive; +} + +void QOpenGL2PaintEngineExPrivate::transferMode(EngineMode newMode) +{ + if (newMode == mode) + return; + + if (mode == TextDrawingMode || mode == ImageDrawingMode || mode == ImageArrayDrawingMode) { + lastTextureUsed = GLuint(-1); + } + + if (newMode == TextDrawingMode) { + shaderManager->setHasComplexGeometry(true); + } else { + shaderManager->setHasComplexGeometry(false); + } + + if (newMode == ImageDrawingMode) { + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray); + setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, staticTextureCoordinateArray); + } + + if (newMode == ImageArrayDrawingMode) { + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data()); + setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data()); + setVertexAttributePointer(QT_OPACITY_ATTR, (GLfloat*)opacityArray.data()); + } + + // This needs to change when we implement high-quality anti-aliasing... + if (newMode != TextDrawingMode) + shaderManager->setMaskType(QOpenGLEngineShaderManager::NoMask); + + mode = newMode; +} + +struct QOpenGL2PEVectorPathCache +{ +#ifdef QT_OPENGL_CACHE_AS_VBOS + GLuint vbo; + GLuint ibo; +#else + float *vertices; + void *indices; +#endif + int vertexCount; + int indexCount; + GLenum primitiveType; + qreal iscale; +}; + +void QOpenGL2PaintEngineExPrivate::cleanupVectorPath(QPaintEngineEx *engine, void *data) +{ + QOpenGL2PEVectorPathCache *c = (QOpenGL2PEVectorPathCache *) data; +#ifdef QT_OPENGL_CACHE_AS_VBOS + Q_ASSERT(engine->type() == QPaintEngine::OpenGL2); + static_cast<QOpenGL2PaintEngineEx *>(engine)->d_func()->unusedVBOSToClean << c->vbo; + if (c->ibo) + d->unusedIBOSToClean << c->ibo; +#else + Q_UNUSED(engine); + qFree(c->vertices); + qFree(c->indices); +#endif + delete c; +} + +// Assumes everything is configured for the brush you want to use +void QOpenGL2PaintEngineExPrivate::fill(const QVectorPath& path) +{ + transferMode(BrushDrawingMode); + + if (snapToPixelGrid) { + snapToPixelGrid = false; + matrixDirty = true; + } + + // Might need to call updateMatrix to re-calculate inverseScale + if (matrixDirty) + updateMatrix(); + + const QPointF* const points = reinterpret_cast<const QPointF*>(path.points()); + + // Check to see if there's any hints + if (path.shape() == QVectorPath::RectangleHint) { + QOpenGLRect rect(points[0].x(), points[0].y(), points[2].x(), points[2].y()); + prepareForDraw(currentBrush.isOpaque()); + composite(rect); + } else if (path.isConvex()) { + + if (path.isCacheable()) { + QVectorPath::CacheEntry *data = path.lookupCacheData(q); + QOpenGL2PEVectorPathCache *cache; + + bool updateCache = false; + + if (data) { + cache = (QOpenGL2PEVectorPathCache *) data->data; + // Check if scale factor is exceeded for curved paths and generate curves if so... + if (path.isCurved()) { + qreal scaleFactor = cache->iscale / inverseScale; + if (scaleFactor < 0.5 || scaleFactor > 2.0) { +#ifdef QT_OPENGL_CACHE_AS_VBOS + glDeleteBuffers(1, &cache->vbo); + cache->vbo = 0; + Q_ASSERT(cache->ibo == 0); +#else + qFree(cache->vertices); + Q_ASSERT(cache->indices == 0); +#endif + updateCache = true; + } + } + } else { + cache = new QOpenGL2PEVectorPathCache; + data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath); + updateCache = true; + } + + // Flatten the path at the current scale factor and fill it into the cache struct. + if (updateCache) { + vertexCoordinateArray.clear(); + vertexCoordinateArray.addPath(path, inverseScale, false); + int vertexCount = vertexCoordinateArray.vertexCount(); + int floatSizeInBytes = vertexCount * 2 * sizeof(float); + cache->vertexCount = vertexCount; + cache->indexCount = 0; + cache->primitiveType = GL_TRIANGLE_FAN; + cache->iscale = inverseScale; +#ifdef QT_OPENGL_CACHE_AS_VBOS + glGenBuffers(1, &cache->vbo); + glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); + glBufferData(GL_ARRAY_BUFFER, floatSizeInBytes, vertexCoordinateArray.data(), GL_STATIC_DRAW); + cache->ibo = 0; +#else + cache->vertices = (float *) qMalloc(floatSizeInBytes); + memcpy(cache->vertices, vertexCoordinateArray.data(), floatSizeInBytes); + cache->indices = 0; +#endif + } + + prepareForDraw(currentBrush.isOpaque()); +#ifdef QT_OPENGL_CACHE_AS_VBOS + glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0); +#else + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices); +#endif + glDrawArrays(cache->primitiveType, 0, cache->vertexCount); + + } else { + // printf(" - Marking path as cachable...\n"); + // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable + path.makeCacheable(); + vertexCoordinateArray.clear(); + vertexCoordinateArray.addPath(path, inverseScale, false); + prepareForDraw(currentBrush.isOpaque()); + drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN); + } + + } else { + bool useCache = path.isCacheable(); + if (useCache) { + QRectF bbox = path.controlPointRect(); + // If the path doesn't fit within these limits, it is possible that the triangulation will fail. + useCache &= (bbox.left() > -0x8000 * inverseScale) + && (bbox.right() < 0x8000 * inverseScale) + && (bbox.top() > -0x8000 * inverseScale) + && (bbox.bottom() < 0x8000 * inverseScale); + } + + if (useCache) { + QVectorPath::CacheEntry *data = path.lookupCacheData(q); + QOpenGL2PEVectorPathCache *cache; + + bool updateCache = false; + + if (data) { + cache = (QOpenGL2PEVectorPathCache *) data->data; + // Check if scale factor is exceeded for curved paths and generate curves if so... + if (path.isCurved()) { + qreal scaleFactor = cache->iscale / inverseScale; + if (scaleFactor < 0.5 || scaleFactor > 2.0) { +#ifdef QT_OPENGL_CACHE_AS_VBOS + glDeleteBuffers(1, &cache->vbo); + glDeleteBuffers(1, &cache->ibo); +#else + qFree(cache->vertices); + qFree(cache->indices); +#endif + updateCache = true; + } + } + } else { + cache = new QOpenGL2PEVectorPathCache; + data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath); + updateCache = true; + } + + // Flatten the path at the current scale factor and fill it into the cache struct. + if (updateCache) { + QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale)); + cache->vertexCount = polys.vertices.size() / 2; + cache->indexCount = polys.indices.size(); + cache->primitiveType = GL_TRIANGLES; + cache->iscale = inverseScale; +#ifdef QT_OPENGL_CACHE_AS_VBOS + glGenBuffers(1, &cache->vbo); + glGenBuffers(1, &cache->ibo); + glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo); + + if (funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint)) + funcs.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint32) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW); + else + funcs.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint16) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW); + + QVarLengthArray<float> vertices(polys.vertices.size()); + for (int i = 0; i < polys.vertices.size(); ++i) + vertices[i] = float(inverseScale * polys.vertices.at(i)); + funcs.glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_STATIC_DRAW); +#else + cache->vertices = (float *) qMalloc(sizeof(float) * polys.vertices.size()); + if (funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint)) { + cache->indices = (quint32 *) qMalloc(sizeof(quint32) * polys.indices.size()); + memcpy(cache->indices, polys.indices.data(), sizeof(quint32) * polys.indices.size()); + } else { + cache->indices = (quint16 *) qMalloc(sizeof(quint16) * polys.indices.size()); + memcpy(cache->indices, polys.indices.data(), sizeof(quint16) * polys.indices.size()); + } + for (int i = 0; i < polys.vertices.size(); ++i) + cache->vertices[i] = float(inverseScale * polys.vertices.at(i)); +#endif + } + + prepareForDraw(currentBrush.isOpaque()); +#ifdef QT_OPENGL_CACHE_AS_VBOS + glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo); + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0); + if (funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint)) + glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, 0); + else + glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_SHORT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); +#else + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices); + if (funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint)) + glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, (qint32 *)cache->indices); + else + glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_SHORT, (qint16 *)cache->indices); +#endif + + } else { + // printf(" - Marking path as cachable...\n"); + // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable + path.makeCacheable(); + + if (!device->format().stencilBufferSize()) { + // If there is no stencil buffer, triangulate the path instead. + + QRectF bbox = path.controlPointRect(); + // If the path doesn't fit within these limits, it is possible that the triangulation will fail. + bool withinLimits = (bbox.left() > -0x8000 * inverseScale) + && (bbox.right() < 0x8000 * inverseScale) + && (bbox.top() > -0x8000 * inverseScale) + && (bbox.bottom() < 0x8000 * inverseScale); + if (withinLimits) { + QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale)); + + QVarLengthArray<float> vertices(polys.vertices.size()); + for (int i = 0; i < polys.vertices.size(); ++i) + vertices[i] = float(inverseScale * polys.vertices.at(i)); + + prepareForDraw(currentBrush.isOpaque()); + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, vertices.constData()); + if (funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint)) + glDrawElements(GL_TRIANGLES, polys.indices.size(), GL_UNSIGNED_INT, polys.indices.data()); + else + glDrawElements(GL_TRIANGLES, polys.indices.size(), GL_UNSIGNED_SHORT, polys.indices.data()); + } else { + // We can't handle big, concave painter paths with OpenGL without stencil buffer. + qWarning("Painter path exceeds +/-32767 pixels."); + } + return; + } + + // The path is too complicated & needs the stencil technique + vertexCoordinateArray.clear(); + vertexCoordinateArray.addPath(path, inverseScale, false); + + fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill()); + + glStencilMask(0xff); + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + + if (q->state()->clipTestEnabled) { + // Pass when high bit is set, replace stencil value with current clip + glStencilFunc(GL_NOTEQUAL, q->state()->currentClip, GL_STENCIL_HIGH_BIT); + } else if (path.hasWindingFill()) { + // Pass when any bit is set, replace stencil value with 0 + glStencilFunc(GL_NOTEQUAL, 0, 0xff); + } else { + // Pass when high bit is set, replace stencil value with 0 + glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT); + } + prepareForDraw(currentBrush.isOpaque()); + + // Stencil the brush onto the dest buffer + composite(vertexCoordinateArray.boundingRect()); + glStencilMask(0); + updateClipScissorTest(); + } + } +} + + +void QOpenGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data, + int count, + int *stops, + int stopCount, + const QOpenGLRect &bounds, + StencilFillMode mode) +{ + Q_ASSERT(count || stops); + +// qDebug("QOpenGL2PaintEngineExPrivate::fillStencilWithVertexArray()"); + glStencilMask(0xff); // Enable stencil writes + + if (dirtyStencilRegion.intersects(currentScissorBounds)) { + QVector<QRect> clearRegion = dirtyStencilRegion.intersected(currentScissorBounds).rects(); + glClearStencil(0); // Clear to zero + for (int i = 0; i < clearRegion.size(); ++i) { +#ifndef QT_GL_NO_SCISSOR_TEST + setScissor(clearRegion.at(i)); +#endif + glClear(GL_STENCIL_BUFFER_BIT); + } + + dirtyStencilRegion -= currentScissorBounds; + +#ifndef QT_GL_NO_SCISSOR_TEST + updateClipScissorTest(); +#endif + } + + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes + useSimpleShader(); + glEnable(GL_STENCIL_TEST); // For some reason, this has to happen _after_ the simple shader is use()'d + + if (mode == WindingFillMode) { + Q_ASSERT(stops && !count); + if (q->state()->clipTestEnabled) { + // Flatten clip values higher than current clip, and set high bit to match current clip + glStencilFunc(GL_LEQUAL, GL_STENCIL_HIGH_BIT | q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + composite(bounds); + + glStencilFunc(GL_EQUAL, GL_STENCIL_HIGH_BIT, GL_STENCIL_HIGH_BIT); + } else if (!stencilClean) { + // Clear stencil buffer within bounding rect + glStencilFunc(GL_ALWAYS, 0, 0xff); + glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); + composite(bounds); + } + + // Inc. for front-facing triangle + funcs.glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP); + // Dec. for back-facing "holes" + funcs.glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_DECR_WRAP); + glStencilMask(~GL_STENCIL_HIGH_BIT); + drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN); + + if (q->state()->clipTestEnabled) { + // Clear high bit of stencil outside of path + glStencilFunc(GL_EQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + glStencilMask(GL_STENCIL_HIGH_BIT); + composite(bounds); + } + } else if (mode == OddEvenFillMode) { + glStencilMask(GL_STENCIL_HIGH_BIT); + glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit + drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN); + + } else { // TriStripStrokeFillMode + Q_ASSERT(count && !stops); // tristrips generated directly, so no vertexArray or stops + glStencilMask(GL_STENCIL_HIGH_BIT); +#if 0 + glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data); + glDrawArrays(GL_TRIANGLE_STRIP, 0, count); +#else + + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + if (q->state()->clipTestEnabled) { + glStencilFunc(GL_LEQUAL, q->state()->currentClip | GL_STENCIL_HIGH_BIT, + ~GL_STENCIL_HIGH_BIT); + } else { + glStencilFunc(GL_ALWAYS, GL_STENCIL_HIGH_BIT, 0xff); + } + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data); + glDrawArrays(GL_TRIANGLE_STRIP, 0, count); +#endif + } + + // Enable color writes & disable stencil writes + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + +/* + If the maximum value in the stencil buffer is GL_STENCIL_HIGH_BIT - 1, + restore the stencil buffer to a pristine state. The current clip region + is set to 1, and the rest to 0. +*/ +void QOpenGL2PaintEngineExPrivate::resetClipIfNeeded() +{ + if (maxClip != (GL_STENCIL_HIGH_BIT - 1)) + return; + + Q_Q(QOpenGL2PaintEngineEx); + + useSimpleShader(); + glEnable(GL_STENCIL_TEST); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + + QRectF bounds = q->state()->matrix.inverted().mapRect(QRectF(0, 0, width, height)); + QOpenGLRect rect(bounds.left(), bounds.top(), bounds.right(), bounds.bottom()); + + // Set high bit on clip region + glStencilFunc(GL_LEQUAL, q->state()->currentClip, 0xff); + glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT); + glStencilMask(GL_STENCIL_HIGH_BIT); + composite(rect); + + // Reset clipping to 1 and everything else to zero + glStencilFunc(GL_NOTEQUAL, 0x01, GL_STENCIL_HIGH_BIT); + glStencilOp(GL_ZERO, GL_REPLACE, GL_REPLACE); + glStencilMask(0xff); + composite(rect); + + q->state()->currentClip = 1; + q->state()->canRestoreClip = false; + + maxClip = 1; + + glStencilMask(0x0); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + +bool QOpenGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) +{ + if (brushTextureDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode) + updateBrushTexture(); + + if (compositionModeDirty) + updateCompositionMode(); + + if (matrixDirty) + updateMatrix(); + + const bool stateHasOpacity = q->state()->opacity < 0.99f; + if (q->state()->composition_mode == QPainter::CompositionMode_Source + || (q->state()->composition_mode == QPainter::CompositionMode_SourceOver + && srcPixelsAreOpaque && !stateHasOpacity)) + { + glDisable(GL_BLEND); + } else { + glEnable(GL_BLEND); + } + + QOpenGLEngineShaderManager::OpacityMode opacityMode; + if (mode == ImageArrayDrawingMode) { + opacityMode = QOpenGLEngineShaderManager::AttributeOpacity; + } else { + opacityMode = stateHasOpacity ? QOpenGLEngineShaderManager::UniformOpacity + : QOpenGLEngineShaderManager::NoOpacity; + if (stateHasOpacity && (mode != ImageDrawingMode)) { + // Using a brush + bool brushIsPattern = (currentBrush.style() >= Qt::Dense1Pattern) && + (currentBrush.style() <= Qt::DiagCrossPattern); + + if ((currentBrush.style() == Qt::SolidPattern) || brushIsPattern) + opacityMode = QOpenGLEngineShaderManager::NoOpacity; // Global opacity handled by srcPixel shader + } + } + shaderManager->setOpacityMode(opacityMode); + + bool changed = shaderManager->useCorrectShaderProg(); + // If the shader program needs changing, we change it and mark all uniforms as dirty + if (changed) { + // The shader program has changed so mark all uniforms as dirty: + brushUniformsDirty = true; + opacityUniformDirty = true; + matrixUniformDirty = true; + } + + if (brushUniformsDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode) + updateBrushUniforms(); + + if (opacityMode == QOpenGLEngineShaderManager::UniformOpacity && opacityUniformDirty) { + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::GlobalOpacity), (GLfloat)q->state()->opacity); + opacityUniformDirty = false; + } + + if (matrixUniformDirty && shaderManager->hasComplexGeometry()) { + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Matrix), + pmvMatrix); + matrixUniformDirty = false; + } + + return changed; +} + +void QOpenGL2PaintEngineExPrivate::composite(const QOpenGLRect& boundingRect) +{ + setCoords(staticVertexCoordinateArray, boundingRect); + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + +// Draws the vertex array as a set of <vertexArrayStops.size()> triangle fans. +void QOpenGL2PaintEngineExPrivate::drawVertexArrays(const float *data, int *stops, int stopCount, + GLenum primitive) +{ + // Now setup the pointer to the vertex array: + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)data); + + int previousStop = 0; + for (int i=0; i<stopCount; ++i) { + int stop = stops[i]; +/* + qDebug("Drawing triangle fan for vertecies %d -> %d:", previousStop, stop-1); + for (int i=previousStop; i<stop; ++i) + qDebug(" %02d: [%.2f, %.2f]", i, vertexArray.data()[i].x, vertexArray.data()[i].y); +*/ + glDrawArrays(primitive, previousStop, stop - previousStop); + previousStop = stop; + } +} + +/////////////////////////////////// Public Methods ////////////////////////////////////////// + +QOpenGL2PaintEngineEx::QOpenGL2PaintEngineEx() + : QPaintEngineEx(*(new QOpenGL2PaintEngineExPrivate(this))) +{ +} + +QOpenGL2PaintEngineEx::~QOpenGL2PaintEngineEx() +{ +} + +void QOpenGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush) +{ + Q_D(QOpenGL2PaintEngineEx); + + if (qbrush_style(brush) == Qt::NoBrush) + return; + ensureActive(); + d->setBrush(brush); + d->fill(path); +} + +Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp + + +void QOpenGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) +{ + Q_D(QOpenGL2PaintEngineEx); + + const QBrush &penBrush = qpen_brush(pen); + if (qpen_style(pen) == Qt::NoPen || qbrush_style(penBrush) == Qt::NoBrush) + return; + + QOpenGL2PaintEngineState *s = state(); + if (pen.isCosmetic() && !qt_scaleForTransform(s->transform(), 0)) { + // QTriangulatingStroker class is not meant to support cosmetically sheared strokes. + QPaintEngineEx::stroke(path, pen); + return; + } + + ensureActive(); + d->setBrush(penBrush); + d->stroke(path, pen); +} + +void QOpenGL2PaintEngineExPrivate::stroke(const QVectorPath &path, const QPen &pen) +{ + const QOpenGL2PaintEngineState *s = q->state(); + if (snapToPixelGrid) { + snapToPixelGrid = false; + matrixDirty = true; + } + + const Qt::PenStyle penStyle = qpen_style(pen); + const QBrush &penBrush = qpen_brush(pen); + const bool opaque = penBrush.isOpaque() && s->opacity > 0.99; + + transferMode(BrushDrawingMode); + + // updateMatrix() is responsible for setting the inverse scale on + // the strokers, so we need to call it here and not wait for + // prepareForDraw() down below. + updateMatrix(); + + QRectF clip = q->state()->matrix.inverted().mapRect(q->state()->clipEnabled + ? q->state()->rectangleClip + : QRectF(0, 0, width, height)); + + if (penStyle == Qt::SolidLine) { + stroker.process(path, pen, clip); + + } else { // Some sort of dash + dasher.process(path, pen, clip); + + QVectorPath dashStroke(dasher.points(), + dasher.elementCount(), + dasher.elementTypes()); + stroker.process(dashStroke, pen, clip); + } + + if (!stroker.vertexCount()) + return; + + if (opaque) { + prepareForDraw(opaque); + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, stroker.vertices()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, stroker.vertexCount() / 2); + +// QBrush b(Qt::green); +// d->setBrush(&b); +// d->prepareForDraw(true); +// glDrawArrays(GL_LINE_STRIP, 0, d->stroker.vertexCount() / 2); + + } else { + qreal width = qpen_widthf(pen) / 2; + if (width == 0) + width = 0.5; + qreal extra = pen.joinStyle() == Qt::MiterJoin + ? qMax(pen.miterLimit() * width, width) + : width; + + if (pen.isCosmetic()) + extra = extra * inverseScale; + + QRectF bounds = path.controlPointRect().adjusted(-extra, -extra, extra, extra); + + fillStencilWithVertexArray(stroker.vertices(), stroker.vertexCount() / 2, + 0, 0, bounds, QOpenGL2PaintEngineExPrivate::TriStripStrokeFillMode); + + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + + // Pass when any bit is set, replace stencil value with 0 + glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT); + prepareForDraw(false); + + // Stencil the brush onto the dest buffer + composite(bounds); + + glStencilMask(0); + + updateClipScissorTest(); + } +} + +void QOpenGL2PaintEngineEx::penChanged() { } +void QOpenGL2PaintEngineEx::brushChanged() { } +void QOpenGL2PaintEngineEx::brushOriginChanged() { } + +void QOpenGL2PaintEngineEx::opacityChanged() +{ +// qDebug("QOpenGL2PaintEngineEx::opacityChanged()"); + Q_D(QOpenGL2PaintEngineEx); + state()->opacityChanged = true; + + Q_ASSERT(d->shaderManager); + d->brushUniformsDirty = true; + d->opacityUniformDirty = true; +} + +void QOpenGL2PaintEngineEx::compositionModeChanged() +{ +// qDebug("QOpenGL2PaintEngineEx::compositionModeChanged()"); + Q_D(QOpenGL2PaintEngineEx); + state()->compositionModeChanged = true; + d->compositionModeDirty = true; +} + +void QOpenGL2PaintEngineEx::renderHintsChanged() +{ + state()->renderHintsChanged = true; + +#if !defined(QT_OPENGL_ES_2) + if ((state()->renderHints & QPainter::Antialiasing) + || (state()->renderHints & QPainter::HighQualityAntialiasing)) + glEnable(GL_MULTISAMPLE); + else + glDisable(GL_MULTISAMPLE); +#endif + + Q_D(QOpenGL2PaintEngineEx); + d->lastTextureUsed = GLuint(-1); + d->brushTextureDirty = true; +// qDebug("QOpenGL2PaintEngineEx::renderHintsChanged() not implemented!"); +} + +void QOpenGL2PaintEngineEx::transformChanged() +{ + Q_D(QOpenGL2PaintEngineEx); + d->matrixDirty = true; + state()->matrixChanged = true; +} + + +static const QRectF scaleRect(const QRectF &r, qreal sx, qreal sy) +{ + return QRectF(r.x() * sx, r.y() * sy, r.width() * sx, r.height() * sy); +} + +void QOpenGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, const QRectF & src) +{ + Q_D(QOpenGL2PaintEngineEx); + QOpenGLContext *ctx = d->ctx; + + int max_texture_size = ctx->d_func()->maxTextureSize(); + if (pixmap.width() > max_texture_size || pixmap.height() > max_texture_size) { + QPixmap scaled = pixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio); + + const qreal sx = scaled.width() / qreal(pixmap.width()); + const qreal sy = scaled.height() / qreal(pixmap.height()); + + drawPixmap(dest, scaled, scaleRect(src, sx, sy)); + return; + } + + ensureActive(); + d->transferMode(ImageDrawingMode); + + d->funcs.glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); + QOpenGLTexture *texture = 0; +// ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, bindOptions); + + GLfloat top = texture->invertedY() ? (pixmap.height() - src.top()) : src.top(); + GLfloat bottom = texture->invertedY() ? (pixmap.height() - src.bottom()) : src.bottom(); + QOpenGLRect srcRect(src.left(), top, src.right(), bottom); + + bool isBitmap = pixmap.isQBitmap(); + bool isOpaque = !isBitmap && !pixmap.hasAlpha(); + + d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, + state()->renderHints & QPainter::SmoothPixmapTransform, texture->id()); + d->drawTexture(dest, srcRect, pixmap.size(), isOpaque, isBitmap); +} + +void QOpenGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src, + Qt::ImageConversionFlags) +{ + Q_D(QOpenGL2PaintEngineEx); + QOpenGLContext *ctx = d->ctx; + + int max_texture_size = ctx->d_func()->maxTextureSize(); + if (image.width() > max_texture_size || image.height() > max_texture_size) { + QImage scaled = image.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio); + + const qreal sx = scaled.width() / qreal(image.width()); + const qreal sy = scaled.height() / qreal(image.height()); + + drawImage(dest, scaled, scaleRect(src, sx, sy)); + return; + } + + ensureActive(); + d->transferMode(ImageDrawingMode); + + d->funcs.glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); + + QOpenGLTexture *texture = 0;//ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, bindOptions); + GLuint id = texture->id(); + + d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, + state()->renderHints & QPainter::SmoothPixmapTransform, id); + d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel()); +} + +void QOpenGL2PaintEngineEx::drawStaticTextItem(QStaticTextItem *textItem) +{ + Q_D(QOpenGL2PaintEngineEx); + + ensureActive(); + + QPainterState *s = state(); + float det = s->matrix.determinant(); + + // don't try to cache huge fonts or vastly transformed fonts + QFontEngine *fontEngine = textItem->fontEngine(); + const qreal pixelSize = fontEngine->fontDef.pixelSize; + if (shouldDrawCachedGlyphs(pixelSize, s->matrix) || det < 0.25f || det > 4.f) { + QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 + ? QFontEngineGlyphCache::Type(textItem->fontEngine()->glyphFormat) + : d->glyphCacheType; + if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) { + if (d->device->alphaRequested() || s->matrix.type() > QTransform::TxTranslate + || (s->composition_mode != QPainter::CompositionMode_Source + && s->composition_mode != QPainter::CompositionMode_SourceOver)) + { + glyphType = QFontEngineGlyphCache::Raster_A8; + } + } + + d->drawCachedGlyphs(glyphType, textItem); + } else { + QPaintEngineEx::drawStaticTextItem(textItem); + } +} + +bool QOpenGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src) +{ + Q_D(QOpenGL2PaintEngineEx); + if (!d->shaderManager) + return false; + + ensureActive(); + d->transferMode(ImageDrawingMode); + + d->funcs.glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); + glBindTexture(GL_TEXTURE_2D, textureId); + + QOpenGLRect srcRect(src.left(), src.bottom(), src.right(), src.top()); + + d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, + state()->renderHints & QPainter::SmoothPixmapTransform, textureId); + d->drawTexture(dest, srcRect, size, false); + return true; +} + +void QOpenGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem) +{ + Q_D(QOpenGL2PaintEngineEx); + + ensureActive(); + QOpenGL2PaintEngineState *s = state(); + + const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); + + QTransform::TransformationType txtype = s->matrix.type(); + + float det = s->matrix.determinant(); + bool drawCached = txtype < QTransform::TxProject; + + // don't try to cache huge fonts or vastly transformed fonts + const qreal pixelSize = ti.fontEngine->fontDef.pixelSize; + if (shouldDrawCachedGlyphs(pixelSize, s->matrix) || det < 0.25f || det > 4.f) + drawCached = false; + + QFontEngineGlyphCache::Type glyphType = ti.fontEngine->glyphFormat >= 0 + ? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat) + : d->glyphCacheType; + + + if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) { + if (d->device->alphaRequested() || txtype > QTransform::TxTranslate + || (state()->composition_mode != QPainter::CompositionMode_Source + && state()->composition_mode != QPainter::CompositionMode_SourceOver)) + { + glyphType = QFontEngineGlyphCache::Raster_A8; + } + } + + if (drawCached) { + QVarLengthArray<QFixedPoint> positions; + QVarLengthArray<glyph_t> glyphs; + QTransform matrix = QTransform::fromTranslate(p.x(), p.y()); + ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); + + { + QStaticTextItem staticTextItem; + staticTextItem.chars = const_cast<QChar *>(ti.chars); + staticTextItem.setFontEngine(ti.fontEngine); + staticTextItem.glyphs = glyphs.data(); + staticTextItem.numChars = ti.num_chars; + staticTextItem.numGlyphs = glyphs.size(); + staticTextItem.glyphPositions = positions.data(); + + d->drawCachedGlyphs(glyphType, &staticTextItem); + } + return; + } + + QPaintEngineEx::drawTextItem(p, ti); +} + +namespace { + + class QOpenGLStaticTextUserData: public QStaticTextUserData + { + public: + QOpenGLStaticTextUserData() + : QStaticTextUserData(OpenGLUserData), cacheSize(0, 0), cacheSerialNumber(0) + { + } + + ~QOpenGLStaticTextUserData() + { + } + + QSize cacheSize; + QOpenGL2PEXVertexArray vertexCoordinateArray; + QOpenGL2PEXVertexArray textureCoordinateArray; + QFontEngineGlyphCache::Type glyphType; + int cacheSerialNumber; + }; + +} + +#if defined(Q_WS_WIN) +static bool fontSmoothingApproximately(qreal target) +{ + extern Q_GUI_EXPORT qreal qt_fontsmoothing_gamma; // qapplication_win.cpp + return (qAbs(qt_fontsmoothing_gamma - target) < 0.2); +} +#endif + +// #define QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO + +void QOpenGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyphType, + QStaticTextItem *staticTextItem) +{ + Q_Q(QOpenGL2PaintEngineEx); + + QOpenGL2PaintEngineState *s = q->state(); + + void *cacheKey = ctx->shareGroup(); + bool recreateVertexArrays = false; + + QOpenGLTextureGlyphCache *cache = + (QOpenGLTextureGlyphCache *) staticTextItem->fontEngine()->glyphCache(cacheKey, glyphType, QTransform()); + if (!cache || cache->cacheType() != glyphType || cache->contextGroup() == 0) { + cache = new QOpenGLTextureGlyphCache(glyphType, QTransform()); + staticTextItem->fontEngine()->setGlyphCache(cacheKey, cache); + recreateVertexArrays = true; + } + + if (staticTextItem->userDataNeedsUpdate) { + recreateVertexArrays = true; + } else if (staticTextItem->userData() == 0) { + recreateVertexArrays = true; + } else if (staticTextItem->userData()->type != QStaticTextUserData::OpenGLUserData) { + recreateVertexArrays = true; + } else { + QOpenGLStaticTextUserData *userData = static_cast<QOpenGLStaticTextUserData *>(staticTextItem->userData()); + if (userData->glyphType != glyphType) { + recreateVertexArrays = true; + } else if (userData->cacheSerialNumber != cache->serialNumber()) { + recreateVertexArrays = true; + } + } + + // We only need to update the cache with new glyphs if we are actually going to recreate the vertex arrays. + // If the cache size has changed, we do need to regenerate the vertices, but we don't need to repopulate the + // cache so this text is performed before we test if the cache size has changed. + if (recreateVertexArrays) { + cache->setPaintEnginePrivate(this); + if (!cache->populate(staticTextItem->fontEngine(), staticTextItem->numGlyphs, + staticTextItem->glyphs, staticTextItem->glyphPositions)) { + // No space for glyphs in cache. We need to reset it and try again. + cache->clear(); + cache->populate(staticTextItem->fontEngine(), staticTextItem->numGlyphs, + staticTextItem->glyphs, staticTextItem->glyphPositions); + } + cache->fillInPendingGlyphs(); + } + + if (cache->width() == 0 || cache->height() == 0) + return; + + transferMode(TextDrawingMode); + + int margin = cache->glyphMargin(); + + GLfloat dx = 1.0 / cache->width(); + GLfloat dy = 1.0 / cache->height(); + + // Use global arrays by default + QOpenGL2PEXVertexArray *vertexCoordinates = &vertexCoordinateArray; + QOpenGL2PEXVertexArray *textureCoordinates = &textureCoordinateArray; + + if (staticTextItem->useBackendOptimizations) { + QOpenGLStaticTextUserData *userData = 0; + + if (staticTextItem->userData() == 0 + || staticTextItem->userData()->type != QStaticTextUserData::OpenGLUserData) { + + userData = new QOpenGLStaticTextUserData(); + staticTextItem->setUserData(userData); + + } else { + userData = static_cast<QOpenGLStaticTextUserData*>(staticTextItem->userData()); + } + + userData->glyphType = glyphType; + userData->cacheSerialNumber = cache->serialNumber(); + + // Use cache if backend optimizations is turned on + vertexCoordinates = &userData->vertexCoordinateArray; + textureCoordinates = &userData->textureCoordinateArray; + + QSize size(cache->width(), cache->height()); + if (userData->cacheSize != size) { + recreateVertexArrays = true; + userData->cacheSize = size; + } + } + + if (recreateVertexArrays) { + vertexCoordinates->clear(); + textureCoordinates->clear(); + + bool supportsSubPixelPositions = staticTextItem->fontEngine()->supportsSubPixelPositions(); + for (int i=0; i<staticTextItem->numGlyphs; ++i) { + QFixed subPixelPosition; + if (supportsSubPixelPositions) + subPixelPosition = cache->subPixelPositionForX(staticTextItem->glyphPositions[i].x); + + QTextureGlyphCache::GlyphAndSubPixelPosition glyph(staticTextItem->glyphs[i], subPixelPosition); + + const QTextureGlyphCache::Coord &c = cache->coords[glyph]; + if (c.isNull()) + continue; + + int x = qFloor(staticTextItem->glyphPositions[i].x) + c.baseLineX - margin; + int y = qFloor(staticTextItem->glyphPositions[i].y) - c.baseLineY - margin; + + vertexCoordinates->addQuad(QRectF(x, y, c.w, c.h)); + textureCoordinates->addQuad(QRectF(c.x*dx, c.y*dy, c.w * dx, c.h * dy)); + } + + staticTextItem->userDataNeedsUpdate = false; + } + + int numGlyphs = vertexCoordinates->vertexCount() / 4; + if (numGlyphs == 0) + return; + + if (elementIndices.size() < numGlyphs*6) { + Q_ASSERT(elementIndices.size() % 6 == 0); + int j = elementIndices.size() / 6 * 4; + while (j < numGlyphs*4) { + elementIndices.append(j + 0); + elementIndices.append(j + 0); + elementIndices.append(j + 1); + elementIndices.append(j + 2); + elementIndices.append(j + 3); + elementIndices.append(j + 3); + + j += 4; + } + +#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO) + if (elementIndicesVBOId == 0) + glGenBuffers(1, &elementIndicesVBOId); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementIndices.size() * sizeof(GLushort), + elementIndices.constData(), GL_STATIC_DRAW); +#endif + } else { +#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId); +#endif + } + + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data()); + setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data()); + + if (!snapToPixelGrid) { + snapToPixelGrid = true; + matrixDirty = true; + } + + QBrush pensBrush = q->state()->pen.brush(); + setBrush(pensBrush); + + if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) { + + // Subpixel antialiasing without gamma correction + + QPainter::CompositionMode compMode = q->state()->composition_mode; + Q_ASSERT(compMode == QPainter::CompositionMode_Source + || compMode == QPainter::CompositionMode_SourceOver); + + shaderManager->setMaskType(QOpenGLEngineShaderManager::SubPixelMaskPass1); + + if (pensBrush.style() == Qt::SolidPattern) { + // Solid patterns can get away with only one pass. + QColor c = pensBrush.color(); + qreal oldOpacity = q->state()->opacity; + if (compMode == QPainter::CompositionMode_Source) { + c = qt_premultiplyColor(c, q->state()->opacity); + q->state()->opacity = 1; + opacityUniformDirty = true; + } + + compositionModeDirty = false; // I can handle this myself, thank you very much + prepareForDraw(false); // Text always causes src pixels to be transparent + + // prepareForDraw() have set the opacity on the current shader, so the opacity state can now be reset. + if (compMode == QPainter::CompositionMode_Source) { + q->state()->opacity = oldOpacity; + opacityUniformDirty = true; + } + + glEnable(GL_BLEND); + glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR); + funcs.glBlendColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()); + } else { + // Other brush styles need two passes. + + qreal oldOpacity = q->state()->opacity; + if (compMode == QPainter::CompositionMode_Source) { + q->state()->opacity = 1; + opacityUniformDirty = true; + pensBrush = Qt::white; + setBrush(pensBrush); + } + + compositionModeDirty = false; // I can handle this myself, thank you very much + prepareForDraw(false); // Text always causes src pixels to be transparent + glEnable(GL_BLEND); + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + + funcs.glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT); + glBindTexture(GL_TEXTURE_2D, cache->texture()); + updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false); + +#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO) + glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0); +#else + glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data()); +#endif + + shaderManager->setMaskType(QOpenGLEngineShaderManager::SubPixelMaskPass2); + + if (compMode == QPainter::CompositionMode_Source) { + q->state()->opacity = oldOpacity; + opacityUniformDirty = true; + pensBrush = q->state()->pen.brush(); + setBrush(pensBrush); + } + + compositionModeDirty = false; + prepareForDraw(false); // Text always causes src pixels to be transparent + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + } + compositionModeDirty = true; + } else { + // Greyscale/mono glyphs + + shaderManager->setMaskType(QOpenGLEngineShaderManager::PixelMask); + prepareForDraw(false); // Text always causes src pixels to be transparent + } + + QOpenGLTextureGlyphCache::FilterMode filterMode = (s->matrix.type() > QTransform::TxTranslate)?QOpenGLTextureGlyphCache::Linear:QOpenGLTextureGlyphCache::Nearest; + if (lastMaskTextureUsed != cache->texture() || cache->filterMode() != filterMode) { + + funcs.glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT); + if (lastMaskTextureUsed != cache->texture()) { + glBindTexture(GL_TEXTURE_2D, cache->texture()); + lastMaskTextureUsed = cache->texture(); + } + + if (cache->filterMode() != filterMode) { + if (filterMode == QOpenGLTextureGlyphCache::Linear) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + cache->setFilterMode(filterMode); + } + } + + bool srgbFrameBufferEnabled = false; + if (funcs.hasOpenGLExtension(QOpenGLExtensions::SRGBFrameBuffer)) { +#if defined(Q_WS_MAC) + if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) +#elif defined(Q_WS_WIN) + if (glyphType != QFontEngineGlyphCache::Raster_RGBMask || fontSmoothingApproximately(2.1)) +#else + if (false) +#endif + { + glEnable(GL_FRAMEBUFFER_SRGB); + srgbFrameBufferEnabled = true; + } + } + +#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO) + glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +#else + glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data()); +#endif + + if (srgbFrameBufferEnabled) + glDisable(GL_FRAMEBUFFER_SRGB); + +} + +void QOpenGL2PaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, + QPainter::PixmapFragmentHints hints) +{ + Q_D(QOpenGL2PaintEngineEx); + // Use fallback for extended composition modes. + if (state()->composition_mode > QPainter::CompositionMode_Plus) { + QPaintEngineEx::drawPixmapFragments(fragments, fragmentCount, pixmap, hints); + return; + } + + ensureActive(); + int max_texture_size = d->ctx->d_func()->maxTextureSize(); + if (pixmap.width() > max_texture_size || pixmap.height() > max_texture_size) { + QPixmap scaled = pixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio); + d->drawPixmapFragments(fragments, fragmentCount, scaled, hints); + } else { + d->drawPixmapFragments(fragments, fragmentCount, pixmap, hints); + } +} + + +void QOpenGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragment *fragments, + int fragmentCount, const QPixmap &pixmap, + QPainter::PixmapFragmentHints hints) +{ + GLfloat dx = 1.0f / pixmap.size().width(); + GLfloat dy = 1.0f / pixmap.size().height(); + + vertexCoordinateArray.clear(); + textureCoordinateArray.clear(); + opacityArray.reset(); + + if (snapToPixelGrid) { + snapToPixelGrid = false; + matrixDirty = true; + } + + bool allOpaque = true; + + for (int i = 0; i < fragmentCount; ++i) { + qreal s = 0; + qreal c = 1; + if (fragments[i].rotation != 0) { + s = qFastSin(fragments[i].rotation * Q_PI / 180); + c = qFastCos(fragments[i].rotation * Q_PI / 180); + } + + qreal right = 0.5 * fragments[i].scaleX * fragments[i].width; + qreal bottom = 0.5 * fragments[i].scaleY * fragments[i].height; + QOpenGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c); + QOpenGLPoint bottomLeft(-right * c - bottom * s, -right * s + bottom * c); + + vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y); + vertexCoordinateArray.addVertex(-bottomLeft.x + fragments[i].x, -bottomLeft.y + fragments[i].y); + vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y); + vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y); + vertexCoordinateArray.addVertex(bottomLeft.x + fragments[i].x, bottomLeft.y + fragments[i].y); + vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y); + + QOpenGLRect src(fragments[i].sourceLeft * dx, fragments[i].sourceTop * dy, + (fragments[i].sourceLeft + fragments[i].width) * dx, + (fragments[i].sourceTop + fragments[i].height) * dy); + + textureCoordinateArray.addVertex(src.right, src.bottom); + textureCoordinateArray.addVertex(src.right, src.top); + textureCoordinateArray.addVertex(src.left, src.top); + textureCoordinateArray.addVertex(src.left, src.top); + textureCoordinateArray.addVertex(src.left, src.bottom); + textureCoordinateArray.addVertex(src.right, src.bottom); + + qreal opacity = fragments[i].opacity * q->state()->opacity; + opacityArray << opacity << opacity << opacity << opacity << opacity << opacity; + allOpaque &= (opacity >= 0.99f); + } + + funcs.glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); + QOpenGLTexture *texture = 0;//ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, + // QOpenGLContext::InternalBindOption + // | QOpenGLContext::CanFlipNativePixmapBindOption); + + if (texture->invertedY()) { + // Flip texture y-coordinate. + QOpenGLPoint *data = textureCoordinateArray.data(); + for (int i = 0; i < 6 * fragmentCount; ++i) + data[i].y = 1 - data[i].y; + } + + transferMode(ImageArrayDrawingMode); + + bool isBitmap = pixmap.isQBitmap(); + bool isOpaque = !isBitmap && (!pixmap.hasAlpha() || (hints & QPainter::OpaqueHint)) && allOpaque; + + updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, + q->state()->renderHints & QPainter::SmoothPixmapTransform, texture->id()); + + // Setup for texture drawing + currentBrush = noBrush; + shaderManager->setSrcPixelType(isBitmap ? QOpenGLEngineShaderManager::PatternSrc + : QOpenGLEngineShaderManager::ImageSrc); + if (prepareForDraw(isOpaque)) + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT); + + if (isBitmap) { + QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col); + } + + glDrawArrays(GL_TRIANGLES, 0, 6 * fragmentCount); +} + +bool QOpenGL2PaintEngineEx::begin(QPaintDevice *pdev) +{ + Q_D(QOpenGL2PaintEngineEx); + +// qDebug("QOpenGL2PaintEngineEx::begin()"); + if (pdev->devType() == QInternal::OpenGL) + d->device = static_cast<QOpenGLPaintDevice*>(pdev); + else + d->device = QOpenGLPaintDevice::getDevice(pdev); + + if (!d->device) + return false; + + if (d->device->group() != QOpenGLContextGroup::currentContextGroup()) { + qWarning("QPainter::begin(): OpenGL resource not valid in current context"); + return false; + } + + d->ctx = QOpenGLContext::currentContext(); + d->ctx->d_func()->active_engine = this; + + d->funcs.initializeGLFunctions(); + + for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i) + d->vertexAttributeArraysEnabledState[i] = false; + + const QSize sz = d->device->size(); + d->width = sz.width(); + d->height = sz.height(); + d->mode = BrushDrawingMode; + d->brushTextureDirty = true; + d->brushUniformsDirty = true; + d->matrixUniformDirty = true; + d->matrixDirty = true; + d->compositionModeDirty = true; + d->opacityUniformDirty = true; + d->needsSync = true; + d->useSystemClip = !systemClip().isEmpty(); + d->currentBrush = QBrush(); + + d->dirtyStencilRegion = QRect(0, 0, d->width, d->height); + d->stencilClean = true; + + // Calling begin paint should make the correct context current. So, any + // code which calls into GL or otherwise needs a current context *must* + // go after beginPaint: + d->device->beginPaint(); + d->shaderManager = new QOpenGLEngineShaderManager(d->ctx); + + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + +#if !defined(QT_OPENGL_ES_2) + glDisable(GL_MULTISAMPLE); +#endif + + d->glyphCacheType = QFontEngineGlyphCache::Raster_A8; + +#if !defined(QT_OPENGL_ES_2) +#if defined(Q_WS_WIN) + if (qt_cleartype_enabled + && (fontSmoothingApproximately(1.0) || fontSmoothingApproximately(2.1))) +#endif +#if defined(Q_WS_MAC) + if (qt_applefontsmoothing_enabled) +#endif + d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask; +#endif + +#if defined(QT_OPENGL_ES_2) + // OpenGL ES can't switch MSAA off, so if the gl paint device is + // multisampled, it's always multisampled. + d->multisamplingAlwaysEnabled = d->device->format().samples() > 1; +#else + d->multisamplingAlwaysEnabled = false; +#endif + + return true; +} + +bool QOpenGL2PaintEngineEx::end() +{ + Q_D(QOpenGL2PaintEngineEx); + + QOpenGLContext *ctx = d->ctx; + d->funcs.glUseProgram(0); + d->transferMode(BrushDrawingMode); + d->device->endPaint(); + + ctx->d_func()->active_engine = 0; + + d->resetGLState(); + + delete d->shaderManager; + d->shaderManager = 0; + d->currentBrush = QBrush(); + +#ifdef QT_OPENGL_CACHE_AS_VBOS + if (!d->unusedVBOSToClean.isEmpty()) { + glDeleteBuffers(d->unusedVBOSToClean.size(), d->unusedVBOSToClean.constData()); + d->unusedVBOSToClean.clear(); + } + if (!d->unusedIBOSToClean.isEmpty()) { + glDeleteBuffers(d->unusedIBOSToClean.size(), d->unusedIBOSToClean.constData()); + d->unusedIBOSToClean.clear(); + } +#endif + + return false; +} + +void QOpenGL2PaintEngineEx::ensureActive() +{ + Q_D(QOpenGL2PaintEngineEx); + QOpenGLContext *ctx = d->ctx; + + if (isActive() && ctx->d_func()->active_engine != this) { + ctx->d_func()->active_engine = this; + d->needsSync = true; + } + + d->device->ensureActiveTarget(); + + if (d->needsSync) { + d->transferMode(BrushDrawingMode); + glViewport(0, 0, d->width, d->height); + d->needsSync = false; + d->lastMaskTextureUsed = 0; + d->shaderManager->setDirty(); + d->syncGlState(); + for (int i = 0; i < 3; ++i) + d->vertexAttribPointers[i] = (GLfloat*)-1; // Assume the pointers are clobbered + setState(state()); + } +} + +void QOpenGL2PaintEngineExPrivate::updateClipScissorTest() +{ + Q_Q(QOpenGL2PaintEngineEx); + if (q->state()->clipTestEnabled) { + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); + } else { + glDisable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0xff); + } + +#ifdef QT_GL_NO_SCISSOR_TEST + currentScissorBounds = QRect(0, 0, width, height); +#else + QRect bounds = q->state()->rectangleClip; + if (!q->state()->clipEnabled) { + if (useSystemClip) + bounds = systemClip.boundingRect(); + else + bounds = QRect(0, 0, width, height); + } else { + if (useSystemClip) + bounds = bounds.intersected(systemClip.boundingRect()); + else + bounds = bounds.intersected(QRect(0, 0, width, height)); + } + + currentScissorBounds = bounds; + + if (bounds == QRect(0, 0, width, height)) { + glDisable(GL_SCISSOR_TEST); + } else { + glEnable(GL_SCISSOR_TEST); + setScissor(bounds); + } +#endif +} + +void QOpenGL2PaintEngineExPrivate::setScissor(const QRect &rect) +{ + const int left = rect.left(); + const int width = rect.width(); + int bottom = height - (rect.top() + rect.height()); + if (device->isFlipped()) { + bottom = rect.top(); + } + const int height = rect.height(); + + glScissor(left, bottom, width, height); +} + +void QOpenGL2PaintEngineEx::clipEnabledChanged() +{ + Q_D(QOpenGL2PaintEngineEx); + + state()->clipChanged = true; + + if (painter()->hasClipping()) + d->regenerateClip(); + else + d->systemStateChanged(); +} + +void QOpenGL2PaintEngineExPrivate::clearClip(uint value) +{ + dirtyStencilRegion -= currentScissorBounds; + + glStencilMask(0xff); + glClearStencil(value); + glClear(GL_STENCIL_BUFFER_BIT); + glStencilMask(0x0); + + q->state()->needsClipBufferClear = false; +} + +void QOpenGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint value) +{ + transferMode(BrushDrawingMode); + + if (snapToPixelGrid) { + snapToPixelGrid = false; + matrixDirty = true; + } + + if (matrixDirty) + updateMatrix(); + + stencilClean = false; + + const bool singlePass = !path.hasWindingFill() + && (((q->state()->currentClip == maxClip - 1) && q->state()->clipTestEnabled) + || q->state()->needsClipBufferClear); + const uint referenceClipValue = q->state()->needsClipBufferClear ? 1 : q->state()->currentClip; + + if (q->state()->needsClipBufferClear) + clearClip(1); + + if (path.isEmpty()) { + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT); + return; + } + + if (q->state()->clipTestEnabled) + glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); + else + glStencilFunc(GL_ALWAYS, 0, 0xff); + + vertexCoordinateArray.clear(); + vertexCoordinateArray.addPath(path, inverseScale, false); + + if (!singlePass) + fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill()); + + glColorMask(false, false, false, false); + glEnable(GL_STENCIL_TEST); + useSimpleShader(); + + if (singlePass) { + // Under these conditions we can set the new stencil value in a single + // pass, by using the current value and the "new value" as the toggles + + glStencilFunc(GL_LEQUAL, referenceClipValue, ~GL_STENCIL_HIGH_BIT); + glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT); + glStencilMask(value ^ referenceClipValue); + + drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN); + } else { + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + glStencilMask(0xff); + + if (!q->state()->clipTestEnabled && path.hasWindingFill()) { + // Pass when any clip bit is set, set high bit + glStencilFunc(GL_NOTEQUAL, GL_STENCIL_HIGH_BIT, ~GL_STENCIL_HIGH_BIT); + composite(vertexCoordinateArray.boundingRect()); + } + + // Pass when high bit is set, replace stencil value with new clip value + glStencilFunc(GL_NOTEQUAL, value, GL_STENCIL_HIGH_BIT); + + composite(vertexCoordinateArray.boundingRect()); + } + + glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT); + glStencilMask(0); + + glColorMask(true, true, true, true); +} + +void QOpenGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op) +{ +// qDebug("QOpenGL2PaintEngineEx::clip()"); + Q_D(QOpenGL2PaintEngineEx); + + state()->clipChanged = true; + + ensureActive(); + + if (op == Qt::ReplaceClip) { + op = Qt::IntersectClip; + if (d->hasClipOperations()) { + d->systemStateChanged(); + state()->canRestoreClip = false; + } + } + +#ifndef QT_GL_NO_SCISSOR_TEST + if (!path.isEmpty() && op == Qt::IntersectClip && (path.shape() == QVectorPath::RectangleHint)) { + const QPointF* const points = reinterpret_cast<const QPointF*>(path.points()); + QRectF rect(points[0], points[2]); + + if (state()->matrix.type() <= QTransform::TxScale + || (state()->matrix.type() == QTransform::TxRotate + && qFuzzyIsNull(state()->matrix.m11()) + && qFuzzyIsNull(state()->matrix.m22()))) + { + state()->rectangleClip = state()->rectangleClip.intersected(state()->matrix.mapRect(rect).toRect()); + d->updateClipScissorTest(); + return; + } + } +#endif + + const QRect pathRect = state()->matrix.mapRect(path.controlPointRect()).toAlignedRect(); + + switch (op) { + case Qt::NoClip: + if (d->useSystemClip) { + state()->clipTestEnabled = true; + state()->currentClip = 1; + } else { + state()->clipTestEnabled = false; + } + state()->rectangleClip = QRect(0, 0, d->width, d->height); + state()->canRestoreClip = false; + d->updateClipScissorTest(); + break; + case Qt::IntersectClip: + state()->rectangleClip = state()->rectangleClip.intersected(pathRect); + d->updateClipScissorTest(); + d->resetClipIfNeeded(); + ++d->maxClip; + d->writeClip(path, d->maxClip); + state()->currentClip = d->maxClip; + state()->clipTestEnabled = true; + break; + default: + break; + } +} + +void QOpenGL2PaintEngineExPrivate::regenerateClip() +{ + systemStateChanged(); + replayClipOperations(); +} + +void QOpenGL2PaintEngineExPrivate::systemStateChanged() +{ + Q_Q(QOpenGL2PaintEngineEx); + + q->state()->clipChanged = true; + + if (systemClip.isEmpty()) { + useSystemClip = false; + } else { + if (q->paintDevice()->devType() == QInternal::Widget && currentClipDevice) { + //QWidgetPrivate *widgetPrivate = qt_widget_private(static_cast<QWidget *>(currentClipDevice)->window()); + //useSystemClip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter; + useSystemClip = true; + } else { + useSystemClip = true; + } + } + + q->state()->clipTestEnabled = false; + q->state()->needsClipBufferClear = true; + + q->state()->currentClip = 1; + maxClip = 1; + + q->state()->rectangleClip = useSystemClip ? systemClip.boundingRect() : QRect(0, 0, width, height); + updateClipScissorTest(); + + if (systemClip.rectCount() == 1) { + if (systemClip.boundingRect() == QRect(0, 0, width, height)) + useSystemClip = false; +#ifndef QT_GL_NO_SCISSOR_TEST + // scissoring takes care of the system clip + return; +#endif + } + + if (useSystemClip) { + clearClip(0); + + QPainterPath path; + path.addRegion(systemClip); + + q->state()->currentClip = 0; + writeClip(qtVectorPathForPath(q->state()->matrix.inverted().map(path)), 1); + q->state()->currentClip = 1; + q->state()->clipTestEnabled = true; + } +} + +void QOpenGL2PaintEngineEx::setState(QPainterState *new_state) +{ + // qDebug("QOpenGL2PaintEngineEx::setState()"); + + Q_D(QOpenGL2PaintEngineEx); + + QOpenGL2PaintEngineState *s = static_cast<QOpenGL2PaintEngineState *>(new_state); + QOpenGL2PaintEngineState *old_state = state(); + + QPaintEngineEx::setState(s); + + if (s->isNew) { + // Newly created state object. The call to setState() + // will either be followed by a call to begin(), or we are + // setting the state as part of a save(). + s->isNew = false; + return; + } + + // Setting the state as part of a restore(). + + if (old_state == s || old_state->renderHintsChanged) + renderHintsChanged(); + + if (old_state == s || old_state->matrixChanged) + d->matrixDirty = true; + + if (old_state == s || old_state->compositionModeChanged) + d->compositionModeDirty = true; + + if (old_state == s || old_state->opacityChanged) + d->opacityUniformDirty = true; + + if (old_state == s || old_state->clipChanged) { + if (old_state && old_state != s && old_state->canRestoreClip) { + d->updateClipScissorTest(); + glDepthFunc(GL_LEQUAL); + } else { + d->regenerateClip(); + } + } +} + +QPainterState *QOpenGL2PaintEngineEx::createState(QPainterState *orig) const +{ + if (orig) + const_cast<QOpenGL2PaintEngineEx *>(this)->ensureActive(); + + QOpenGL2PaintEngineState *s; + if (!orig) + s = new QOpenGL2PaintEngineState(); + else + s = new QOpenGL2PaintEngineState(*static_cast<QOpenGL2PaintEngineState *>(orig)); + + s->matrixChanged = false; + s->compositionModeChanged = false; + s->opacityChanged = false; + s->renderHintsChanged = false; + s->clipChanged = false; + + return s; +} + +QOpenGL2PaintEngineState::QOpenGL2PaintEngineState(QOpenGL2PaintEngineState &other) + : QPainterState(other) +{ + isNew = true; + needsClipBufferClear = other.needsClipBufferClear; + clipTestEnabled = other.clipTestEnabled; + currentClip = other.currentClip; + canRestoreClip = other.canRestoreClip; + rectangleClip = other.rectangleClip; +} + +QOpenGL2PaintEngineState::QOpenGL2PaintEngineState() +{ + isNew = true; + needsClipBufferClear = true; + clipTestEnabled = false; + canRestoreClip = true; +} + +QOpenGL2PaintEngineState::~QOpenGL2PaintEngineState() +{ +} + +void QOpenGL2PaintEngineExPrivate::setVertexAttribArrayEnabled(int arrayIndex, bool enabled) +{ + Q_ASSERT(arrayIndex < QT_GL_VERTEX_ARRAY_TRACKED_COUNT); + + if (vertexAttributeArraysEnabledState[arrayIndex] && !enabled) + funcs.glDisableVertexAttribArray(arrayIndex); + + if (!vertexAttributeArraysEnabledState[arrayIndex] && enabled) + funcs.glEnableVertexAttribArray(arrayIndex); + + vertexAttributeArraysEnabledState[arrayIndex] = enabled; +} + +void QOpenGL2PaintEngineExPrivate::syncGlState() +{ + for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i) { + if (vertexAttributeArraysEnabledState[i]) + funcs.glEnableVertexAttribArray(i); + else + funcs.glDisableVertexAttribArray(i); + } +} + + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglpaintengine_p.h b/src/gui/opengl/qopenglpaintengine_p.h new file mode 100644 index 0000000000..d38f2a8659 --- /dev/null +++ b/src/gui/opengl/qopenglpaintengine_p.h @@ -0,0 +1,338 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGLPAINTENGINE_P_H +#define QOPENGLPAINTENGINE_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 <QDebug> + +#include <private/qpaintengineex_p.h> +#include <private/qopenglengineshadermanager_p.h> +#include <private/qopengl2pexvertexarray_p.h> +#include <private/qopenglpaintdevice_p.h> +#include <private/qfontengine_p.h> +#include <private/qdatabuffer_p.h> +#include <private/qopengltriangulatingstroker_p.h> + +#include <private/qopenglextensions_p.h> + +enum EngineMode { + ImageDrawingMode, + TextDrawingMode, + BrushDrawingMode, + ImageArrayDrawingMode +}; + +QT_BEGIN_NAMESPACE + +#define GL_STENCIL_HIGH_BIT GLuint(0x80) +#define QT_BRUSH_TEXTURE_UNIT GLuint(0) +#define QT_IMAGE_TEXTURE_UNIT GLuint(0) //Can be the same as brush texture unit +#define QT_MASK_TEXTURE_UNIT GLuint(1) +#define QT_BACKGROUND_TEXTURE_UNIT GLuint(2) + +class QOpenGL2PaintEngineExPrivate; + +class QOpenGL2PaintEngineState : public QPainterState +{ +public: + QOpenGL2PaintEngineState(QOpenGL2PaintEngineState &other); + QOpenGL2PaintEngineState(); + ~QOpenGL2PaintEngineState(); + + uint isNew : 1; + uint needsClipBufferClear : 1; + uint clipTestEnabled : 1; + uint canRestoreClip : 1; + uint matrixChanged : 1; + uint compositionModeChanged : 1; + uint opacityChanged : 1; + uint renderHintsChanged : 1; + uint clipChanged : 1; + uint currentClip : 8; + + QRect rectangleClip; +}; + +class Q_GUI_EXPORT QOpenGL2PaintEngineEx : public QPaintEngineEx +{ + Q_DECLARE_PRIVATE(QOpenGL2PaintEngineEx) +public: + QOpenGL2PaintEngineEx(); + ~QOpenGL2PaintEngineEx(); + + bool begin(QPaintDevice *device); + void ensureActive(); + bool end(); + + virtual void clipEnabledChanged(); + virtual void penChanged(); + virtual void brushChanged(); + virtual void brushOriginChanged(); + virtual void opacityChanged(); + virtual void compositionModeChanged(); + virtual void renderHintsChanged(); + virtual void transformChanged(); + + virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); + virtual void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, + QPainter::PixmapFragmentHints hints); + virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, + Qt::ImageConversionFlags flags = Qt::AutoColor); + virtual void drawTextItem(const QPointF &p, const QTextItem &textItem); + virtual void fill(const QVectorPath &path, const QBrush &brush); + virtual void stroke(const QVectorPath &path, const QPen &pen); + virtual void clip(const QVectorPath &path, Qt::ClipOperation op); + + virtual void drawStaticTextItem(QStaticTextItem *textItem); + + bool drawTexture(const QRectF &r, GLuint textureId, const QSize &size, const QRectF &sr); + + Type type() const { return OpenGL2; } + + virtual void setState(QPainterState *s); + virtual QPainterState *createState(QPainterState *orig) const; + inline QOpenGL2PaintEngineState *state() { + return static_cast<QOpenGL2PaintEngineState *>(QPaintEngineEx::state()); + } + inline const QOpenGL2PaintEngineState *state() const { + return static_cast<const QOpenGL2PaintEngineState *>(QPaintEngineEx::state()); + } + + void beginNativePainting(); + void endNativePainting(); + + void invalidateState(); + + void setRenderTextActive(bool); + + bool isNativePaintingActive() const; + bool supportsTransformations(qreal, const QTransform &) const { return true; } + +private: + Q_DISABLE_COPY(QOpenGL2PaintEngineEx) + + friend class QOpenGLEngineShaderManager; +}; + +// This probably needs to grow to GL_MAX_VERTEX_ATTRIBS, but 3 is ok for now as that's +// all the GL2 engine uses: +#define QT_GL_VERTEX_ARRAY_TRACKED_COUNT 3 + +class QOpenGL2PaintEngineExPrivate : public QPaintEngineExPrivate +{ + Q_DECLARE_PUBLIC(QOpenGL2PaintEngineEx) +public: + enum StencilFillMode { + OddEvenFillMode, + WindingFillMode, + TriStripStrokeFillMode + }; + + QOpenGL2PaintEngineExPrivate(QOpenGL2PaintEngineEx *q_ptr) : + q(q_ptr), + shaderManager(0), + width(0), height(0), + ctx(0), + useSystemClip(true), + elementIndicesVBOId(0), + opacityArray(0), + snapToPixelGrid(false), + nativePaintingActive(false), + inverseScale(1), + lastMaskTextureUsed(0) + { } + + ~QOpenGL2PaintEngineExPrivate(); + + void updateBrushTexture(); + void updateBrushUniforms(); + void updateMatrix(); + void updateCompositionMode(); + void updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id = -1); + + void resetGLState(); + + // fill, stroke, drawTexture, drawPixmaps & drawCachedGlyphs are the main rendering entry-points, + // however writeClip can also be thought of as en entry point as it does similar things. + void fill(const QVectorPath &path); + void stroke(const QVectorPath &path, const QPen &pen); + void drawTexture(const QOpenGLRect& dest, const QOpenGLRect& src, const QSize &textureSize, bool opaque, bool pattern = false); + void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, + QPainter::PixmapFragmentHints hints); + void drawCachedGlyphs(QFontEngineGlyphCache::Type glyphType, QStaticTextItem *staticTextItem); + + // Calls glVertexAttributePointer if the pointer has changed + inline void setVertexAttributePointer(unsigned int arrayIndex, const GLfloat *pointer); + + // draws whatever is in the vertex array: + void drawVertexArrays(const float *data, int *stops, int stopCount, GLenum primitive); + void drawVertexArrays(QOpenGL2PEXVertexArray &vertexArray, GLenum primitive) { + drawVertexArrays((const float *) vertexArray.data(), vertexArray.stops(), vertexArray.stopCount(), primitive); + } + + // Composites the bounding rect onto dest buffer: + void composite(const QOpenGLRect& boundingRect); + + // Calls drawVertexArrays to render into stencil buffer: + void fillStencilWithVertexArray(const float *data, int count, int *stops, int stopCount, const QOpenGLRect &bounds, StencilFillMode mode); + void fillStencilWithVertexArray(QOpenGL2PEXVertexArray& vertexArray, bool useWindingFill) { + fillStencilWithVertexArray((const float *) vertexArray.data(), 0, vertexArray.stops(), vertexArray.stopCount(), + vertexArray.boundingRect(), + useWindingFill ? WindingFillMode : OddEvenFillMode); + } + + void setBrush(const QBrush& brush); + void transferMode(EngineMode newMode); + bool prepareForDraw(bool srcPixelsAreOpaque); // returns true if the program has changed + inline void useSimpleShader(); + inline GLuint location(const QOpenGLEngineShaderManager::Uniform uniform) { + return shaderManager->getUniformLocation(uniform); + } + + void clearClip(uint value); + void writeClip(const QVectorPath &path, uint value); + void resetClipIfNeeded(); + + void updateClipScissorTest(); + void setScissor(const QRect &rect); + void regenerateClip(); + void systemStateChanged(); + + void setVertexAttribArrayEnabled(int arrayIndex, bool enabled = true); + void syncGlState(); + + static QOpenGLEngineShaderManager* shaderManagerForEngine(QOpenGL2PaintEngineEx *engine) { return engine->d_func()->shaderManager; } + static QOpenGL2PaintEngineExPrivate *getData(QOpenGL2PaintEngineEx *engine) { return engine->d_func(); } + static void cleanupVectorPath(QPaintEngineEx *engine, void *data); + + QOpenGLExtensions funcs; + + QOpenGL2PaintEngineEx* q; + QOpenGLEngineShaderManager* shaderManager; + QOpenGLPaintDevice* device; + int width, height; + QOpenGLContext *ctx; + EngineMode mode; + QFontEngineGlyphCache::Type glyphCacheType; + + bool vertexAttributeArraysEnabledState[QT_GL_VERTEX_ARRAY_TRACKED_COUNT]; + + // Dirty flags + bool matrixDirty; // Implies matrix uniforms are also dirty + bool compositionModeDirty; + bool brushTextureDirty; + bool brushUniformsDirty; + bool opacityUniformDirty; + bool matrixUniformDirty; + + bool stencilClean; // Has the stencil not been used for clipping so far? + bool useSystemClip; + QRegion dirtyStencilRegion; + QRect currentScissorBounds; + uint maxClip; + + QBrush currentBrush; // May not be the state's brush! + const QBrush noBrush; + + QPixmap currentBrushPixmap; + + QOpenGL2PEXVertexArray vertexCoordinateArray; + QOpenGL2PEXVertexArray textureCoordinateArray; + QVector<GLushort> elementIndices; + GLuint elementIndicesVBOId; + QDataBuffer<GLfloat> opacityArray; + GLfloat staticVertexCoordinateArray[8]; + GLfloat staticTextureCoordinateArray[8]; + + bool snapToPixelGrid; + bool nativePaintingActive; + GLfloat pmvMatrix[3][3]; + GLfloat inverseScale; + + GLuint lastTextureUsed; + GLuint lastMaskTextureUsed; + + bool needsSync; + bool multisamplingAlwaysEnabled; + + GLfloat depthRange[2]; + + float textureInvertedY; + + QTriangulatingStroker stroker; + QDashedStrokeProcessor dasher; + + QSet<QVectorPath::CacheEntry *> pathCaches; + QVector<GLuint> unusedVBOSToClean; + QVector<GLuint> unusedIBOSToClean; + + const GLfloat *vertexAttribPointers[3]; +}; + + +void QOpenGL2PaintEngineExPrivate::setVertexAttributePointer(unsigned int arrayIndex, const GLfloat *pointer) +{ + Q_ASSERT(arrayIndex < 3); + if (pointer == vertexAttribPointers[arrayIndex]) + return; + + vertexAttribPointers[arrayIndex] = pointer; + if (arrayIndex == QT_OPACITY_ATTR) + funcs.glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, pointer); + else + funcs.glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, pointer); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/opengl/qopenglshadercache_meego_p.h b/src/gui/opengl/qopenglshadercache_meego_p.h new file mode 100644 index 0000000000..86a8a861da --- /dev/null +++ b/src/gui/opengl/qopenglshadercache_meego_p.h @@ -0,0 +1,457 @@ +/**************************************************************************** +** +** 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 QtGui 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$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef QOPENGLSHADERCACHE_MEEGO_P_H +#define QOPENGLSHADERCACHE_MEEGO_P_H + +#include <QtCore/qopenglobal.h> + +#if defined(QT_MEEGO_EXPERIMENTAL_SHADERCACHE) && defined(QT_OPENGL_ES_2) + +#include <QtCore/qcryptographichash.h> +#include <QtCore/qsharedmemory.h> +#include <QtCore/qsystemsemaphore.h> + +#ifndef QT_BOOTSTRAPPED +# include <GLES2/gl2ext.h> +#endif +#if defined(QT_DEBUG) || defined(QT_MEEGO_EXPERIMENTAL_SHADERCACHE_TRACE) +# include <syslog.h> +#endif + +QT_BEGIN_HEADER + +/* + This cache stores internal Qt shader programs in shared memory. + + This header file is ugly on purpose and can only be included once. It is only to be used + for the internal shader cache, not as a generic cache for anyone's shaders. + + The cache stores either ShaderCacheMaxEntries shader programs or ShaderCacheDataSize kilobytes + of shader programs, whatever limit is reached first. + + The layout of the cache is as outlined in the CachedShaders struct. After some + integers, an array of headers is reserved, then comes the space for the actual binaries. + + Shader Programs are identified by the md5sum of their frag and vertex shader source code. + + Shader Programs are never removed. The cache never shrinks or re-shuffles. This is done + on purpose to ensure minimum amount of locking, no alignment problems and very few write + operations. + + Note: Locking the shader cache could be expensive, because the entire system might hang. + That's why the cache is immutable to minimize the time we need to keep it locked. + + Why is it Meego specific? + + First, the size is chosen so that it fits to generic meego usage. Second, on Meego, there's + always at least one Qt application active (the launcher), so the cache will never be destroyed. + Only when the last Qt app exits, the cache dies, which should only be when someone kills the + X11 server. And last but not least it was only tested with Meego's SGX driver. + + There's a small tool in src/opengl/util/meego that dumps the contents of the cache. + */ + +// anonymous namespace, prevent exporting of the private symbols +namespace +{ + +struct CachedShaderHeader +{ + /* the index in the data[] member of CachedShaders */ + int index; + /* the size of the binary shader */ + GLsizei size; + /* the format of the binary shader */ + GLenum format; + /* the md5sum of the frag+vertex shaders */ + char md5Sum[16]; +}; + +enum +{ + /* The maximum amount of shader programs the cache can hold */ + ShaderCacheMaxEntries = 20 +}; + +typedef CachedShaderHeader CachedShaderHeaders[ShaderCacheMaxEntries]; + +enum +{ + // ShaderCacheDataSize is 20k minus the other data members of CachedShaders + ShaderCacheDataSize = 1024 * ShaderCacheMaxEntries - sizeof(CachedShaderHeaders) - 2 * sizeof(int) +}; + +struct CachedShaders +{ + /* How much space is still available in the cache */ + inline int availableSize() const { return ShaderCacheDataSize - dataSize; } + + /* The current amount of cached shaders */ + int shaderCount; + + /* The current amount (in bytes) of cached data */ + int dataSize; + + /* The headers describing the shaders */ + CachedShaderHeaders headers; + + /* The actual binary data of the shader programs */ + char data[ShaderCacheDataSize]; +}; + +//#define QT_DEBUG_SHADER_CACHE +#ifdef QT_DEBUG_SHADER_CACHE +static QDebug shaderCacheDebug() +{ + return QDebug(QtDebugMsg); +} +#else +static inline QNoDebug shaderCacheDebug() { return QNoDebug(); } +#endif + +class ShaderCacheSharedMemory +{ +public: + ShaderCacheSharedMemory() + : shm(QLatin1String("qt_gles2_shadercache_" QT_VERSION_STR)) + { + // we need a system semaphore here, since cache creation and initialization must be atomic + QSystemSemaphore attachSemaphore(QLatin1String("qt_gles2_shadercache_mutex_" QT_VERSION_STR), 1); + + if (!attachSemaphore.acquire()) { + shaderCacheDebug() << "Unable to require shader cache semaphore:" << attachSemaphore.errorString(); + return; + } + + if (shm.attach()) { + // success! + shaderCacheDebug() << "Attached to shader cache"; + } else { + + // no cache exists - create and initialize it + if (shm.create(sizeof(CachedShaders))) { + shaderCacheDebug() << "Created new shader cache"; + initializeCache(); + } else { + shaderCacheDebug() << "Unable to create shader cache:" << shm.errorString(); + } + } + + attachSemaphore.release(); + } + + inline bool isAttached() const { return shm.isAttached(); } + + inline bool lock() { return shm.lock(); } + inline bool unlock() { return shm.unlock(); } + inline void *data() { return shm.data(); } + inline QString errorString() { return shm.errorString(); } + + ~ShaderCacheSharedMemory() + { + if (!shm.detach()) + shaderCacheDebug() << "Unable to detach shader cache" << shm.errorString(); + } + +private: + void initializeCache() + { + // no need to lock the shared memory since we're already protected by the + // attach system semaphore. + + void *data = shm.data(); + Q_ASSERT(data); + + memset(data, 0, sizeof(CachedShaders)); + } + + QSharedMemory shm; +}; + +class ShaderCacheLocker +{ +public: + inline ShaderCacheLocker(ShaderCacheSharedMemory *cache) + : shm(cache->lock() ? cache : (ShaderCacheSharedMemory *)0) + { + if (!shm) + shaderCacheDebug() << "Unable to lock shader cache" << cache->errorString(); + } + + inline bool isLocked() const { return shm; } + + inline ~ShaderCacheLocker() + { + if (!shm) + return; + if (!shm->unlock()) + shaderCacheDebug() << "Unable to unlock shader cache" << shm->errorString(); + } + +private: + ShaderCacheSharedMemory *shm; +}; + +#ifdef QT_BOOTSTRAPPED +} // end namespace +#else + +static void traceCacheOverflow(const char *message) +{ +#if defined(QT_DEBUG) || defined (QT_MEEGO_EXPERIMENTAL_SHADERCACHE_TRACE) + openlog(qPrintable(QCoreApplication::applicationName()), LOG_PID | LOG_ODELAY, LOG_USER); + syslog(LOG_DEBUG, message); + closelog(); +#endif + shaderCacheDebug() << message; +} + +Q_GLOBAL_STATIC(ShaderCacheSharedMemory, shaderCacheSharedMemory) + +/* + Finds the index of the shader program identified by md5Sum in the cache. + Note: Does NOT lock the cache for reading, the cache must already be locked! + + Returns -1 when no shader was found. + */ +static int qt_cache_index_unlocked(const QByteArray &md5Sum, CachedShaders *cache) +{ + for (int i = 0; i < cache->shaderCount; ++i) { + if (qstrncmp(md5Sum.constData(), cache->headers[i].md5Sum, 16) == 0) { + return i; + } + } + return -1; +} + +/* Returns the index of the shader identified by md5Sum */ +static int qt_cache_index(const QByteArray &md5Sum) +{ + ShaderCacheSharedMemory *shm = shaderCacheSharedMemory(); + if (!shm || !shm->isAttached()) + return false; + + Q_ASSERT(md5Sum.length() == 16); + + ShaderCacheLocker locker(shm); + if (!locker.isLocked()) + return false; + + void *data = shm->data(); + Q_ASSERT(data); + + CachedShaders *cache = reinterpret_cast<CachedShaders *>(data); + + return qt_cache_index_unlocked(md5Sum, cache); +} + +/* Loads the cached shader at index \a shaderIndex into \a program + * Note: Since the cache is immutable, this operation doesn't lock the shared memory. + */ +static bool qt_cached_shader(QOpenGLShaderProgram *program, QOpenGLContext *ctx, int shaderIndex) +{ + Q_ASSERT(shaderIndex >= 0 && shaderIndex <= ShaderCacheMaxEntries); + Q_ASSERT(program); + + ShaderCacheSharedMemory *shm = shaderCacheSharedMemory(); + if (!shm || !shm->isAttached()) + return false; + + void *data = shm->data(); + Q_ASSERT(data); + + CachedShaders *cache = reinterpret_cast<CachedShaders *>(data); + + shaderCacheDebug() << "fetching cached shader at index" << shaderIndex + << "dataIndex" << cache->headers[shaderIndex].index + << "size" << cache->headers[shaderIndex].size + << "format" << cache->headers[shaderIndex].format; + + // call program->programId first, since that resolves the glProgramBinaryOES symbol + GLuint programId = program->programId(); + glProgramBinaryOES(programId, cache->headers[shaderIndex].format, + cache->data + cache->headers[shaderIndex].index, + cache->headers[shaderIndex].size); + + return true; +} + +/* Stores the shader program in the cache. Returns false if there's an error with the cache, or + if the cache is too small to hold the shader. */ +static bool qt_cache_shader(const QOpenGLShaderProgram *shader, QOpenGLContext *ctx, const QByteArray &md5Sum) +{ + ShaderCacheSharedMemory *shm = shaderCacheSharedMemory(); + if (!shm || !shm->isAttached()) + return false; + + void *data = shm->data(); + Q_ASSERT(data); + + CachedShaders *cache = reinterpret_cast<CachedShaders *>(data); + + ShaderCacheLocker locker(shm); + if (!locker.isLocked()) + return false; + + int cacheIdx = cache->shaderCount; + if (cacheIdx >= ShaderCacheMaxEntries) { + traceCacheOverflow("Qt OpenGL shader cache index overflow!"); + return false; + } + + // now that we have the lock on the shared memory, make sure no one + // inserted the shader already while we were unlocked + if (qt_cache_index_unlocked(md5Sum, cache) != -1) + return true; // already cached + + shaderCacheDebug() << "Caching shader at index" << cacheIdx; + + GLint binaryLength = 0; + glGetProgramiv(shader->programId(), GL_PROGRAM_BINARY_LENGTH_OES, &binaryLength); + + if (!binaryLength) { + shaderCacheDebug() << "Unable to determine binary shader size!"; + return false; + } + + if (binaryLength > cache->availableSize()) { + traceCacheOverflow("Qt OpenGL shader cache data overflow!"); + return false; + } + + GLsizei size = 0; + GLenum format = 0; + glGetProgramBinaryOES(shader->programId(), binaryLength, &size, &format, + cache->data + cache->dataSize); + + if (!size) { + shaderCacheDebug() << "Unable to get binary shader!"; + return false; + } + + cache->headers[cacheIdx].index = cache->dataSize; + cache->dataSize += binaryLength; + ++cache->shaderCount; + cache->headers[cacheIdx].size = binaryLength; + cache->headers[cacheIdx].format = format; + + memcpy(cache->headers[cacheIdx].md5Sum, md5Sum.constData(), 16); + + shaderCacheDebug() << "cached shader size" << size + << "format" << format + << "binarySize" << binaryLength + << "cache index" << cacheIdx + << "data index" << cache->headers[cacheIdx].index; + + return true; +} + +} // namespace + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class CachedShader +{ +public: + CachedShader(const QByteArray &fragSource, const QByteArray &vertexSource) + : cacheIdx(-1) + { + QCryptographicHash md5Hash(QCryptographicHash::Md5); + + md5Hash.addData(fragSource); + md5Hash.addData(vertexSource); + + md5Sum = md5Hash.result(); + } + + bool isCached() + { + return cacheIndex() != -1; + } + + int cacheIndex() + { + if (cacheIdx != -1) + return cacheIdx; + cacheIdx = qt_cache_index(md5Sum); + return cacheIdx; + } + + bool load(QOpenGLShaderProgram *program, QOpenGLContext *ctx) + { + if (cacheIndex() == -1) + return false; + return qt_cached_shader(program, ctx, cacheIdx); + } + + bool store(QOpenGLShaderProgram *program, QOpenGLContext *ctx) + { + return qt_cache_shader(program, ctx, md5Sum); + } + +private: + QByteArray md5Sum; + int cacheIdx; +}; + + +QT_END_NAMESPACE + +#endif + +QT_END_HEADER + +#endif +#endif diff --git a/src/gui/opengl/qopenglshadercache_p.h b/src/gui/opengl/qopenglshadercache_p.h new file mode 100644 index 0000000000..05a058050c --- /dev/null +++ b/src/gui/opengl/qopenglshadercache_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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 QtGui 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$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef QOPENGLSHADERCACHE_P_H +#define QOPENGLSHADERCACHE_P_H + +#include <QtCore/qglobal.h> + +#if defined(QT_MEEGO_EXPERIMENTAL_SHADERCACHE) && defined(QT_OPENGL_ES_2) +# include "qopenglshadercache_meego_p.h" +#else + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QOpenGLShaderProgram; +class QOpenGLContext; + +class CachedShader +{ +public: + inline CachedShader(const QByteArray &, const QByteArray &) + {} + + inline bool isCached() + { + return false; + } + + inline bool load(QOpenGLShaderProgram *, QOpenGLContext *) + { + return false; + } + + inline bool store(QOpenGLShaderProgram *, QOpenGLContext *) + { + return false; + } +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif +#endif diff --git a/src/gui/opengl/qopenglshaderprogram.cpp b/src/gui/opengl/qopenglshaderprogram.cpp new file mode 100644 index 0000000000..aa7767a7eb --- /dev/null +++ b/src/gui/opengl/qopenglshaderprogram.cpp @@ -0,0 +1,3105 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qopenglshaderprogram.h" +#include "qopenglfunctions.h" +#include "private/qopenglcontext_p.h" +#include <QtCore/private/qobject_p.h> +#include <QtCore/qdebug.h> +#include <QtCore/qfile.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qvector.h> +#include <QtGui/qtransform.h> +#include <QtGui/QColor> + +QT_BEGIN_NAMESPACE + +/*! + \class QOpenGLShaderProgram + \brief The QOpenGLShaderProgram class allows OpenGL shader programs to be linked and used. + \since 5.0 + \ingroup painting-3D + + \section1 Introduction + + This class supports shader programs written in the OpenGL Shading + Language (GLSL) and in the OpenGL/ES Shading Language (GLSL/ES). + + QOpenGLShader and QOpenGLShaderProgram shelter the programmer from the details of + compiling and linking vertex and fragment shaders. + + The following example creates a vertex shader program using the + supplied source \c{code}. Once compiled and linked, the shader + program is activated in the current QOpenGLContext by calling + QOpenGLShaderProgram::bind(): + + \snippet doc/src/snippets/code/src_opengl_qopenglshaderprogram.cpp 0 + + \section1 Writing portable shaders + + Shader programs can be difficult to reuse across OpenGL implementations + because of varying levels of support for standard vertex attributes and + uniform variables. In particular, GLSL/ES lacks all of the + standard variables that are present on desktop OpenGL systems: + \c{gl_Vertex}, \c{gl_Normal}, \c{gl_Color}, and so on. Desktop OpenGL + lacks the variable qualifiers \c{highp}, \c{mediump}, and \c{lowp}. + + The QOpenGLShaderProgram class makes the process of writing portable shaders + easier by prefixing all shader programs with the following lines on + desktop OpenGL: + + \code + #define highp + #define mediump + #define lowp + \endcode + + This makes it possible to run most GLSL/ES shader programs + on desktop systems. The programmer should restrict themselves + to just features that are present in GLSL/ES, and avoid + standard variable names that only work on the desktop. + + \section1 Simple shader example + + \snippet doc/src/snippets/code/src_opengl_qopenglshaderprogram.cpp 1 + + With the above shader program active, we can draw a green triangle + as follows: + + \snippet doc/src/snippets/code/src_opengl_qopenglshaderprogram.cpp 2 + + \section1 Binary shaders and programs + + Binary shaders may be specified using \c{glShaderBinary()} on + the return value from QOpenGLShader::shaderId(). The QOpenGLShader instance + containing the binary can then be added to the shader program with + addShader() and linked in the usual fashion with link(). + + Binary programs may be specified using \c{glProgramBinaryOES()} + on the return value from programId(). Then the application should + call link(), which will notice that the program has already been + specified and linked, allowing other operations to be performed + on the shader program. + + \sa QOpenGLShader +*/ + +/*! + \class QOpenGLShader + \brief The QOpenGLShader class allows OpenGL shaders to be compiled. + \since 5.0 + \ingroup painting-3D + + This class supports shaders written in the OpenGL Shading Language (GLSL) + and in the OpenGL/ES Shading Language (GLSL/ES). + + QOpenGLShader and QOpenGLShaderProgram shelter the programmer from the details of + compiling and linking vertex and fragment shaders. + + \sa QOpenGLShaderProgram +*/ + +/*! + \enum QOpenGLShader::ShaderTypeBit + This enum specifies the type of QOpenGLShader that is being created. + + \value Vertex Vertex shader written in the OpenGL Shading Language (GLSL). + \value Fragment Fragment shader written in the OpenGL Shading Language (GLSL). + \value Geometry Geometry shaders written in the OpenGL Shading + Language (GLSL), based on the GL_EXT_geometry_shader4 extension. +*/ + +class QOpenGLShaderPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QOpenGLShader) +public: + QOpenGLShaderPrivate(QOpenGLContext *ctx, QOpenGLShader::ShaderType type) + : shaderGuard(0) + , shaderType(type) + , compiled(false) + , glfuncs(new QOpenGLFunctions(ctx)) + { + } + ~QOpenGLShaderPrivate(); + + QOpenGLSharedResourceGuard *shaderGuard; + QOpenGLShader::ShaderType shaderType; + bool compiled; + QString log; + + QOpenGLFunctions *glfuncs; + + bool create(); + bool compile(QOpenGLShader *q); + void deleteShader(); +}; + +namespace { + void freeShaderFunc(QOpenGLFunctions *funcs, GLuint id) + { + funcs->glDeleteShader(id); + } +} + +QOpenGLShaderPrivate::~QOpenGLShaderPrivate() +{ + delete glfuncs; + if (shaderGuard) + shaderGuard->free(); +} + +bool QOpenGLShaderPrivate::create() +{ + QOpenGLContext *context = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); + if (!context) + return false; + GLuint shader; + if (shaderType == QOpenGLShader::Vertex) + shader = glfuncs->glCreateShader(GL_VERTEX_SHADER); +#if 0 + else if (shaderType == QOpenGLShader::Geometry) + shader = glfuncs->glCreateShader(GL_GEOMETRY_SHADER_EXT); +#endif + else + shader = glfuncs->glCreateShader(GL_FRAGMENT_SHADER); + if (!shader) { + qWarning() << "QOpenGLShader: could not create shader"; + return false; + } + shaderGuard = new QOpenGLSharedResourceGuard(context, shader, freeShaderFunc); + return true; +} + +bool QOpenGLShaderPrivate::compile(QOpenGLShader *q) +{ + GLuint shader = shaderGuard ? shaderGuard->id() : 0; + if (!shader) + return false; + glfuncs->glCompileShader(shader); + GLint value = 0; + glfuncs->glGetShaderiv(shader, GL_COMPILE_STATUS, &value); + compiled = (value != 0); + value = 0; + glfuncs->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &value); + if (!compiled && value > 1) { + char *logbuf = new char [value]; + GLint len; + glfuncs->glGetShaderInfoLog(shader, value, &len, logbuf); + log = QString::fromLatin1(logbuf); + QString name = q->objectName(); + + const char *types[] = { + "Fragment", + "Vertex", + "Geometry", + "" + }; + + const char *type = types[3]; + if (shaderType == QOpenGLShader::Fragment) + type = types[0]; + else if (shaderType == QOpenGLShader::Vertex) + type = types[1]; + else if (shaderType == QOpenGLShader::Geometry) + type = types[2]; + + if (name.isEmpty()) + qWarning("QOpenGLShader::compile(%s): %s", type, qPrintable(log)); + else + qWarning("QOpenGLShader::compile(%s)[%s]: %s", type, qPrintable(name), qPrintable(log)); + + delete [] logbuf; + } + return compiled; +} + +void QOpenGLShaderPrivate::deleteShader() +{ + if (shaderGuard) { + shaderGuard->free(); + shaderGuard = 0; + } +} + +/*! + Constructs a new QOpenGLShader object of the specified \a type + and attaches it to \a parent. If shader programs are not supported, + QOpenGLShaderProgram::hasOpenGLShaderPrograms() will return false. + + This constructor is normally followed by a call to compileSourceCode() + or compileSourceFile(). + + The shader will be associated with the current QOpenGLContext. + + \sa compileSourceCode(), compileSourceFile() +*/ +QOpenGLShader::QOpenGLShader(QOpenGLShader::ShaderType type, QObject *parent) + : QObject(*new QOpenGLShaderPrivate(QOpenGLContext::currentContext(), type), parent) +{ + Q_D(QOpenGLShader); + d->create(); +} + +/*! + Deletes this shader. If the shader has been attached to a + QOpenGLShaderProgram object, then the actual shader will stay around + until the QOpenGLShaderProgram is destroyed. +*/ +QOpenGLShader::~QOpenGLShader() +{ +} + +/*! + Returns the type of this shader. +*/ +QOpenGLShader::ShaderType QOpenGLShader::shaderType() const +{ + Q_D(const QOpenGLShader); + return d->shaderType; +} + +// The precision qualifiers are useful on OpenGL/ES systems, +// but usually not present on desktop systems. Define the +// keywords to empty strings on desktop systems. +#if !defined(QT_OPENGL_ES) || defined(QT_OPENGL_FORCE_SHADER_DEFINES) +#define QOpenGL_DEFINE_QUALIFIERS 1 +static const char qualifierDefines[] = + "#define lowp\n" + "#define mediump\n" + "#define highp\n"; + +#else + +// The "highp" qualifier doesn't exist in fragment shaders +// on all ES platforms. When it doesn't exist, use "mediump". +#define QOpenGL_REDEFINE_HIGHP 1 +static const char redefineHighp[] = + "#ifndef GL_FRAGMENT_PRECISION_HIGH\n" + "#define highp mediump\n" + "#endif\n"; +#endif + +/*! + Sets the \a source code for this shader and compiles it. + Returns true if the source was successfully compiled, false otherwise. + + \sa compileSourceFile() +*/ +bool QOpenGLShader::compileSourceCode(const char *source) +{ + Q_D(QOpenGLShader); + if (d->shaderGuard && d->shaderGuard->id()) { + QVarLengthArray<const char *, 4> src; + QVarLengthArray<GLint, 4> srclen; + int headerLen = 0; + while (source && source[headerLen] == '#') { + // Skip #version and #extension directives at the start of + // the shader code. We need to insert the qualifierDefines + // and redefineHighp just after them. + if (qstrncmp(source + headerLen, "#version", 8) != 0 && + qstrncmp(source + headerLen, "#extension", 10) != 0) { + break; + } + while (source[headerLen] != '\0' && source[headerLen] != '\n') + ++headerLen; + if (source[headerLen] == '\n') + ++headerLen; + } + if (headerLen > 0) { + src.append(source); + srclen.append(GLint(headerLen)); + } +#ifdef QOpenGL_DEFINE_QUALIFIERS + src.append(qualifierDefines); + srclen.append(GLint(sizeof(qualifierDefines) - 1)); +#endif +#ifdef QOpenGL_REDEFINE_HIGHP + if (d->shaderType == Fragment) { + src.append(redefineHighp); + srclen.append(GLint(sizeof(redefineHighp) - 1)); + } +#endif + src.append(source + headerLen); + srclen.append(GLint(qstrlen(source + headerLen))); + d->glfuncs->glShaderSource(d->shaderGuard->id(), src.size(), src.data(), srclen.data()); + return d->compile(this); + } else { + return false; + } +} + +/*! + \overload + + Sets the \a source code for this shader and compiles it. + Returns true if the source was successfully compiled, false otherwise. + + \sa compileSourceFile() +*/ +bool QOpenGLShader::compileSourceCode(const QByteArray& source) +{ + return compileSourceCode(source.constData()); +} + +/*! + \overload + + Sets the \a source code for this shader and compiles it. + Returns true if the source was successfully compiled, false otherwise. + + \sa compileSourceFile() +*/ +bool QOpenGLShader::compileSourceCode(const QString& source) +{ + return compileSourceCode(source.toLatin1().constData()); +} + +/*! + Sets the source code for this shader to the contents of \a fileName + and compiles it. Returns true if the file could be opened and the + source compiled, false otherwise. + + \sa compileSourceCode() +*/ +bool QOpenGLShader::compileSourceFile(const QString& fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) { + qWarning() << "QOpenGLShader: Unable to open file" << fileName; + return false; + } + + QByteArray contents = file.readAll(); + return compileSourceCode(contents.constData()); +} + +/*! + Returns the source code for this shader. + + \sa compileSourceCode() +*/ +QByteArray QOpenGLShader::sourceCode() const +{ + Q_D(const QOpenGLShader); + GLuint shader = d->shaderGuard ? d->shaderGuard->id() : 0; + if (!shader) + return QByteArray(); + GLint size = 0; + d->glfuncs->glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &size); + if (size <= 0) + return QByteArray(); + GLint len = 0; + char *source = new char [size]; + d->glfuncs->glGetShaderSource(shader, size, &len, source); + QByteArray src(source); + delete [] source; + return src; +} + +/*! + Returns true if this shader has been compiled; false otherwise. + + \sa compileSourceCode(), compileSourceFile() +*/ +bool QOpenGLShader::isCompiled() const +{ + Q_D(const QOpenGLShader); + return d->compiled; +} + +/*! + Returns the errors and warnings that occurred during the last compile. + + \sa compileSourceCode(), compileSourceFile() +*/ +QString QOpenGLShader::log() const +{ + Q_D(const QOpenGLShader); + return d->log; +} + +/*! + Returns the OpenGL identifier associated with this shader. + + \sa QOpenGLShaderProgram::programId() +*/ +GLuint QOpenGLShader::shaderId() const +{ + Q_D(const QOpenGLShader); + return d->shaderGuard ? d->shaderGuard->id() : 0; +} + +class QOpenGLShaderProgramPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QOpenGLShaderProgram) +public: + QOpenGLShaderProgramPrivate(QOpenGLContext *ctx) + : programGuard(0) + , linked(false) + , inited(false) + , removingShaders(false) + , geometryVertexCount(64) + , geometryInputType(0) + , geometryOutputType(0) + , glfuncs(new QOpenGLFunctions(ctx)) + { + } + ~QOpenGLShaderProgramPrivate(); + + QOpenGLSharedResourceGuard *programGuard; + bool linked; + bool inited; + bool removingShaders; + + int geometryVertexCount; + GLenum geometryInputType; + GLenum geometryOutputType; + + QString log; + QList<QOpenGLShader *> shaders; + QList<QOpenGLShader *> anonShaders; + + QOpenGLFunctions *glfuncs; + + bool hasShader(QOpenGLShader::ShaderType type) const; +}; + +namespace { + void freeProgramFunc(QOpenGLFunctions *funcs, GLuint id) + { + funcs->glDeleteProgram(id); + } +} + + +QOpenGLShaderProgramPrivate::~QOpenGLShaderProgramPrivate() +{ + delete glfuncs; + if (programGuard) + programGuard->free(); +} + +bool QOpenGLShaderProgramPrivate::hasShader(QOpenGLShader::ShaderType type) const +{ + foreach (QOpenGLShader *shader, shaders) { + if (shader->shaderType() == type) + return true; + } + return false; +} + +/*! + Constructs a new shader program and attaches it to \a parent. + The program will be invalid until addShader() is called. + + The shader program will be associated with the current QOpenGLContext. + + \sa addShader() +*/ +QOpenGLShaderProgram::QOpenGLShaderProgram(QObject *parent) + : QObject(*new QOpenGLShaderProgramPrivate(QOpenGLContext::currentContext()), parent) +{ +} + +/*! + Deletes this shader program. +*/ +QOpenGLShaderProgram::~QOpenGLShaderProgram() +{ +} + +bool QOpenGLShaderProgram::init() +{ + Q_D(QOpenGLShaderProgram); + if ((d->programGuard && d->programGuard->id()) || d->inited) + return true; + d->inited = true; + QOpenGLContext *context = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); + if (!context) + return false; + GLuint program = d->glfuncs->glCreateProgram(); + if (!program) { + qWarning() << "QOpenGLShaderProgram: could not create shader program"; + return false; + } + if (d->programGuard) + delete d->programGuard; + d->programGuard = new QOpenGLSharedResourceGuard(context, program, freeProgramFunc); + return true; +} + +/*! + Adds a compiled \a shader to this shader program. Returns true + if the shader could be added, or false otherwise. + + Ownership of the \a shader object remains with the caller. + It will not be deleted when this QOpenGLShaderProgram instance + is deleted. This allows the caller to add the same shader + to multiple shader programs. + + \sa addShaderFromSourceCode(), addShaderFromSourceFile() + \sa removeShader(), link(), removeAllShaders() +*/ +bool QOpenGLShaderProgram::addShader(QOpenGLShader *shader) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + if (d->shaders.contains(shader)) + return true; // Already added to this shader program. + if (d->programGuard && d->programGuard->id() && shader) { + if (!shader->d_func()->shaderGuard || !shader->d_func()->shaderGuard->id()) + return false; + if (d->programGuard->group() != shader->d_func()->shaderGuard->group()) { + qWarning("QOpenGLShaderProgram::addShader: Program and shader are not associated with same context."); + return false; + } + d->glfuncs->glAttachShader(d->programGuard->id(), shader->d_func()->shaderGuard->id()); + d->linked = false; // Program needs to be relinked. + d->shaders.append(shader); + connect(shader, SIGNAL(destroyed()), this, SLOT(shaderDestroyed())); + return true; + } else { + return false; + } +} + +/*! + Compiles \a source as a shader of the specified \a type and + adds it to this shader program. Returns true if compilation + was successful, false otherwise. The compilation errors + and warnings will be made available via log(). + + This function is intended to be a short-cut for quickly + adding vertex and fragment shaders to a shader program without + creating an instance of QOpenGLShader first. + + \sa addShader(), addShaderFromSourceFile() + \sa removeShader(), link(), log(), removeAllShaders() +*/ +bool QOpenGLShaderProgram::addShaderFromSourceCode(QOpenGLShader::ShaderType type, const char *source) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + QOpenGLShader *shader = new QOpenGLShader(type, this); + if (!shader->compileSourceCode(source)) { + d->log = shader->log(); + delete shader; + return false; + } + d->anonShaders.append(shader); + return addShader(shader); +} + +/*! + \overload + + Compiles \a source as a shader of the specified \a type and + adds it to this shader program. Returns true if compilation + was successful, false otherwise. The compilation errors + and warnings will be made available via log(). + + This function is intended to be a short-cut for quickly + adding vertex and fragment shaders to a shader program without + creating an instance of QOpenGLShader first. + + \sa addShader(), addShaderFromSourceFile() + \sa removeShader(), link(), log(), removeAllShaders() +*/ +bool QOpenGLShaderProgram::addShaderFromSourceCode(QOpenGLShader::ShaderType type, const QByteArray& source) +{ + return addShaderFromSourceCode(type, source.constData()); +} + +/*! + \overload + + Compiles \a source as a shader of the specified \a type and + adds it to this shader program. Returns true if compilation + was successful, false otherwise. The compilation errors + and warnings will be made available via log(). + + This function is intended to be a short-cut for quickly + adding vertex and fragment shaders to a shader program without + creating an instance of QOpenGLShader first. + + \sa addShader(), addShaderFromSourceFile() + \sa removeShader(), link(), log(), removeAllShaders() +*/ +bool QOpenGLShaderProgram::addShaderFromSourceCode(QOpenGLShader::ShaderType type, const QString& source) +{ + return addShaderFromSourceCode(type, source.toLatin1().constData()); +} + +/*! + Compiles the contents of \a fileName as a shader of the specified + \a type and adds it to this shader program. Returns true if + compilation was successful, false otherwise. The compilation errors + and warnings will be made available via log(). + + This function is intended to be a short-cut for quickly + adding vertex and fragment shaders to a shader program without + creating an instance of QOpenGLShader first. + + \sa addShader(), addShaderFromSourceCode() +*/ +bool QOpenGLShaderProgram::addShaderFromSourceFile + (QOpenGLShader::ShaderType type, const QString& fileName) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + QOpenGLShader *shader = new QOpenGLShader(type, this); + if (!shader->compileSourceFile(fileName)) { + d->log = shader->log(); + delete shader; + return false; + } + d->anonShaders.append(shader); + return addShader(shader); +} + +/*! + Removes \a shader from this shader program. The object is not deleted. + + The shader program must be valid in the current QOpenGLContext. + + \sa addShader(), link(), removeAllShaders() +*/ +void QOpenGLShaderProgram::removeShader(QOpenGLShader *shader) +{ + Q_D(QOpenGLShaderProgram); + if (d->programGuard && d->programGuard->id() + && shader && shader->d_func()->shaderGuard) + { + d->glfuncs->glDetachShader(d->programGuard->id(), shader->d_func()->shaderGuard->id()); + } + d->linked = false; // Program needs to be relinked. + if (shader) { + d->shaders.removeAll(shader); + d->anonShaders.removeAll(shader); + disconnect(shader, SIGNAL(destroyed()), this, SLOT(shaderDestroyed())); + } +} + +/*! + Returns a list of all shaders that have been added to this shader + program using addShader(). + + \sa addShader(), removeShader() +*/ +QList<QOpenGLShader *> QOpenGLShaderProgram::shaders() const +{ + Q_D(const QOpenGLShaderProgram); + return d->shaders; +} + +/*! + Removes all of the shaders that were added to this program previously. + The QOpenGLShader objects for the shaders will not be deleted if they + were constructed externally. QOpenGLShader objects that are constructed + internally by QOpenGLShaderProgram will be deleted. + + \sa addShader(), removeShader() +*/ +void QOpenGLShaderProgram::removeAllShaders() +{ + Q_D(QOpenGLShaderProgram); + d->removingShaders = true; + foreach (QOpenGLShader *shader, d->shaders) { + if (d->programGuard && d->programGuard->id() + && shader && shader->d_func()->shaderGuard) + { + d->glfuncs->glDetachShader(d->programGuard->id(), shader->d_func()->shaderGuard->id()); + } + } + foreach (QOpenGLShader *shader, d->anonShaders) { + // Delete shader objects that were created anonymously. + delete shader; + } + d->shaders.clear(); + d->anonShaders.clear(); + d->linked = false; // Program needs to be relinked. + d->removingShaders = false; +} + +/*! + Links together the shaders that were added to this program with + addShader(). Returns true if the link was successful or + false otherwise. If the link failed, the error messages can + be retrieved with log(). + + Subclasses can override this function to initialize attributes + and uniform variables for use in specific shader programs. + + If the shader program was already linked, calling this + function again will force it to be re-linked. + + \sa addShader(), log() +*/ +bool QOpenGLShaderProgram::link() +{ + Q_D(QOpenGLShaderProgram); + GLuint program = d->programGuard ? d->programGuard->id() : 0; + if (!program) + return false; + + GLint value; + if (d->shaders.isEmpty()) { + // If there are no explicit shaders, then it is possible that the + // application added a program binary with glProgramBinaryOES(), + // or otherwise populated the shaders itself. Check to see if the + // program is already linked and bail out if so. + value = 0; + d->glfuncs->glGetProgramiv(program, GL_LINK_STATUS, &value); + d->linked = (value != 0); + if (d->linked) + return true; + } + + // Set up the geometry shader parameters +#if 0 + if (glProgramParameteriEXT) { + foreach (QOpenGLShader *shader, d->shaders) { + if (shader->shaderType() & QOpenGLShader::Geometry) { + glProgramParameteriEXT(program, GL_GEOMETRY_INPUT_TYPE_EXT, + d->geometryInputType); + glProgramParameteriEXT(program, GL_GEOMETRY_OUTPUT_TYPE_EXT, + d->geometryOutputType); + glProgramParameteriEXT(program, GL_GEOMETRY_VERTICES_OUT_EXT, + d->geometryVertexCount); + break; + } + } + } +#endif + + d->glfuncs->glLinkProgram(program); + value = 0; + d->glfuncs->glGetProgramiv(program, GL_LINK_STATUS, &value); + d->linked = (value != 0); + value = 0; + d->glfuncs->glGetProgramiv(program, GL_INFO_LOG_LENGTH, &value); + d->log = QString(); + if (value > 1) { + char *logbuf = new char [value]; + GLint len; + d->glfuncs->glGetProgramInfoLog(program, value, &len, logbuf); + d->log = QString::fromLatin1(logbuf); + QString name = objectName(); + if (name.isEmpty()) + qWarning() << "QOpenGLShader::link:" << d->log; + else + qWarning() << "QOpenGLShader::link[" << name << "]:" << d->log; + delete [] logbuf; + } + return d->linked; +} + +/*! + Returns true if this shader program has been linked; false otherwise. + + \sa link() +*/ +bool QOpenGLShaderProgram::isLinked() const +{ + Q_D(const QOpenGLShaderProgram); + return d->linked; +} + +/*! + Returns the errors and warnings that occurred during the last link() + or addShader() with explicitly specified source code. + + \sa link() +*/ +QString QOpenGLShaderProgram::log() const +{ + Q_D(const QOpenGLShaderProgram); + return d->log; +} + +/*! + Binds this shader program to the active QOpenGLContext and makes + it the current shader program. Any previously bound shader program + is released. This is equivalent to calling \c{glUseProgram()} on + programId(). Returns true if the program was successfully bound; + false otherwise. If the shader program has not yet been linked, + or it needs to be re-linked, this function will call link(). + + \sa link(), release() +*/ +bool QOpenGLShaderProgram::bind() +{ + Q_D(QOpenGLShaderProgram); + GLuint program = d->programGuard ? d->programGuard->id() : 0; + if (!program) + return false; + if (!d->linked && !link()) + return false; +#ifndef QT_NO_DEBUG + if (d->programGuard->group() != QOpenGLContextGroup::currentContextGroup()) { + qWarning("QOpenGLShaderProgram::bind: program is not valid in the current context."); + return false; + } +#endif + d->glfuncs->glUseProgram(program); + return true; +} + +/*! + Releases the active shader program from the current QOpenGLContext. + This is equivalent to calling \c{glUseProgram(0)}. + + \sa bind() +*/ +void QOpenGLShaderProgram::release() +{ + Q_D(QOpenGLShaderProgram); +#ifndef QT_NO_DEBUG + if (d->programGuard->group() != QOpenGLContextGroup::currentContextGroup()) + qWarning("QOpenGLShaderProgram::release: program is not valid in the current context."); +#endif + d->glfuncs->glUseProgram(0); +} + +/*! + Returns the OpenGL identifier associated with this shader program. + + \sa QOpenGLShader::shaderId() +*/ +GLuint QOpenGLShaderProgram::programId() const +{ + Q_D(const QOpenGLShaderProgram); + GLuint id = d->programGuard ? d->programGuard->id() : 0; + if (id) + return id; + + // Create the identifier if we don't have one yet. This is for + // applications that want to create the attached shader configuration + // themselves, particularly those using program binaries. + if (!const_cast<QOpenGLShaderProgram *>(this)->init()) + return 0; + return d->programGuard ? d->programGuard->id() : 0; +} + +/*! + Binds the attribute \a name to the specified \a location. This + function can be called before or after the program has been linked. + Any attributes that have not been explicitly bound when the program + is linked will be assigned locations automatically. + + When this function is called after the program has been linked, + the program will need to be relinked for the change to take effect. + + \sa attributeLocation() +*/ +void QOpenGLShaderProgram::bindAttributeLocation(const char *name, int location) +{ + Q_D(QOpenGLShaderProgram); + if (!init() || !d->programGuard || !d->programGuard->id()) + return; + d->glfuncs->glBindAttribLocation(d->programGuard->id(), location, name); + d->linked = false; // Program needs to be relinked. +} + +/*! + \overload + + Binds the attribute \a name to the specified \a location. This + function can be called before or after the program has been linked. + Any attributes that have not been explicitly bound when the program + is linked will be assigned locations automatically. + + When this function is called after the program has been linked, + the program will need to be relinked for the change to take effect. + + \sa attributeLocation() +*/ +void QOpenGLShaderProgram::bindAttributeLocation(const QByteArray& name, int location) +{ + bindAttributeLocation(name.constData(), location); +} + +/*! + \overload + + Binds the attribute \a name to the specified \a location. This + function can be called before or after the program has been linked. + Any attributes that have not been explicitly bound when the program + is linked will be assigned locations automatically. + + When this function is called after the program has been linked, + the program will need to be relinked for the change to take effect. + + \sa attributeLocation() +*/ +void QOpenGLShaderProgram::bindAttributeLocation(const QString& name, int location) +{ + bindAttributeLocation(name.toLatin1().constData(), location); +} + +/*! + Returns the location of the attribute \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + attribute for this shader program. + + \sa uniformLocation(), bindAttributeLocation() +*/ +int QOpenGLShaderProgram::attributeLocation(const char *name) const +{ + Q_D(const QOpenGLShaderProgram); + if (d->linked && d->programGuard && d->programGuard->id()) { + return d->glfuncs->glGetAttribLocation(d->programGuard->id(), name); + } else { + qWarning() << "QOpenGLShaderProgram::attributeLocation(" << name + << "): shader program is not linked"; + return -1; + } +} + +/*! + \overload + + Returns the location of the attribute \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + attribute for this shader program. + + \sa uniformLocation(), bindAttributeLocation() +*/ +int QOpenGLShaderProgram::attributeLocation(const QByteArray& name) const +{ + return attributeLocation(name.constData()); +} + +/*! + \overload + + Returns the location of the attribute \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + attribute for this shader program. + + \sa uniformLocation(), bindAttributeLocation() +*/ +int QOpenGLShaderProgram::attributeLocation(const QString& name) const +{ + return attributeLocation(name.toLatin1().constData()); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(int location, GLfloat value) +{ + Q_D(QOpenGLShaderProgram); + if (location != -1) + d->glfuncs->glVertexAttrib1fv(location, &value); +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(const char *name, GLfloat value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to + the 2D vector (\a x, \a y). + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(int location, GLfloat x, GLfloat y) +{ + Q_D(QOpenGLShaderProgram); + if (location != -1) { + GLfloat values[2] = {x, y}; + d->glfuncs->glVertexAttrib2fv(location, values); + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to + the 2D vector (\a x, \a y). + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(const char *name, GLfloat x, GLfloat y) +{ + setAttributeValue(attributeLocation(name), x, y); +} + +/*! + Sets the attribute at \a location in the current context to + the 3D vector (\a x, \a y, \a z). + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue + (int location, GLfloat x, GLfloat y, GLfloat z) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[3] = {x, y, z}; + d->glfuncs->glVertexAttrib3fv(location, values); + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to + the 3D vector (\a x, \a y, \a z). + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue + (const char *name, GLfloat x, GLfloat y, GLfloat z) +{ + setAttributeValue(attributeLocation(name), x, y, z); +} + +/*! + Sets the attribute at \a location in the current context to + the 4D vector (\a x, \a y, \a z, \a w). + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue + (int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + Q_D(QOpenGLShaderProgram); + if (location != -1) { + GLfloat values[4] = {x, y, z, w}; + d->glfuncs->glVertexAttrib4fv(location, values); + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to + the 4D vector (\a x, \a y, \a z, \a w). + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue + (const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + setAttributeValue(attributeLocation(name), x, y, z, w); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(int location, const QVector2D& value) +{ + Q_D(QOpenGLShaderProgram); + if (location != -1) + d->glfuncs->glVertexAttrib2fv(location, reinterpret_cast<const GLfloat *>(&value)); +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(const char *name, const QVector2D& value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(int location, const QVector3D& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glVertexAttrib3fv(location, reinterpret_cast<const GLfloat *>(&value)); +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(const char *name, const QVector3D& value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(int location, const QVector4D& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glVertexAttrib4fv(location, reinterpret_cast<const GLfloat *>(&value)); +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(const char *name, const QVector4D& value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(int location, const QColor& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {GLfloat(value.redF()), GLfloat(value.greenF()), + GLfloat(value.blueF()), GLfloat(value.alphaF())}; + d->glfuncs->glVertexAttrib4fv(location, values); + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(const char *name, const QColor& value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to the + contents of \a values, which contains \a columns elements, each + consisting of \a rows elements. The \a rows value should be + 1, 2, 3, or 4. This function is typically used to set matrix + values and column vectors. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue + (int location, const GLfloat *values, int columns, int rows) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (rows < 1 || rows > 4) { + qWarning() << "QOpenGLShaderProgram::setAttributeValue: rows" << rows << "not supported"; + return; + } + if (location != -1) { + while (columns-- > 0) { + if (rows == 1) + d->glfuncs->glVertexAttrib1fv(location, values); + else if (rows == 2) + d->glfuncs->glVertexAttrib2fv(location, values); + else if (rows == 3) + d->glfuncs->glVertexAttrib3fv(location, values); + else + d->glfuncs->glVertexAttrib4fv(location, values); + values += rows; + ++location; + } + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to the + contents of \a values, which contains \a columns elements, each + consisting of \a rows elements. The \a rows value should be + 1, 2, 3, or 4. This function is typically used to set matrix + values and column vectors. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue + (const char *name, const GLfloat *values, int columns, int rows) +{ + setAttributeValue(attributeLocation(name), values, columns, rows); +} + +/*! + Sets an array of vertex \a values on the attribute at \a location + in this shader program. The \a tupleSize indicates the number of + components per vertex (1, 2, 3, or 4), and the \a stride indicates + the number of bytes between vertices. A default \a stride value + of zero indicates that the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on the \a location. Otherwise the value specified with + setAttributeValue() for \a location will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (int location, const GLfloat *values, int tupleSize, int stride) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + d->glfuncs->glVertexAttribPointer(location, tupleSize, GL_FLOAT, GL_FALSE, + stride, values); + } +} + +/*! + Sets an array of 2D vertex \a values on the attribute at \a location + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on the \a location. Otherwise the value specified with + setAttributeValue() for \a location will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (int location, const QVector2D *values, int stride) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + d->glfuncs->glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, + stride, values); + } +} + +/*! + Sets an array of 3D vertex \a values on the attribute at \a location + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on the \a location. Otherwise the value specified with + setAttributeValue() for \a location will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (int location, const QVector3D *values, int stride) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + d->glfuncs->glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, + stride, values); + } +} + +/*! + Sets an array of 4D vertex \a values on the attribute at \a location + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on the \a location. Otherwise the value specified with + setAttributeValue() for \a location will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (int location, const QVector4D *values, int stride) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + d->glfuncs->glVertexAttribPointer(location, 4, GL_FLOAT, GL_FALSE, + stride, values); + } +} + +/*! + Sets an array of vertex \a values on the attribute at \a location + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The \a type indicates the type of elements in the \a values array, + usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a tupleSize + indicates the number of components per vertex: 1, 2, 3, or 4. + + The array will become active when enableAttributeArray() is called + on the \a location. Otherwise the value specified with + setAttributeValue() for \a location will be used. + + The setAttributeBuffer() function can be used to set the attribute + array to an offset within a vertex buffer. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray(), setAttributeBuffer() +*/ +void QOpenGLShaderProgram::setAttributeArray + (int location, GLenum type, const void *values, int tupleSize, int stride) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + d->glfuncs->glVertexAttribPointer(location, tupleSize, type, GL_TRUE, + stride, values); + } +} + +/*! + \overload + + Sets an array of vertex \a values on the attribute called \a name + in this shader program. The \a tupleSize indicates the number of + components per vertex (1, 2, 3, or 4), and the \a stride indicates + the number of bytes between vertices. A default \a stride value + of zero indicates that the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on \a name. Otherwise the value specified with setAttributeValue() + for \a name will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (const char *name, const GLfloat *values, int tupleSize, int stride) +{ + setAttributeArray(attributeLocation(name), values, tupleSize, stride); +} + +/*! + \overload + + Sets an array of 2D vertex \a values on the attribute called \a name + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on \a name. Otherwise the value specified with setAttributeValue() + for \a name will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (const char *name, const QVector2D *values, int stride) +{ + setAttributeArray(attributeLocation(name), values, stride); +} + +/*! + \overload + + Sets an array of 3D vertex \a values on the attribute called \a name + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on \a name. Otherwise the value specified with setAttributeValue() + for \a name will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (const char *name, const QVector3D *values, int stride) +{ + setAttributeArray(attributeLocation(name), values, stride); +} + +/*! + \overload + + Sets an array of 4D vertex \a values on the attribute called \a name + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on \a name. Otherwise the value specified with setAttributeValue() + for \a name will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (const char *name, const QVector4D *values, int stride) +{ + setAttributeArray(attributeLocation(name), values, stride); +} + +/*! + \overload + + Sets an array of vertex \a values on the attribute called \a name + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The \a type indicates the type of elements in the \a values array, + usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a tupleSize + indicates the number of components per vertex: 1, 2, 3, or 4. + + The array will become active when enableAttributeArray() is called + on the \a name. Otherwise the value specified with + setAttributeValue() for \a name will be used. + + The setAttributeBuffer() function can be used to set the attribute + array to an offset within a vertex buffer. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray(), setAttributeBuffer() +*/ +void QOpenGLShaderProgram::setAttributeArray + (const char *name, GLenum type, const void *values, int tupleSize, int stride) +{ + setAttributeArray(attributeLocation(name), type, values, tupleSize, stride); +} + +/*! + Sets an array of vertex values on the attribute at \a location in + this shader program, starting at a specific \a offset in the + currently bound vertex buffer. The \a stride indicates the number + of bytes between vertices. A default \a stride value of zero + indicates that the vertices are densely packed in the value array. + + The \a type indicates the type of elements in the vertex value + array, usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a + tupleSize indicates the number of components per vertex: 1, 2, 3, + or 4. + + The array will become active when enableAttributeArray() is called + on the \a location. Otherwise the value specified with + setAttributeValue() for \a location will be used. + + \sa setAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeBuffer + (int location, GLenum type, int offset, int tupleSize, int stride) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + d->glfuncs->glVertexAttribPointer(location, tupleSize, type, GL_TRUE, stride, + reinterpret_cast<const void *>(offset)); + } +} + +/*! + \overload + + Sets an array of vertex values on the attribute called \a name + in this shader program, starting at a specific \a offset in the + currently bound vertex buffer. The \a stride indicates the number + of bytes between vertices. A default \a stride value of zero + indicates that the vertices are densely packed in the value array. + + The \a type indicates the type of elements in the vertex value + array, usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a + tupleSize indicates the number of components per vertex: 1, 2, 3, + or 4. + + The array will become active when enableAttributeArray() is called + on the \a name. Otherwise the value specified with + setAttributeValue() for \a name will be used. + + \sa setAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeBuffer + (const char *name, GLenum type, int offset, int tupleSize, int stride) +{ + setAttributeBuffer(attributeLocation(name), type, offset, tupleSize, stride); +} + +/*! + Enables the vertex array at \a location in this shader program + so that the value set by setAttributeArray() on \a location + will be used by the shader program. + + \sa disableAttributeArray(), setAttributeArray(), setAttributeValue() + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::enableAttributeArray(int location) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glEnableVertexAttribArray(location); +} + +/*! + \overload + + Enables the vertex array called \a name in this shader program + so that the value set by setAttributeArray() on \a name + will be used by the shader program. + + \sa disableAttributeArray(), setAttributeArray(), setAttributeValue() + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::enableAttributeArray(const char *name) +{ + enableAttributeArray(attributeLocation(name)); +} + +/*! + Disables the vertex array at \a location in this shader program + that was enabled by a previous call to enableAttributeArray(). + + \sa enableAttributeArray(), setAttributeArray(), setAttributeValue() + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::disableAttributeArray(int location) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glDisableVertexAttribArray(location); +} + +/*! + \overload + + Disables the vertex array called \a name in this shader program + that was enabled by a previous call to enableAttributeArray(). + + \sa enableAttributeArray(), setAttributeArray(), setAttributeValue() + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::disableAttributeArray(const char *name) +{ + disableAttributeArray(attributeLocation(name)); +} + +/*! + Returns the location of the uniform variable \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + uniform variable for this shader program. + + \sa attributeLocation() +*/ +int QOpenGLShaderProgram::uniformLocation(const char *name) const +{ + Q_D(const QOpenGLShaderProgram); + Q_UNUSED(d); + if (d->linked && d->programGuard && d->programGuard->id()) { + return d->glfuncs->glGetUniformLocation(d->programGuard->id(), name); + } else { + qWarning() << "QOpenGLShaderProgram::uniformLocation(" << name + << "): shader program is not linked"; + return -1; + } +} + +/*! + \overload + + Returns the location of the uniform variable \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + uniform variable for this shader program. + + \sa attributeLocation() +*/ +int QOpenGLShaderProgram::uniformLocation(const QByteArray& name) const +{ + return uniformLocation(name.constData()); +} + +/*! + \overload + + Returns the location of the uniform variable \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + uniform variable for this shader program. + + \sa attributeLocation() +*/ +int QOpenGLShaderProgram::uniformLocation(const QString& name) const +{ + return uniformLocation(name.toLatin1().constData()); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, GLfloat value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform1fv(location, 1, &value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, GLfloat value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, GLint value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform1i(location, value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, GLint value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + This function should be used when setting sampler values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, GLuint value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform1i(location, value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. This function should be used when setting sampler values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, GLuint value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to + the 2D vector (\a x, \a y). + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, GLfloat x, GLfloat y) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[2] = {x, y}; + d->glfuncs->glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to + the 2D vector (\a x, \a y). + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, GLfloat x, GLfloat y) +{ + setUniformValue(uniformLocation(name), x, y); +} + +/*! + Sets the uniform variable at \a location in the current context to + the 3D vector (\a x, \a y, \a z). + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue + (int location, GLfloat x, GLfloat y, GLfloat z) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[3] = {x, y, z}; + d->glfuncs->glUniform3fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to + the 3D vector (\a x, \a y, \a z). + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue + (const char *name, GLfloat x, GLfloat y, GLfloat z) +{ + setUniformValue(uniformLocation(name), x, y, z); +} + +/*! + Sets the uniform variable at \a location in the current context to + the 4D vector (\a x, \a y, \a z, \a w). + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue + (int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {x, y, z, w}; + d->glfuncs->glUniform4fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to + the 4D vector (\a x, \a y, \a z, \a w). + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue + (const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + setUniformValue(uniformLocation(name), x, y, z, w); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QVector2D& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform2fv(location, 1, reinterpret_cast<const GLfloat *>(&value)); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QVector2D& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QVector3D& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform3fv(location, 1, reinterpret_cast<const GLfloat *>(&value)); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QVector3D& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QVector4D& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform4fv(location, 1, reinterpret_cast<const GLfloat *>(&value)); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QVector4D& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to + the red, green, blue, and alpha components of \a color. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QColor& color) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {GLfloat(color.redF()), GLfloat(color.greenF()), + GLfloat(color.blueF()), GLfloat(color.alphaF())}; + d->glfuncs->glUniform4fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to + the red, green, blue, and alpha components of \a color. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QColor& color) +{ + setUniformValue(uniformLocation(name), color); +} + +/*! + Sets the uniform variable at \a location in the current context to + the x and y coordinates of \a point. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QPoint& point) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {GLfloat(point.x()), GLfloat(point.y())}; + d->glfuncs->glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable associated with \a name in the current + context to the x and y coordinates of \a point. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QPoint& point) +{ + setUniformValue(uniformLocation(name), point); +} + +/*! + Sets the uniform variable at \a location in the current context to + the x and y coordinates of \a point. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QPointF& point) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {GLfloat(point.x()), GLfloat(point.y())}; + d->glfuncs->glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable associated with \a name in the current + context to the x and y coordinates of \a point. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QPointF& point) +{ + setUniformValue(uniformLocation(name), point); +} + +/*! + Sets the uniform variable at \a location in the current context to + the width and height of the given \a size. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QSize& size) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {GLfloat(size.width()), GLfloat(size.height())}; + d->glfuncs->glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable associated with \a name in the current + context to the width and height of the given \a size. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QSize& size) +{ + setUniformValue(uniformLocation(name), size); +} + +/*! + Sets the uniform variable at \a location in the current context to + the width and height of the given \a size. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QSizeF& size) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {GLfloat(size.width()), GLfloat(size.height())}; + d->glfuncs->glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable associated with \a name in the current + context to the width and height of the given \a size. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QSizeF& size) +{ + setUniformValue(uniformLocation(name), size); +} + +// We have to repack matrices from qreal to GLfloat. +#define setUniformMatrix(func,location,value,cols,rows) \ + if (location == -1) \ + return; \ + if (sizeof(qreal) == sizeof(GLfloat)) { \ + func(location, 1, GL_FALSE, \ + reinterpret_cast<const GLfloat *>(value.constData())); \ + } else { \ + GLfloat mat[cols * rows]; \ + const qreal *data = value.constData(); \ + for (int i = 0; i < cols * rows; ++i) \ + mat[i] = data[i]; \ + func(location, 1, GL_FALSE, mat); \ + } +#define setUniformGenericMatrix(colfunc,location,value,cols,rows) \ + if (location == -1) \ + return; \ + if (sizeof(qreal) == sizeof(GLfloat)) { \ + const GLfloat *data = reinterpret_cast<const GLfloat *> \ + (value.constData()); \ + colfunc(location, cols, data); \ + } else { \ + GLfloat mat[cols * rows]; \ + const qreal *data = value.constData(); \ + for (int i = 0; i < cols * rows; ++i) \ + mat[i] = data[i]; \ + colfunc(location, cols, mat); \ + } + +/*! + Sets the uniform variable at \a location in the current context + to a 2x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix2x2& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformMatrix(d->glfuncs->glUniformMatrix2fv, location, value, 2, 2); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 2x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix2x2& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 2x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix2x3& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrix + (d->glfuncs->glUniform3fv, location, value, 2, 3); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 2x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix2x3& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 2x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix2x4& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrix + (d->glfuncs->glUniform4fv, location, value, 2, 4); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 2x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix2x4& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 3x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix3x2& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrix + (d->glfuncs->glUniform2fv, location, value, 3, 2); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 3x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix3x2& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 3x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix3x3& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformMatrix(d->glfuncs->glUniformMatrix3fv, location, value, 3, 3); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 3x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix3x3& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 3x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix3x4& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrix + (d->glfuncs->glUniform4fv, location, value, 3, 4); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 3x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix3x4& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 4x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix4x2& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrix + (d->glfuncs->glUniform2fv, location, value, 4, 2); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 4x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix4x2& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 4x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix4x3& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrix + (d->glfuncs->glUniform3fv, location, value, 4, 3); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 4x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix4x3& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 4x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix4x4& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformMatrix(d->glfuncs->glUniformMatrix4fv, location, value, 4, 4); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 4x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix4x4& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + \overload + + Sets the uniform variable at \a location in the current context + to a 2x2 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const GLfloat value[2][2]) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniformMatrix2fv(location, 1, GL_FALSE, value[0]); +} + +/*! + \overload + + Sets the uniform variable at \a location in the current context + to a 3x3 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const GLfloat value[3][3]) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniformMatrix3fv(location, 1, GL_FALSE, value[0]); +} + +/*! + \overload + + Sets the uniform variable at \a location in the current context + to a 4x4 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const GLfloat value[4][4]) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniformMatrix4fv(location, 1, GL_FALSE, value[0]); +} + + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 2x2 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const GLfloat value[2][2]) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 3x3 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const GLfloat value[3][3]) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 4x4 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const GLfloat value[4][4]) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to a + 3x3 transformation matrix \a value that is specified as a QTransform value. + + To set a QTransform value as a 4x4 matrix in a shader, use + \c{setUniformValue(location, QMatrix4x4(value))}. +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QTransform& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat mat[3][3] = { + {GLfloat(value.m11()), GLfloat(value.m12()), GLfloat(value.m13())}, + {GLfloat(value.m21()), GLfloat(value.m22()), GLfloat(value.m23())}, + {GLfloat(value.m31()), GLfloat(value.m32()), GLfloat(value.m33())} + }; + d->glfuncs->glUniformMatrix3fv(location, 1, GL_FALSE, mat[0]); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to a + 3x3 transformation matrix \a value that is specified as a QTransform value. + + To set a QTransform value as a 4x4 matrix in a shader, use + \c{setUniformValue(name, QMatrix4x4(value))}. +*/ +void QOpenGLShaderProgram::setUniformValue + (const char *name, const QTransform& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const GLint *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform1iv(location, count, values); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray + (const char *name, const GLint *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count elements of \a values. This overload + should be used when setting an array of sampler values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const GLuint *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform1iv(location, count, reinterpret_cast<const GLint *>(values)); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count elements of \a values. This overload + should be used when setting an array of sampler values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray + (const char *name, const GLuint *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count elements of \a values. Each element + has \a tupleSize components. The \a tupleSize must be 1, 2, 3, or 4. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const GLfloat *values, int count, int tupleSize) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + if (tupleSize == 1) + d->glfuncs->glUniform1fv(location, count, values); + else if (tupleSize == 2) + d->glfuncs->glUniform2fv(location, count, values); + else if (tupleSize == 3) + d->glfuncs->glUniform3fv(location, count, values); + else if (tupleSize == 4) + d->glfuncs->glUniform4fv(location, count, values); + else + qWarning() << "QOpenGLShaderProgram::setUniformValue: size" << tupleSize << "not supported"; + } +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count elements of \a values. Each element + has \a tupleSize components. The \a tupleSize must be 1, 2, 3, or 4. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray + (const char *name, const GLfloat *values, int count, int tupleSize) +{ + setUniformValueArray(uniformLocation(name), values, count, tupleSize); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 2D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QVector2D *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform2fv(location, count, reinterpret_cast<const GLfloat *>(values)); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 2D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QVector2D *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 3D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QVector3D *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform3fv(location, count, reinterpret_cast<const GLfloat *>(values)); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 3D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QVector3D *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 4D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QVector4D *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform4fv(location, count, reinterpret_cast<const GLfloat *>(values)); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 4D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QVector4D *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +// We have to repack matrix arrays from qreal to GLfloat. +#define setUniformMatrixArray(func,location,values,count,type,cols,rows) \ + if (location == -1 || count <= 0) \ + return; \ + if (sizeof(type) == sizeof(GLfloat) * cols * rows) { \ + func(location, count, GL_FALSE, \ + reinterpret_cast<const GLfloat *>(values[0].constData())); \ + } else { \ + QVarLengthArray<GLfloat> temp(cols * rows * count); \ + for (int index = 0; index < count; ++index) { \ + for (int index2 = 0; index2 < (cols * rows); ++index2) { \ + temp.data()[cols * rows * index + index2] = \ + values[index].constData()[index2]; \ + } \ + } \ + func(location, count, GL_FALSE, temp.constData()); \ + } +#define setUniformGenericMatrixArray(colfunc,location,values,count,type,cols,rows) \ + if (location == -1 || count <= 0) \ + return; \ + if (sizeof(type) == sizeof(GLfloat) * cols * rows) { \ + const GLfloat *data = reinterpret_cast<const GLfloat *> \ + (values[0].constData()); \ + colfunc(location, count * cols, data); \ + } else { \ + QVarLengthArray<GLfloat> temp(cols * rows * count); \ + for (int index = 0; index < count; ++index) { \ + for (int index2 = 0; index2 < (cols * rows); ++index2) { \ + temp.data()[cols * rows * index + index2] = \ + values[index].constData()[index2]; \ + } \ + } \ + colfunc(location, count * cols, temp.constData()); \ + } + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 2x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix2x2 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformMatrixArray + (d->glfuncs->glUniformMatrix2fv, location, values, count, QMatrix2x2, 2, 2); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 2x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix2x2 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 2x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix2x3 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrixArray + (d->glfuncs->glUniform3fv, location, values, count, + QMatrix2x3, 2, 3); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 2x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix2x3 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 2x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix2x4 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrixArray + (d->glfuncs->glUniform4fv, location, values, count, + QMatrix2x4, 2, 4); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 2x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix2x4 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 3x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix3x2 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrixArray + (d->glfuncs->glUniform2fv, location, values, count, + QMatrix3x2, 3, 2); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 3x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix3x2 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 3x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix3x3 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformMatrixArray + (d->glfuncs->glUniformMatrix3fv, location, values, count, QMatrix3x3, 3, 3); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 3x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix3x3 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 3x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix3x4 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrixArray + (d->glfuncs->glUniform4fv, location, values, count, + QMatrix3x4, 3, 4); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 3x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix3x4 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 4x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix4x2 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrixArray + (d->glfuncs->glUniform2fv, location, values, count, + QMatrix4x2, 4, 2); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 4x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x2 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 4x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix4x3 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrixArray + (d->glfuncs->glUniform3fv, location, values, count, + QMatrix4x3, 4, 3); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 4x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x3 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 4x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix4x4 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformMatrixArray + (d->glfuncs->glUniformMatrix4fv, location, values, count, QMatrix4x4, 4, 4); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 4x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x4 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Returns the hardware limit for how many vertices a geometry shader + can output. + + \sa setGeometryOutputVertexCount() +*/ +int QOpenGLShaderProgram::maxGeometryOutputVertices() const +{ + GLint n = 0; +// glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT, &n); + return n; +} + +/*! + Sets the maximum number of vertices the current geometry shader + program will produce, if active, to \a count. + + This parameter takes effect the next time the program is linked. +*/ +void QOpenGLShaderProgram::setGeometryOutputVertexCount(int count) +{ +#ifndef QT_NO_DEBUG + int max = maxGeometryOutputVertices(); + if (count > max) { + qWarning("QOpenGLShaderProgram::setGeometryOutputVertexCount: count: %d higher than maximum: %d", + count, max); + } +#endif + d_func()->geometryVertexCount = count; +} + + +/*! + Returns the maximum number of vertices the current geometry shader + program will produce, if active. + + This parameter takes effect the ntext time the program is linked. +*/ +int QOpenGLShaderProgram::geometryOutputVertexCount() const +{ + return d_func()->geometryVertexCount; +} + + +/*! + Sets the input type from \a inputType. + + This parameter takes effect the next time the program is linked. +*/ +void QOpenGLShaderProgram::setGeometryInputType(GLenum inputType) +{ + d_func()->geometryInputType = inputType; +} + + +/*! + Returns the geometry shader input type, if active. + + This parameter takes effect the next time the program is linked. + */ + +GLenum QOpenGLShaderProgram::geometryInputType() const +{ + return d_func()->geometryInputType; +} + + +/*! + Sets the output type from the geometry shader, if active, to + \a outputType. + + This parameter takes effect the next time the program is linked. +*/ +void QOpenGLShaderProgram::setGeometryOutputType(GLenum outputType) +{ + d_func()->geometryOutputType = outputType; +} + + +/*! + Returns the geometry shader output type, if active. + + This parameter takes effect the next time the program is linked. + */ +GLenum QOpenGLShaderProgram::geometryOutputType() const +{ + return d_func()->geometryOutputType; +} + + +/*! + Returns true if shader programs written in the OpenGL Shading + Language (GLSL) are supported on this system; false otherwise. + + The \a context is used to resolve the GLSL extensions. + If \a context is null, then QOpenGLContext::currentContext() is used. +*/ +bool QOpenGLShaderProgram::hasOpenGLShaderPrograms(QOpenGLContext *context) +{ +#if !defined(QT_OPENGL_ES_2) + if (!context) + context = QOpenGLContext::currentContext(); + if (!context) + return false; + return QOpenGLFunctions(context).hasOpenGLFeature(QOpenGLFunctions::Shaders); +#else + Q_UNUSED(context); + return true; +#endif +} + +/*! + \internal +*/ +void QOpenGLShaderProgram::shaderDestroyed() +{ + Q_D(QOpenGLShaderProgram); + QOpenGLShader *shader = qobject_cast<QOpenGLShader *>(sender()); + if (shader && !d->removingShaders) + removeShader(shader); +} + +/*! + Returns true if shader programs of type \a type are supported on + this system; false otherwise. + + The \a context is used to resolve the GLSL extensions. + If \a context is null, then QOpenGLContext::currentContext() is used. +*/ +bool QOpenGLShader::hasOpenGLShaders(ShaderType type, QOpenGLContext *context) +{ + if (!context) + context = QOpenGLContext::currentContext(); + if (!context) + return false; + + if ((type & ~(Geometry | Vertex | Fragment)) || type == 0) + return false; + +#if 0 + bool resolved = qt_resolve_glsl_extensions(const_cast<QOpenGLContext *>(context)); + if (!resolved) + return false; + + if ((type & Geometry) && !QByteArray((const char *) glGetString(GL_EXTENSIONS)).contains("GL_EXT_geometry_shader4")) + return false; +#endif + + return true; +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglshaderprogram.h b/src/gui/opengl/qopenglshaderprogram.h new file mode 100644 index 0000000000..4c123749a2 --- /dev/null +++ b/src/gui/opengl/qopenglshaderprogram.h @@ -0,0 +1,317 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGLSHADERPROGRAM_H +#define QOPENGLSHADERPROGRAM_H + +#include <QtGui/qopengl.h> +#include <QtGui/qvector2d.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> +#include <QtGui/qmatrix4x4.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QOpenGLContext; +class QOpenGLShaderProgram; +class QOpenGLShaderPrivate; + +class Q_GUI_EXPORT QOpenGLShader : public QObject +{ + Q_OBJECT +public: + enum ShaderTypeBit + { + Vertex = 0x0001, + Fragment = 0x0002, + Geometry = 0x0004 + }; + Q_DECLARE_FLAGS(ShaderType, ShaderTypeBit) + + explicit QOpenGLShader(QOpenGLShader::ShaderType type, QObject *parent = 0); + virtual ~QOpenGLShader(); + + QOpenGLShader::ShaderType shaderType() const; + + bool compileSourceCode(const char *source); + bool compileSourceCode(const QByteArray& source); + bool compileSourceCode(const QString& source); + bool compileSourceFile(const QString& fileName); + + QByteArray sourceCode() const; + + bool isCompiled() const; + QString log() const; + + GLuint shaderId() const; + + static bool hasOpenGLShaders(ShaderType type, QOpenGLContext *context = 0); + +private: + friend class QOpenGLShaderProgram; + + Q_DISABLE_COPY(QOpenGLShader) + Q_DECLARE_PRIVATE(QOpenGLShader) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLShader::ShaderType) + + +class QOpenGLShaderProgramPrivate; + +#ifndef GL_EXT_geometry_shader4 +# define GL_LINES_ADJACENCY_EXT 0xA +# define GL_LINE_STRIP_ADJACENCY_EXT 0xB +# define GL_TRIANGLES_ADJACENCY_EXT 0xC +# define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0xD +#endif + + +class Q_GUI_EXPORT QOpenGLShaderProgram : public QObject +{ + Q_OBJECT +public: + explicit QOpenGLShaderProgram(QObject *parent = 0); + virtual ~QOpenGLShaderProgram(); + + bool addShader(QOpenGLShader *shader); + void removeShader(QOpenGLShader *shader); + QList<QOpenGLShader *> shaders() const; + + bool addShaderFromSourceCode(QOpenGLShader::ShaderType type, const char *source); + bool addShaderFromSourceCode(QOpenGLShader::ShaderType type, const QByteArray& source); + bool addShaderFromSourceCode(QOpenGLShader::ShaderType type, const QString& source); + bool addShaderFromSourceFile(QOpenGLShader::ShaderType type, const QString& fileName); + + void removeAllShaders(); + + virtual bool link(); + bool isLinked() const; + QString log() const; + + bool bind(); + void release(); + + GLuint programId() const; + + int maxGeometryOutputVertices() const; + + void setGeometryOutputVertexCount(int count); + int geometryOutputVertexCount() const; + + void setGeometryInputType(GLenum inputType); + GLenum geometryInputType() const; + + void setGeometryOutputType(GLenum outputType); + GLenum geometryOutputType() const; + + void bindAttributeLocation(const char *name, int location); + void bindAttributeLocation(const QByteArray& name, int location); + void bindAttributeLocation(const QString& name, int location); + + int attributeLocation(const char *name) const; + int attributeLocation(const QByteArray& name) const; + int attributeLocation(const QString& name) const; + + void setAttributeValue(int location, GLfloat value); + void setAttributeValue(int location, GLfloat x, GLfloat y); + void setAttributeValue(int location, GLfloat x, GLfloat y, GLfloat z); + void setAttributeValue(int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void setAttributeValue(int location, const QVector2D& value); + void setAttributeValue(int location, const QVector3D& value); + void setAttributeValue(int location, const QVector4D& value); + void setAttributeValue(int location, const QColor& value); + void setAttributeValue(int location, const GLfloat *values, int columns, int rows); + + void setAttributeValue(const char *name, GLfloat value); + void setAttributeValue(const char *name, GLfloat x, GLfloat y); + void setAttributeValue(const char *name, GLfloat x, GLfloat y, GLfloat z); + void setAttributeValue(const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void setAttributeValue(const char *name, const QVector2D& value); + void setAttributeValue(const char *name, const QVector3D& value); + void setAttributeValue(const char *name, const QVector4D& value); + void setAttributeValue(const char *name, const QColor& value); + void setAttributeValue(const char *name, const GLfloat *values, int columns, int rows); + + void setAttributeArray + (int location, const GLfloat *values, int tupleSize, int stride = 0); + void setAttributeArray + (int location, const QVector2D *values, int stride = 0); + void setAttributeArray + (int location, const QVector3D *values, int stride = 0); + void setAttributeArray + (int location, const QVector4D *values, int stride = 0); + void setAttributeArray + (int location, GLenum type, const void *values, int tupleSize, int stride = 0); + void setAttributeArray + (const char *name, const GLfloat *values, int tupleSize, int stride = 0); + void setAttributeArray + (const char *name, const QVector2D *values, int stride = 0); + void setAttributeArray + (const char *name, const QVector3D *values, int stride = 0); + void setAttributeArray + (const char *name, const QVector4D *values, int stride = 0); + void setAttributeArray + (const char *name, GLenum type, const void *values, int tupleSize, int stride = 0); + + void setAttributeBuffer + (int location, GLenum type, int offset, int tupleSize, int stride = 0); + void setAttributeBuffer + (const char *name, GLenum type, int offset, int tupleSize, int stride = 0); + + void enableAttributeArray(int location); + void enableAttributeArray(const char *name); + void disableAttributeArray(int location); + void disableAttributeArray(const char *name); + + int uniformLocation(const char *name) const; + int uniformLocation(const QByteArray& name) const; + int uniformLocation(const QString& name) const; + + void setUniformValue(int location, GLfloat value); + void setUniformValue(int location, GLint value); + void setUniformValue(int location, GLuint value); + void setUniformValue(int location, GLfloat x, GLfloat y); + void setUniformValue(int location, GLfloat x, GLfloat y, GLfloat z); + void setUniformValue(int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void setUniformValue(int location, const QVector2D& value); + void setUniformValue(int location, const QVector3D& value); + void setUniformValue(int location, const QVector4D& value); + void setUniformValue(int location, const QColor& color); + void setUniformValue(int location, const QPoint& point); + void setUniformValue(int location, const QPointF& point); + void setUniformValue(int location, const QSize& size); + void setUniformValue(int location, const QSizeF& size); + void setUniformValue(int location, const QMatrix2x2& value); + void setUniformValue(int location, const QMatrix2x3& value); + void setUniformValue(int location, const QMatrix2x4& value); + void setUniformValue(int location, const QMatrix3x2& value); + void setUniformValue(int location, const QMatrix3x3& value); + void setUniformValue(int location, const QMatrix3x4& value); + void setUniformValue(int location, const QMatrix4x2& value); + void setUniformValue(int location, const QMatrix4x3& value); + void setUniformValue(int location, const QMatrix4x4& value); + void setUniformValue(int location, const GLfloat value[2][2]); + void setUniformValue(int location, const GLfloat value[3][3]); + void setUniformValue(int location, const GLfloat value[4][4]); + void setUniformValue(int location, const QTransform& value); + + void setUniformValue(const char *name, GLfloat value); + void setUniformValue(const char *name, GLint value); + void setUniformValue(const char *name, GLuint value); + void setUniformValue(const char *name, GLfloat x, GLfloat y); + void setUniformValue(const char *name, GLfloat x, GLfloat y, GLfloat z); + void setUniformValue(const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void setUniformValue(const char *name, const QVector2D& value); + void setUniformValue(const char *name, const QVector3D& value); + void setUniformValue(const char *name, const QVector4D& value); + void setUniformValue(const char *name, const QColor& color); + void setUniformValue(const char *name, const QPoint& point); + void setUniformValue(const char *name, const QPointF& point); + void setUniformValue(const char *name, const QSize& size); + void setUniformValue(const char *name, const QSizeF& size); + void setUniformValue(const char *name, const QMatrix2x2& value); + void setUniformValue(const char *name, const QMatrix2x3& value); + void setUniformValue(const char *name, const QMatrix2x4& value); + void setUniformValue(const char *name, const QMatrix3x2& value); + void setUniformValue(const char *name, const QMatrix3x3& value); + void setUniformValue(const char *name, const QMatrix3x4& value); + void setUniformValue(const char *name, const QMatrix4x2& value); + void setUniformValue(const char *name, const QMatrix4x3& value); + void setUniformValue(const char *name, const QMatrix4x4& value); + void setUniformValue(const char *name, const GLfloat value[2][2]); + void setUniformValue(const char *name, const GLfloat value[3][3]); + void setUniformValue(const char *name, const GLfloat value[4][4]); + void setUniformValue(const char *name, const QTransform& value); + + void setUniformValueArray(int location, const GLfloat *values, int count, int tupleSize); + void setUniformValueArray(int location, const GLint *values, int count); + void setUniformValueArray(int location, const GLuint *values, int count); + void setUniformValueArray(int location, const QVector2D *values, int count); + void setUniformValueArray(int location, const QVector3D *values, int count); + void setUniformValueArray(int location, const QVector4D *values, int count); + void setUniformValueArray(int location, const QMatrix2x2 *values, int count); + void setUniformValueArray(int location, const QMatrix2x3 *values, int count); + void setUniformValueArray(int location, const QMatrix2x4 *values, int count); + void setUniformValueArray(int location, const QMatrix3x2 *values, int count); + void setUniformValueArray(int location, const QMatrix3x3 *values, int count); + void setUniformValueArray(int location, const QMatrix3x4 *values, int count); + void setUniformValueArray(int location, const QMatrix4x2 *values, int count); + void setUniformValueArray(int location, const QMatrix4x3 *values, int count); + void setUniformValueArray(int location, const QMatrix4x4 *values, int count); + + void setUniformValueArray(const char *name, const GLfloat *values, int count, int tupleSize); + void setUniformValueArray(const char *name, const GLint *values, int count); + void setUniformValueArray(const char *name, const GLuint *values, int count); + void setUniformValueArray(const char *name, const QVector2D *values, int count); + void setUniformValueArray(const char *name, const QVector3D *values, int count); + void setUniformValueArray(const char *name, const QVector4D *values, int count); + void setUniformValueArray(const char *name, const QMatrix2x2 *values, int count); + void setUniformValueArray(const char *name, const QMatrix2x3 *values, int count); + void setUniformValueArray(const char *name, const QMatrix2x4 *values, int count); + void setUniformValueArray(const char *name, const QMatrix3x2 *values, int count); + void setUniformValueArray(const char *name, const QMatrix3x3 *values, int count); + void setUniformValueArray(const char *name, const QMatrix3x4 *values, int count); + void setUniformValueArray(const char *name, const QMatrix4x2 *values, int count); + void setUniformValueArray(const char *name, const QMatrix4x3 *values, int count); + void setUniformValueArray(const char *name, const QMatrix4x4 *values, int count); + + static bool hasOpenGLShaderPrograms(QOpenGLContext *context = 0); + +private Q_SLOTS: + void shaderDestroyed(); + +private: + Q_DISABLE_COPY(QOpenGLShaderProgram) + Q_DECLARE_PRIVATE(QOpenGLShaderProgram) + + bool init(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/opengl/qopengltextureglyphcache.cpp b/src/gui/opengl/qopengltextureglyphcache.cpp new file mode 100644 index 0000000000..ffce6f55bb --- /dev/null +++ b/src/gui/opengl/qopengltextureglyphcache.cpp @@ -0,0 +1,396 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qopengltextureglyphcache_p.h" +#include "qopenglpaintengine_p.h" +#include "private/qopenglengineshadersource_p.h" + +QT_BEGIN_NAMESPACE + +#ifdef Q_WS_WIN +extern Q_GUI_EXPORT bool qt_cleartype_enabled; +#endif + +QBasicAtomicInt qopengltextureglyphcache_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1); + +QOpenGLTextureGlyphCache::QOpenGLTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix) + : QImageTextureGlyphCache(type, matrix) + , pex(0) + , m_blitProgram(0) + , m_filterMode(Nearest) + , m_serialNumber(qopengltextureglyphcache_serial_number.fetchAndAddRelaxed(1)) +{ +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug(" -> QOpenGLTextureGlyphCache() %p for context %p.", this, QOpenGLContext::currentContext()); +#endif + m_vertexCoordinateArray[0] = -1.0f; + m_vertexCoordinateArray[1] = -1.0f; + m_vertexCoordinateArray[2] = 1.0f; + m_vertexCoordinateArray[3] = -1.0f; + m_vertexCoordinateArray[4] = 1.0f; + m_vertexCoordinateArray[5] = 1.0f; + m_vertexCoordinateArray[6] = -1.0f; + m_vertexCoordinateArray[7] = 1.0f; + + m_textureCoordinateArray[0] = 0.0f; + m_textureCoordinateArray[1] = 0.0f; + m_textureCoordinateArray[2] = 1.0f; + m_textureCoordinateArray[3] = 0.0f; + m_textureCoordinateArray[4] = 1.0f; + m_textureCoordinateArray[5] = 1.0f; + m_textureCoordinateArray[6] = 0.0f; + m_textureCoordinateArray[7] = 1.0f; +} + +QOpenGLTextureGlyphCache::~QOpenGLTextureGlyphCache() +{ +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug(" -> ~QOpenGLTextureGlyphCache() %p.", this); +#endif + delete m_blitProgram; +} + +void QOpenGLTextureGlyphCache::createTextureData(int width, int height) +{ + QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); + if (ctx == 0) { + qWarning("QOpenGLTextureGlyphCache::createTextureData: Called with no context"); + return; + } + + // create in QImageTextureGlyphCache baseclass is meant to be called + // only to create the initial image and does not preserve the content, + // so we don't call when this function is called from resize. + if (ctx->d_func()->workaround_brokenFBOReadBack && image().isNull()) + QImageTextureGlyphCache::createTextureData(width, height); + + // Make the lower glyph texture size 16 x 16. + if (width < 16) + width = 16; + if (height < 16) + height = 16; + + if (m_textureResource && !m_textureResource->m_texture) + delete m_textureResource; + + if (!m_textureResource) + m_textureResource = new QOpenGLGlyphTexture(ctx); + + glGenTextures(1, &m_textureResource->m_texture); + glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); + + m_textureResource->m_width = width; + m_textureResource->m_height = height; + + if (m_type == QFontEngineGlyphCache::Raster_RGBMask) { + QVarLengthArray<uchar> data(width * height * 4); + for (int i = 0; i < data.size(); ++i) + data[i] = 0; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); + } else { + QVarLengthArray<uchar> data(width * height); + for (int i = 0; i < data.size(); ++i) + data[i] = 0; + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &data[0]); + } + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_filterMode = Nearest; +} + +void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (ctx == 0) { + qWarning("QOpenGLTextureGlyphCache::resizeTextureData: Called with no context"); + return; + } + + int oldWidth = m_textureResource->m_width; + int oldHeight = m_textureResource->m_height; + + // Make the lower glyph texture size 16 x 16. + if (width < 16) + width = 16; + if (height < 16) + height = 16; + + GLuint oldTexture = m_textureResource->m_texture; + createTextureData(width, height); + + if (ctx->d_func()->workaround_brokenFBOReadBack) { + QImageTextureGlyphCache::resizeTextureData(width, height); + Q_ASSERT(image().depth() == 8); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, image().constBits()); + glDeleteTextures(1, &oldTexture); + return; + } + + // ### the QTextureGlyphCache API needs to be reworked to allow + // ### resizeTextureData to fail + + QOpenGLFunctions funcs(ctx); + + funcs.glBindFramebuffer(GL_FRAMEBUFFER, m_textureResource->m_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); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_filterMode = Nearest; + glBindTexture(GL_TEXTURE_2D, 0); + funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, tmp_texture, 0); + + funcs.glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); + glBindTexture(GL_TEXTURE_2D, oldTexture); + + if (pex != 0) + pex->transferMode(BrushDrawingMode); + + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + + glViewport(0, 0, oldWidth, oldHeight); + + QOpenGLShaderProgram *blitProgram = 0; + if (pex == 0) { + if (m_blitProgram == 0) { + m_blitProgram = new QOpenGLShaderProgram(ctx); + + { + QString source; + source.append(QLatin1String(qopenglslMainWithTexCoordsVertexShader)); + source.append(QLatin1String(qopenglslUntransformedPositionVertexShader)); + + QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, m_blitProgram); + vertexShader->compileSourceCode(source); + + m_blitProgram->addShader(vertexShader); + } + + { + QString source; + source.append(QLatin1String(qopenglslMainFragmentShader)); + source.append(QLatin1String(qopenglslImageSrcFragmentShader)); + + QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, m_blitProgram); + fragmentShader->compileSourceCode(source); + + m_blitProgram->addShader(fragmentShader); + } + + m_blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + m_blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + + m_blitProgram->link(); + } + + funcs.glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_vertexCoordinateArray); + funcs.glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_textureCoordinateArray); + + m_blitProgram->bind(); + m_blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); + m_blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR)); + m_blitProgram->disableAttributeArray(int(QT_OPACITY_ATTR)); + + blitProgram = m_blitProgram; + + } else { + pex->setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, m_vertexCoordinateArray); + pex->setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, m_textureCoordinateArray); + + pex->shaderManager->useBlitProgram(); + blitProgram = pex->shaderManager->blitProgram(); + } + + blitProgram->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); + + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight); + + funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, 0); + glDeleteTextures(1, &tmp_texture); + glDeleteTextures(1, &oldTexture); + + funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo); + + if (pex != 0) { + glViewport(0, 0, pex->width, pex->height); + pex->updateClipScissorTest(); + } +} + +void QOpenGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (ctx == 0) { + qWarning("QOpenGLTextureGlyphCache::fillTexture: Called with no context"); + return; + } + + if (ctx->d_func()->workaround_brokenFBOReadBack) { + QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition); + + glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); + const QImage &texture = image(); + const uchar *bits = texture.constBits(); + bits += c.y * texture.bytesPerLine() + c.x; + for (int i=0; i<c.h; ++i) { + glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, c.w, 1, GL_ALPHA, GL_UNSIGNED_BYTE, bits); + bits += texture.bytesPerLine(); + } + return; + } + + QImage mask = textureMapForGlyph(glyph, subPixelPosition); + const int maskWidth = mask.width(); + const int maskHeight = mask.height(); + + if (mask.format() == QImage::Format_Mono) { + mask = mask.convertToFormat(QImage::Format_Indexed8); + for (int y = 0; y < maskHeight; ++y) { + uchar *src = (uchar *) mask.scanLine(y); + for (int x = 0; x < maskWidth; ++x) + src[x] = -src[x]; // convert 0 and 1 into 0 and 255 + } + } else if (mask.format() == QImage::Format_RGB32) { + // Make the alpha component equal to the average of the RGB values. + // This is needed when drawing sub-pixel antialiased text on translucent targets. + for (int y = 0; y < maskHeight; ++y) { + quint32 *src = (quint32 *) mask.scanLine(y); + for (int x = 0; x < maskWidth; ++x) { + uchar r = src[x] >> 16; + uchar g = src[x] >> 8; + uchar b = src[x]; + quint32 avg = (quint32(r) + quint32(g) + quint32(b) + 1) / 3; // "+1" for rounding. + src[x] = (src[x] & 0x00ffffff) | (avg << 24); + } + } + } + + glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); + if (mask.format() == QImage::Format_RGB32) { + glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_BGRA, GL_UNSIGNED_BYTE, mask.bits()); + } else { + // glTexSubImage2D() might cause some garbage to appear in the texture if the mask width is + // not a multiple of four bytes. The bug appeared on a computer with 32-bit Windows Vista + // and nVidia GeForce 8500GT. GL_UNPACK_ALIGNMENT is set to four bytes, 'mask' has a + // multiple of four bytes per line, and most of the glyph shows up correctly in the + // texture, which makes me think that this is a driver bug. + // One workaround is to make sure the mask width is a multiple of four bytes, for instance + // by converting it to a format with four bytes per pixel. Another is to copy one line at a + // time. + +#if 0 + if (!ctx->d_func()->workaround_brokenAlphaTexSubImage_init) { + // don't know which driver versions exhibit this bug, so be conservative for now + const QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION))); + glctx->d_func()->workaround_brokenAlphaTexSubImage = versionString.indexOf("NVIDIA") >= 0; + glctx->d_func()->workaround_brokenAlphaTexSubImage_init = true; + } +#endif + +#if 0 + if (ctx->d_func()->workaround_brokenAlphaTexSubImage) { + for (int i = 0; i < maskHeight; ++i) + glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, maskWidth, 1, GL_ALPHA, GL_UNSIGNED_BYTE, mask.scanLine(i)); + } else { +#endif + glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_ALPHA, GL_UNSIGNED_BYTE, mask.bits()); +// } + } +} + +int QOpenGLTextureGlyphCache::glyphPadding() const +{ + return 1; +} + +int QOpenGLTextureGlyphCache::maxTextureWidth() const +{ + QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); + if (ctx == 0) + return QImageTextureGlyphCache::maxTextureWidth(); + else + return ctx->d_func()->maxTextureSize(); +} + +int QOpenGLTextureGlyphCache::maxTextureHeight() const +{ + QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); + if (ctx == 0) + return QImageTextureGlyphCache::maxTextureHeight(); + + if (ctx->d_func()->workaround_brokenTexSubImage) + return qMin(1024, ctx->d_func()->maxTextureSize()); + else + return ctx->d_func()->maxTextureSize(); +} + +void QOpenGLTextureGlyphCache::clear() +{ + m_textureResource->free(); + m_textureResource = 0; + + m_w = 0; + m_h = 0; + m_cx = 0; + m_cy = 0; + m_currentRowHeight = 0; + coords.clear(); +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopengltextureglyphcache_p.h b/src/gui/opengl/qopengltextureglyphcache_p.h new file mode 100644 index 0000000000..97f9ac3c64 --- /dev/null +++ b/src/gui/opengl/qopengltextureglyphcache_p.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENTEXTUREGLYPHCACHE_P_H +#define QOPENTEXTUREGLYPHCACHE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qtextureglyphcache_p.h> +#include <private/qopenglcontext_p.h> +#include <qopenglshaderprogram.h> +#include <qopenglfunctions.h> + +// #define QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + +QT_BEGIN_NAMESPACE + +class QOpenGL2PaintEngineExPrivate; + +class QOpenGLGlyphTexture : public QOpenGLSharedResource +{ +public: + explicit QOpenGLGlyphTexture(QOpenGLContext *ctx) + : QOpenGLSharedResource(ctx->shareGroup()) + , m_width(0) + , m_height(0) + { + if (ctx && !ctx->d_func()->workaround_brokenFBOReadBack) + QOpenGLFunctions(ctx).glGenFramebuffers(1, &m_fbo); + +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug(" -> QOpenGLGlyphTexture() %p for context %p.", this, ctx); +#endif + } + + void freeResource(QOpenGLContext *context) + { + QOpenGLContext *ctx = context; +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug("~QOpenGLGlyphTexture() %p for context %p.", this, ctx); +#endif + if (!ctx->d_func()->workaround_brokenFBOReadBack) + QOpenGLFunctions(ctx).glDeleteFramebuffers(1, &m_fbo); + if (m_width || m_height) + glDeleteTextures(1, &m_texture); + } + + void invalidateResource() + { + m_texture = 0; + m_fbo = 0; + m_width = 0; + m_height = 0; + } + + GLuint m_texture; + GLuint m_fbo; + int m_width; + int m_height; +}; + +class Q_GUI_EXPORT QOpenGLTextureGlyphCache : public QImageTextureGlyphCache +{ +public: + QOpenGLTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix); + ~QOpenGLTextureGlyphCache(); + + virtual void createTextureData(int width, int height); + virtual void resizeTextureData(int width, int height); + virtual void fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition); + virtual int glyphPadding() const; + virtual int maxTextureWidth() const; + virtual int maxTextureHeight() const; + + inline GLuint texture() const { + QOpenGLTextureGlyphCache *that = const_cast<QOpenGLTextureGlyphCache *>(this); + QOpenGLGlyphTexture *glyphTexture = that->m_textureResource; + return glyphTexture ? glyphTexture->m_texture : 0; + } + + inline int width() const { + QOpenGLTextureGlyphCache *that = const_cast<QOpenGLTextureGlyphCache *>(this); + QOpenGLGlyphTexture *glyphTexture = that->m_textureResource; + return glyphTexture ? glyphTexture->m_width : 0; + } + inline int height() const { + QOpenGLTextureGlyphCache *that = const_cast<QOpenGLTextureGlyphCache *>(this); + QOpenGLGlyphTexture *glyphTexture = that->m_textureResource; + return glyphTexture ? glyphTexture->m_height : 0; + } + + inline void setPaintEnginePrivate(QOpenGL2PaintEngineExPrivate *p) { pex = p; } + + inline const QOpenGLContextGroup *contextGroup() const { return m_textureResource ? m_textureResource->group() : 0; } + + inline int serialNumber() const { return m_serialNumber; } + + enum FilterMode { + Nearest, + Linear + }; + FilterMode filterMode() const { return m_filterMode; } + void setFilterMode(FilterMode m) { m_filterMode = m; } + + void clear(); + +private: + QOpenGLGlyphTexture *m_textureResource; + + QOpenGL2PaintEngineExPrivate *pex; + QOpenGLShaderProgram *m_blitProgram; + FilterMode m_filterMode; + + GLfloat m_vertexCoordinateArray[8]; + GLfloat m_textureCoordinateArray[8]; + + int m_serialNumber; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/gui/opengl/qopengltriangulatingstroker.cpp b/src/gui/opengl/qopengltriangulatingstroker.cpp new file mode 100644 index 0000000000..3dc3452b60 --- /dev/null +++ b/src/gui/opengl/qopengltriangulatingstroker.cpp @@ -0,0 +1,588 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qopengltriangulatingstroker_p.h" +#include <qmath.h> + +QT_BEGIN_NAMESPACE + +#define CURVE_FLATNESS Q_PI / 8 + + + + +void QTriangulatingStroker::endCapOrJoinClosed(const qreal *start, const qreal *cur, + bool implicitClose, bool endsAtStart) +{ + if (endsAtStart) { + join(start + 2); + } else if (implicitClose) { + join(start); + lineTo(start); + join(start+2); + } else { + endCap(cur); + } + int count = m_vertices.size(); + + // Copy the (x, y) values because QDataBuffer::add(const float& t) + // may resize the buffer, which will leave t pointing at the + // previous buffer's memory region if we don't copy first. + float x = m_vertices.at(count-2); + float y = m_vertices.at(count-1); + m_vertices.add(x); + m_vertices.add(y); +} + + +void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen, const QRectF &) +{ + const qreal *pts = path.points(); + const QPainterPath::ElementType *types = path.elements(); + int count = path.elementCount(); + if (count < 2) + return; + + float realWidth = qpen_widthf(pen); + if (realWidth == 0) + realWidth = 1; + + m_width = realWidth / 2; + + bool cosmetic = pen.isCosmetic(); + if (cosmetic) { + m_width = m_width * m_inv_scale; + } + + m_join_style = qpen_joinStyle(pen); + m_cap_style = qpen_capStyle(pen); + m_vertices.reset(); + m_miter_limit = pen.miterLimit() * qpen_widthf(pen); + + // The curvyness is based on the notion that I originally wanted + // roughly one line segment pr 4 pixels. This may seem little, but + // because we sample at constantly incrementing B(t) E [0<t<1], we + // will get longer segments where the curvature is small and smaller + // segments when the curvature is high. + // + // To get a rough idea of the length of each curve, I pretend that + // the curve is a 90 degree arc, whose radius is + // qMax(curveBounds.width, curveBounds.height). Based on this + // logic we can estimate the length of the outline edges based on + // the radius + a pen width and adjusting for scale factors + // depending on if the pen is cosmetic or not. + // + // The curvyness value of PI/14 was based on, + // arcLength = 2*PI*r/4 = PI*r/2 and splitting length into somewhere + // between 3 and 8 where 5 seemed to be give pretty good results + // hence: Q_PI/14. Lower divisors will give more detail at the + // direct cost of performance. + + // simplfy pens that are thin in device size (2px wide or less) + if (realWidth < 2.5 && (cosmetic || m_inv_scale == 1)) { + if (m_cap_style == Qt::RoundCap) + m_cap_style = Qt::SquareCap; + if (m_join_style == Qt::RoundJoin) + m_join_style = Qt::MiterJoin; + m_curvyness_add = 0.5; + m_curvyness_mul = CURVE_FLATNESS / m_inv_scale; + m_roundness = 1; + } else if (cosmetic) { + m_curvyness_add = realWidth / 2; + m_curvyness_mul = CURVE_FLATNESS; + m_roundness = qMax<int>(4, realWidth * CURVE_FLATNESS); + } else { + m_curvyness_add = m_width; + m_curvyness_mul = CURVE_FLATNESS / m_inv_scale; + m_roundness = qMax<int>(4, realWidth * m_curvyness_mul); + } + + // Over this level of segmentation, there doesn't seem to be any + // benefit, even for huge penWidth + if (m_roundness > 24) + m_roundness = 24; + + m_sin_theta = qFastSin(Q_PI / m_roundness); + m_cos_theta = qFastCos(Q_PI / m_roundness); + + const qreal *endPts = pts + (count<<1); + const qreal *startPts = 0; + + Qt::PenCapStyle cap = m_cap_style; + + if (!types) { + // skip duplicate points + while((pts + 2) < endPts && pts[0] == pts[2] && pts[1] == pts[3]) + pts += 2; + if ((pts + 2) == endPts) + return; + + startPts = pts; + + bool endsAtStart = startPts[0] == *(endPts-2) && startPts[1] == *(endPts-1); + + if (endsAtStart || path.hasImplicitClose()) + m_cap_style = Qt::FlatCap; + moveTo(pts); + m_cap_style = cap; + pts += 2; + lineTo(pts); + pts += 2; + while (pts < endPts) { + if (m_cx != pts[0] || m_cy != pts[1]) { + join(pts); + lineTo(pts); + } + pts += 2; + } + + endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); + + } else { + bool endsAtStart = false; + while (pts < endPts) { + switch (*types) { + case QPainterPath::MoveToElement: { + if (pts != path.points()) + endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); + + startPts = pts; + int end = (endPts - pts) / 2; + int i = 2; // Start looking to ahead since we never have two moveto's in a row + while (i<end && types[i] != QPainterPath::MoveToElement) { + ++i; + } + endsAtStart = startPts[0] == pts[i*2 - 2] && startPts[1] == pts[i*2 - 1]; + if (endsAtStart || path.hasImplicitClose()) + m_cap_style = Qt::FlatCap; + + moveTo(pts); + m_cap_style = cap; + pts+=2; + ++types; + break; } + case QPainterPath::LineToElement: + if (*(types - 1) != QPainterPath::MoveToElement) + join(pts); + lineTo(pts); + pts+=2; + ++types; + break; + case QPainterPath::CurveToElement: + if (*(types - 1) != QPainterPath::MoveToElement) + join(pts); + cubicTo(pts); + pts+=6; + types+=3; + break; + default: + Q_ASSERT(false); + break; + } + } + + endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); + } +} + +void QTriangulatingStroker::moveTo(const qreal *pts) +{ + m_cx = pts[0]; + m_cy = pts[1]; + + float x2 = pts[2]; + float y2 = pts[3]; + normalVector(m_cx, m_cy, x2, y2, &m_nvx, &m_nvy); + + + // To acheive jumps we insert zero-area tringles. This is done by + // adding two identical points in both the end of previous strip + // and beginning of next strip + bool invisibleJump = m_vertices.size(); + + switch (m_cap_style) { + case Qt::FlatCap: + if (invisibleJump) { + m_vertices.add(m_cx + m_nvx); + m_vertices.add(m_cy + m_nvy); + } + break; + case Qt::SquareCap: { + float sx = m_cx - m_nvy; + float sy = m_cy + m_nvx; + if (invisibleJump) { + m_vertices.add(sx + m_nvx); + m_vertices.add(sy + m_nvy); + } + emitLineSegment(sx, sy, m_nvx, m_nvy); + break; } + case Qt::RoundCap: { + QVarLengthArray<float> points; + arcPoints(m_cx, m_cy, m_cx + m_nvx, m_cy + m_nvy, m_cx - m_nvx, m_cy - m_nvy, points); + m_vertices.resize(m_vertices.size() + points.size() + 2 * int(invisibleJump)); + int count = m_vertices.size(); + int front = 0; + int end = points.size() / 2; + while (front != end) { + m_vertices.at(--count) = points[2 * end - 1]; + m_vertices.at(--count) = points[2 * end - 2]; + --end; + if (front == end) + break; + m_vertices.at(--count) = points[2 * front + 1]; + m_vertices.at(--count) = points[2 * front + 0]; + ++front; + } + + if (invisibleJump) { + m_vertices.at(count - 1) = m_vertices.at(count + 1); + m_vertices.at(count - 2) = m_vertices.at(count + 0); + } + break; } + default: break; // ssssh gcc... + } + emitLineSegment(m_cx, m_cy, m_nvx, m_nvy); +} + +void QTriangulatingStroker::cubicTo(const qreal *pts) +{ + const QPointF *p = (const QPointF *) pts; + QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]); + + QRectF bounds = bezier.bounds(); + float rad = qMax(bounds.width(), bounds.height()); + int threshold = qMin<float>(64, (rad + m_curvyness_add) * m_curvyness_mul); + if (threshold < 4) + threshold = 4; + qreal threshold_minus_1 = threshold - 1; + float vx, vy; + + float cx = m_cx, cy = m_cy; + float x, y; + + for (int i=1; i<threshold; ++i) { + qreal t = qreal(i) / threshold_minus_1; + QPointF p = bezier.pointAt(t); + x = p.x(); + y = p.y(); + + normalVector(cx, cy, x, y, &vx, &vy); + + emitLineSegment(x, y, vx, vy); + + cx = x; + cy = y; + } + + m_cx = cx; + m_cy = cy; + + m_nvx = vx; + m_nvy = vy; +} + +void QTriangulatingStroker::join(const qreal *pts) +{ + // Creates a join to the next segment (m_cx, m_cy) -> (pts[0], pts[1]) + normalVector(m_cx, m_cy, pts[0], pts[1], &m_nvx, &m_nvy); + + switch (m_join_style) { + case Qt::BevelJoin: + break; + case Qt::SvgMiterJoin: + case Qt::MiterJoin: { + // Find out on which side the join should be. + int count = m_vertices.size(); + float prevNvx = m_vertices.at(count - 2) - m_cx; + float prevNvy = m_vertices.at(count - 1) - m_cy; + float xprod = prevNvx * m_nvy - prevNvy * m_nvx; + float px, py, qx, qy; + + // If the segments are parallel, use bevel join. + if (qFuzzyIsNull(xprod)) + break; + + // Find the corners of the previous and next segment to join. + if (xprod < 0) { + px = m_vertices.at(count - 2); + py = m_vertices.at(count - 1); + qx = m_cx - m_nvx; + qy = m_cy - m_nvy; + } else { + px = m_vertices.at(count - 4); + py = m_vertices.at(count - 3); + qx = m_cx + m_nvx; + qy = m_cy + m_nvy; + } + + // Find intersection point. + float pu = px * prevNvx + py * prevNvy; + float qv = qx * m_nvx + qy * m_nvy; + float ix = (m_nvy * pu - prevNvy * qv) / xprod; + float iy = (prevNvx * qv - m_nvx * pu) / xprod; + + // Check that the distance to the intersection point is less than the miter limit. + if ((ix - px) * (ix - px) + (iy - py) * (iy - py) <= m_miter_limit * m_miter_limit) { + m_vertices.add(ix); + m_vertices.add(iy); + m_vertices.add(ix); + m_vertices.add(iy); + } + // else + // Do a plain bevel join if the miter limit is exceeded or if + // the lines are parallel. This is not what the raster + // engine's stroker does, but it is both faster and similar to + // what some other graphics API's do. + + break; } + case Qt::RoundJoin: { + QVarLengthArray<float> points; + int count = m_vertices.size(); + float prevNvx = m_vertices.at(count - 2) - m_cx; + float prevNvy = m_vertices.at(count - 1) - m_cy; + if (m_nvx * prevNvy - m_nvy * prevNvx < 0) { + arcPoints(0, 0, m_nvx, m_nvy, -prevNvx, -prevNvy, points); + for (int i = points.size() / 2; i > 0; --i) + emitLineSegment(m_cx, m_cy, points[2 * i - 2], points[2 * i - 1]); + } else { + arcPoints(0, 0, -prevNvx, -prevNvy, m_nvx, m_nvy, points); + for (int i = 0; i < points.size() / 2; ++i) + emitLineSegment(m_cx, m_cy, points[2 * i + 0], points[2 * i + 1]); + } + break; } + default: break; // gcc warn-- + } + + emitLineSegment(m_cx, m_cy, m_nvx, m_nvy); +} + +void QTriangulatingStroker::endCap(const qreal *) +{ + switch (m_cap_style) { + case Qt::FlatCap: + break; + case Qt::SquareCap: + emitLineSegment(m_cx + m_nvy, m_cy - m_nvx, m_nvx, m_nvy); + break; + case Qt::RoundCap: { + QVarLengthArray<float> points; + int count = m_vertices.size(); + arcPoints(m_cx, m_cy, m_vertices.at(count - 2), m_vertices.at(count - 1), m_vertices.at(count - 4), m_vertices.at(count - 3), points); + int front = 0; + int end = points.size() / 2; + while (front != end) { + m_vertices.add(points[2 * end - 2]); + m_vertices.add(points[2 * end - 1]); + --end; + if (front == end) + break; + m_vertices.add(points[2 * front + 0]); + m_vertices.add(points[2 * front + 1]); + ++front; + } + break; } + default: break; // to shut gcc up... + } +} + +void QTriangulatingStroker::arcPoints(float cx, float cy, float fromX, float fromY, float toX, float toY, QVarLengthArray<float> &points) +{ + float dx1 = fromX - cx; + float dy1 = fromY - cy; + float dx2 = toX - cx; + float dy2 = toY - cy; + + // while more than 180 degrees left: + while (dx1 * dy2 - dx2 * dy1 < 0) { + float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta; + float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta; + dx1 = tmpx; + dy1 = tmpy; + points.append(cx + dx1); + points.append(cy + dy1); + } + + // while more than 90 degrees left: + while (dx1 * dx2 + dy1 * dy2 < 0) { + float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta; + float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta; + dx1 = tmpx; + dy1 = tmpy; + points.append(cx + dx1); + points.append(cy + dy1); + } + + // while more than 0 degrees left: + while (dx1 * dy2 - dx2 * dy1 > 0) { + float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta; + float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta; + dx1 = tmpx; + dy1 = tmpy; + points.append(cx + dx1); + points.append(cy + dy1); + } + + // remove last point which was rotated beyond [toX, toY]. + if (!points.isEmpty()) + points.resize(points.size() - 2); +} + +static void qdashprocessor_moveTo(qreal x, qreal y, void *data) +{ + ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y); +} + +static void qdashprocessor_lineTo(qreal x, qreal y, void *data) +{ + ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::LineToElement, x, y); +} + +static void qdashprocessor_cubicTo(qreal, qreal, qreal, qreal, qreal, qreal, void *) +{ + Q_ASSERT(0); // The dasher should not produce curves... +} + +QDashedStrokeProcessor::QDashedStrokeProcessor() + : m_points(0), m_types(0), + m_dash_stroker(0), m_inv_scale(1) +{ + m_dash_stroker.setMoveToHook(qdashprocessor_moveTo); + m_dash_stroker.setLineToHook(qdashprocessor_lineTo); + m_dash_stroker.setCubicToHook(qdashprocessor_cubicTo); +} + +void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen, const QRectF &clip) +{ + + const qreal *pts = path.points(); + const QPainterPath::ElementType *types = path.elements(); + int count = path.elementCount(); + + bool cosmetic = pen.isCosmetic(); + + m_points.reset(); + m_types.reset(); + m_points.reserve(path.elementCount()); + m_types.reserve(path.elementCount()); + + qreal width = qpen_widthf(pen); + if (width == 0) + width = 1; + + m_dash_stroker.setDashPattern(pen.dashPattern()); + m_dash_stroker.setStrokeWidth(cosmetic ? width * m_inv_scale : width); + m_dash_stroker.setDashOffset(pen.dashOffset()); + m_dash_stroker.setMiterLimit(pen.miterLimit()); + m_dash_stroker.setClipRect(clip); + + float curvynessAdd, curvynessMul, roundness = 0; + + // simplfy pens that are thin in device size (2px wide or less) + if (width < 2.5 && (cosmetic || m_inv_scale == 1)) { + curvynessAdd = 0.5; + curvynessMul = CURVE_FLATNESS / m_inv_scale; + roundness = 1; + } else if (cosmetic) { + curvynessAdd= width / 2; + curvynessMul= CURVE_FLATNESS; + roundness = qMax<int>(4, width * CURVE_FLATNESS); + } else { + curvynessAdd = width * m_inv_scale; + curvynessMul = CURVE_FLATNESS / m_inv_scale; + roundness = qMax<int>(4, width * curvynessMul); + } + + if (count < 2) + return; + + const qreal *endPts = pts + (count<<1); + + m_dash_stroker.begin(this); + + if (!types) { + m_dash_stroker.moveTo(pts[0], pts[1]); + pts += 2; + while (pts < endPts) { + m_dash_stroker.lineTo(pts[0], pts[1]); + pts += 2; + } + } else { + while (pts < endPts) { + switch (*types) { + case QPainterPath::MoveToElement: + m_dash_stroker.moveTo(pts[0], pts[1]); + pts += 2; + ++types; + break; + case QPainterPath::LineToElement: + m_dash_stroker.lineTo(pts[0], pts[1]); + pts += 2; + ++types; + break; + case QPainterPath::CurveToElement: { + QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1), + *(((const QPointF *) pts)), + *(((const QPointF *) pts) + 1), + *(((const QPointF *) pts) + 2)); + QRectF bounds = b.bounds(); + float rad = qMax(bounds.width(), bounds.height()); + int threshold = qMin<float>(64, (rad + curvynessAdd) * curvynessMul); + if (threshold < 4) + threshold = 4; + + qreal threshold_minus_1 = threshold - 1; + for (int i=0; i<threshold; ++i) { + QPointF pt = b.pointAt(i / threshold_minus_1); + m_dash_stroker.lineTo(pt.x(), pt.y()); + } + pts += 6; + types += 3; + break; } + default: break; + } + } + } + + m_dash_stroker.end(); +} + +QT_END_NAMESPACE + diff --git a/src/gui/opengl/qopengltriangulatingstroker_p.h b/src/gui/opengl/qopengltriangulatingstroker_p.h new file mode 100644 index 0000000000..abb10957c0 --- /dev/null +++ b/src/gui/opengl/qopengltriangulatingstroker_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** 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 QtGui 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 QOPENGLTRIANGULATINGSTROKER_P_H +#define QOPENGLTRIANGULATINGSTROKER_P_H + +#include <private/qdatabuffer_p.h> +#include <qvarlengtharray.h> +#include <private/qvectorpath_p.h> +#include <private/qbezier_p.h> +#include <private/qnumeric_p.h> +#include <private/qmath_p.h> + +QT_BEGIN_NAMESPACE + +class QTriangulatingStroker +{ +public: + QTriangulatingStroker() : m_vertices(0) {} + void process(const QVectorPath &path, const QPen &pen, const QRectF &clip); + + inline int vertexCount() const { return m_vertices.size(); } + inline const float *vertices() const { return m_vertices.data(); } + + inline void setInvScale(qreal invScale) { m_inv_scale = invScale; } + +private: + inline void emitLineSegment(float x, float y, float nx, float ny); + void moveTo(const qreal *pts); + inline void lineTo(const qreal *pts); + void cubicTo(const qreal *pts); + void join(const qreal *pts); + inline void normalVector(float x1, float y1, float x2, float y2, float *nx, float *ny); + void endCap(const qreal *pts); + void arcPoints(float cx, float cy, float fromX, float fromY, float toX, float toY, QVarLengthArray<float> &points); + void endCapOrJoinClosed(const qreal *start, const qreal *cur, bool implicitClose, bool endsAtStart); + + + QDataBuffer<float> m_vertices; + + float m_cx, m_cy; // current points + float m_nvx, m_nvy; // normal vector... + float m_width; + qreal m_miter_limit; + + int m_roundness; // Number of line segments in a round join + qreal m_sin_theta; // sin(m_roundness / 360); + qreal m_cos_theta; // cos(m_roundness / 360); + qreal m_inv_scale; + float m_curvyness_mul; + float m_curvyness_add; + + Qt::PenJoinStyle m_join_style; + Qt::PenCapStyle m_cap_style; +}; + +class QDashedStrokeProcessor +{ +public: + QDashedStrokeProcessor(); + + void process(const QVectorPath &path, const QPen &pen, const QRectF &clip); + + inline void addElement(QPainterPath::ElementType type, qreal x, qreal y) { + m_points.add(x); + m_points.add(y); + m_types.add(type); + } + + inline int elementCount() const { return m_types.size(); } + inline qreal *points() const { return m_points.data(); } + inline QPainterPath::ElementType *elementTypes() const { return m_types.data(); } + + inline void setInvScale(qreal invScale) { m_inv_scale = invScale; } + +private: + QDataBuffer<qreal> m_points; + QDataBuffer<QPainterPath::ElementType> m_types; + QDashStroker m_dash_stroker; + qreal m_inv_scale; +}; + +inline void QTriangulatingStroker::normalVector(float x1, float y1, float x2, float y2, + float *nx, float *ny) +{ + float dx = x2 - x1; + float dy = y2 - y1; + + float pw; + + if (dx == 0) + pw = m_width / qAbs(dy); + else if (dy == 0) + pw = m_width / qAbs(dx); + else + pw = m_width / sqrt(dx*dx + dy*dy); + + *nx = -dy * pw; + *ny = dx * pw; +} + +inline void QTriangulatingStroker::emitLineSegment(float x, float y, float vx, float vy) +{ + m_vertices.add(x + vx); + m_vertices.add(y + vy); + m_vertices.add(x - vx); + m_vertices.add(y - vy); +} + +void QTriangulatingStroker::lineTo(const qreal *pts) +{ + emitLineSegment(pts[0], pts[1], m_nvx, m_nvy); + m_cx = pts[0]; + m_cy = pts[1]; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/opengl/qrbtree_p.h b/src/gui/opengl/qrbtree_p.h new file mode 100644 index 0000000000..ac464a3fbe --- /dev/null +++ b/src/gui/opengl/qrbtree_p.h @@ -0,0 +1,573 @@ +/**************************************************************************** +** +** 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 QtGui 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 QRBTREE_P_H +#define QRBTREE_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 <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +template <class T> +struct QRBTree +{ + struct Node + { + inline Node() : parent(0), left(0), right(0), red(true) { } + inline ~Node() {if (left) delete left; if (right) delete right;} + T data; + Node *parent; + Node *left; + Node *right; + bool red; + }; + + inline QRBTree() : root(0), freeList(0) { } + inline ~QRBTree(); + + inline void clear(); + + void attachBefore(Node *parent, Node *child); + void attachAfter(Node *parent, Node *child); + + inline Node *front(Node *node) const; + inline Node *back(Node *node) const; + Node *next(Node *node) const; + Node *previous(Node *node) const; + + inline void deleteNode(Node *&node); + inline Node *newNode(); + + // Return 1 if 'left' comes after 'right', 0 if equal, and -1 otherwise. + // 'left' and 'right' cannot be null. + int order(Node *left, Node *right); + inline bool validate() const; + +private: + void rotateLeft(Node *node); + void rotateRight(Node *node); + void update(Node *node); + + inline void attachLeft(Node *parent, Node *child); + inline void attachRight(Node *parent, Node *child); + + int blackDepth(Node *top) const; + bool checkRedBlackProperty(Node *top) const; + + void swapNodes(Node *n1, Node *n2); + void detach(Node *node); + + // 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree. + void rebalance(Node *node); + +public: + Node *root; +private: + Node *freeList; +}; + +template <class T> +inline QRBTree<T>::~QRBTree() +{ + clear(); + while (freeList) { + // Avoid recursively calling the destructor, as this list may become large. + Node *next = freeList->right; + freeList->right = 0; + delete freeList; + freeList = next; + } +} + +template <class T> +inline void QRBTree<T>::clear() +{ + if (root) + delete root; + root = 0; +} + +template <class T> +void QRBTree<T>::rotateLeft(Node *node) +{ + // | | // + // N B // + // / \ / \ // + // A B ---> N D // + // / \ / \ // + // C D A C // + + Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root); + ref = node->right; + node->right->parent = node->parent; + + // : // + // N // + // / :| // + // A B // + // / \ // + // C D // + + node->right = ref->left; + if (ref->left) + ref->left->parent = node; + + // : | // + // N B // + // / \ : \ // + // A C D // + + ref->left = node; + node->parent = ref; + + // | // + // B // + // / \ // + // N D // + // / \ // + // A C // +} + +template <class T> +void QRBTree<T>::rotateRight(Node *node) +{ + // | | // + // N A // + // / \ / \ // + // A B ---> C N // + // / \ / \ // + // C D D B // + + Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root); + ref = node->left; + node->left->parent = node->parent; + + node->left = ref->right; + if (ref->right) + ref->right->parent = node; + + ref->right = node; + node->parent = ref; +} + +template <class T> +void QRBTree<T>::update(Node *node) // call this after inserting a node +{ + for (;;) { + Node *parent = node->parent; + + // if the node is the root, color it black + if (!parent) { + node->red = false; + return; + } + + // if the parent is black, the node can be left red + if (!parent->red) + return; + + // at this point, the parent is red and cannot be the root + Node *grandpa = parent->parent; + Q_ASSERT(grandpa); + + Node *uncle = (parent == grandpa->left ? grandpa->right : grandpa->left); + if (uncle && uncle->red) { + // grandpa's black, parent and uncle are red. + // let parent and uncle be black, grandpa red and recursively update grandpa. + Q_ASSERT(!grandpa->red); + parent->red = false; + uncle->red = false; + grandpa->red = true; + node = grandpa; + continue; + } + + // at this point, uncle is black + if (node == parent->right && parent == grandpa->left) + rotateLeft(node = parent); + else if (node == parent->left && parent == grandpa->right) + rotateRight(node = parent); + parent = node->parent; + + if (parent == grandpa->left) { + rotateRight(grandpa); + parent->red = false; + grandpa->red = true; + } else { + rotateLeft(grandpa); + parent->red = false; + grandpa->red = true; + } + return; + } +} + +template <class T> +inline void QRBTree<T>::attachLeft(Node *parent, Node *child) +{ + Q_ASSERT(!parent->left); + parent->left = child; + child->parent = parent; + update(child); +} + +template <class T> +inline void QRBTree<T>::attachRight(Node *parent, Node *child) +{ + Q_ASSERT(!parent->right); + parent->right = child; + child->parent = parent; + update(child); +} + +template <class T> +void QRBTree<T>::attachBefore(Node *parent, Node *child) +{ + if (!root) + update(root = child); + else if (!parent) + attachRight(back(root), child); + else if (parent->left) + attachRight(back(parent->left), child); + else + attachLeft(parent, child); +} + +template <class T> +void QRBTree<T>::attachAfter(Node *parent, Node *child) +{ + if (!root) + update(root = child); + else if (!parent) + attachLeft(front(root), child); + else if (parent->right) + attachLeft(front(parent->right), child); + else + attachRight(parent, child); +} + +template <class T> +void QRBTree<T>::swapNodes(Node *n1, Node *n2) +{ + // Since iterators must not be invalidated, it is not sufficient to only swap the data. + if (n1->parent == n2) { + n1->parent = n2->parent; + n2->parent = n1; + } else if (n2->parent == n1) { + n2->parent = n1->parent; + n1->parent = n2; + } else { + qSwap(n1->parent, n2->parent); + } + + qSwap(n1->left, n2->left); + qSwap(n1->right, n2->right); + qSwap(n1->red, n2->red); + + if (n1->parent) { + if (n1->parent->left == n2) + n1->parent->left = n1; + else + n1->parent->right = n1; + } else { + root = n1; + } + + if (n2->parent) { + if (n2->parent->left == n1) + n2->parent->left = n2; + else + n2->parent->right = n2; + } else { + root = n2; + } + + if (n1->left) + n1->left->parent = n1; + if (n1->right) + n1->right->parent = n1; + + if (n2->left) + n2->left->parent = n2; + if (n2->right) + n2->right->parent = n2; +} + +template <class T> +void QRBTree<T>::detach(Node *node) // call this before removing a node. +{ + if (node->right) + swapNodes(node, front(node->right)); + + Node *child = (node->left ? node->left : node->right); + + if (!node->red) { + if (child && child->red) + child->red = false; + else + rebalance(node); + } + + Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root); + ref = child; + if (child) + child->parent = node->parent; + node->left = node->right = node->parent = 0; +} + +// 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree. +template <class T> +void QRBTree<T>::rebalance(Node *node) +{ + Q_ASSERT(!node->red); + for (;;) { + if (!node->parent) + return; + + // at this point, node is not a parent, it is black, thus it must have a sibling. + Node *sibling = (node == node->parent->left ? node->parent->right : node->parent->left); + Q_ASSERT(sibling); + + if (sibling->red) { + sibling->red = false; + node->parent->red = true; + if (node == node->parent->left) + rotateLeft(node->parent); + else + rotateRight(node->parent); + sibling = (node == node->parent->left ? node->parent->right : node->parent->left); + Q_ASSERT(sibling); + } + + // at this point, the sibling is black. + Q_ASSERT(!sibling->red); + + if ((!sibling->left || !sibling->left->red) && (!sibling->right || !sibling->right->red)) { + bool parentWasRed = node->parent->red; + sibling->red = true; + node->parent->red = false; + if (parentWasRed) + return; + node = node->parent; + continue; + } + + // at this point, at least one of the sibling's children is red. + + if (node == node->parent->left) { + if (!sibling->right || !sibling->right->red) { + Q_ASSERT(sibling->left); + sibling->red = true; + sibling->left->red = false; + rotateRight(sibling); + + sibling = sibling->parent; + Q_ASSERT(sibling); + } + sibling->red = node->parent->red; + node->parent->red = false; + + Q_ASSERT(sibling->right->red); + sibling->right->red = false; + rotateLeft(node->parent); + } else { + if (!sibling->left || !sibling->left->red) { + Q_ASSERT(sibling->right); + sibling->red = true; + sibling->right->red = false; + rotateLeft(sibling); + + sibling = sibling->parent; + Q_ASSERT(sibling); + } + sibling->red = node->parent->red; + node->parent->red = false; + + Q_ASSERT(sibling->left->red); + sibling->left->red = false; + rotateRight(node->parent); + } + return; + } +} + +template <class T> +inline typename QRBTree<T>::Node *QRBTree<T>::front(Node *node) const +{ + while (node->left) + node = node->left; + return node; +} + +template <class T> +inline typename QRBTree<T>::Node *QRBTree<T>::back(Node *node) const +{ + while (node->right) + node = node->right; + return node; +} + +template <class T> +typename QRBTree<T>::Node *QRBTree<T>::next(Node *node) const +{ + if (node->right) + return front(node->right); + while (node->parent && node == node->parent->right) + node = node->parent; + return node->parent; +} + +template <class T> +typename QRBTree<T>::Node *QRBTree<T>::previous(Node *node) const +{ + if (node->left) + return back(node->left); + while (node->parent && node == node->parent->left) + node = node->parent; + return node->parent; +} + +template <class T> +int QRBTree<T>::blackDepth(Node *top) const +{ + if (!top) + return 0; + int leftDepth = blackDepth(top->left); + int rightDepth = blackDepth(top->right); + if (leftDepth != rightDepth) + return -1; + if (!top->red) + ++leftDepth; + return leftDepth; +} + +template <class T> +bool QRBTree<T>::checkRedBlackProperty(Node *top) const +{ + if (!top) + return true; + if (top->left && !checkRedBlackProperty(top->left)) + return false; + if (top->right && !checkRedBlackProperty(top->right)) + return false; + return !(top->red && ((top->left && top->left->red) || (top->right && top->right->red))); +} + +template <class T> +inline bool QRBTree<T>::validate() const +{ + return checkRedBlackProperty(root) && blackDepth(root) != -1; +} + +template <class T> +inline void QRBTree<T>::deleteNode(Node *&node) +{ + Q_ASSERT(node); + detach(node); + node->right = freeList; + freeList = node; + node = 0; +} + +template <class T> +inline typename QRBTree<T>::Node *QRBTree<T>::newNode() +{ + if (freeList) { + Node *node = freeList; + freeList = freeList->right; + node->parent = node->left = node->right = 0; + node->red = true; + return node; + } + return new Node; +} + +// Return 1 if 'left' comes after 'right', 0 if equal, and -1 otherwise. +// 'left' and 'right' cannot be null. +template <class T> +int QRBTree<T>::order(Node *left, Node *right) +{ + Q_ASSERT(left && right); + if (left == right) + return 0; + + QVector<Node *> leftAncestors; + QVector<Node *> rightAncestors; + while (left) { + leftAncestors.push_back(left); + left = left->parent; + } + while (right) { + rightAncestors.push_back(right); + right = right->parent; + } + Q_ASSERT(leftAncestors.back() == root && rightAncestors.back() == root); + + while (!leftAncestors.empty() && !rightAncestors.empty() && leftAncestors.back() == rightAncestors.back()) { + leftAncestors.pop_back(); + rightAncestors.pop_back(); + } + + if (!leftAncestors.empty()) + return (leftAncestors.back() == leftAncestors.back()->parent->left ? -1 : 1); + + if (!rightAncestors.empty()) + return (rightAncestors.back() == rightAncestors.back()->parent->right ? -1 : 1); + + // The code should never reach this point. + Q_ASSERT(!leftAncestors.empty() || !rightAncestors.empty()); + return 0; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/opengl/qtriangulator.cpp b/src/gui/opengl/qtriangulator.cpp new file mode 100644 index 0000000000..d66b2ac9a5 --- /dev/null +++ b/src/gui/opengl/qtriangulator.cpp @@ -0,0 +1,2622 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qtriangulator_p.h" + +#include <QtWidgets/qdialog.h> +#include <QtGui/qevent.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpainterpath.h> +#include <QtGui/private/qbezier_p.h> +#include <QtGui/private/qdatabuffer_p.h> +#include <QtCore/qbitarray.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qqueue.h> +#include <QtCore/qglobal.h> +#include <QtCore/qpoint.h> +#include <QtCore/qalgorithms.h> + +#include <private/qopenglcontext_p.h> +#include <private/qrbtree_p.h> + +#include <math.h> + +QT_BEGIN_NAMESPACE + +//#define Q_TRIANGULATOR_DEBUG + +#define Q_FIXED_POINT_SCALE 32 + +// Quick sort. +template <class T, class LessThan> +#ifdef Q_CC_RVCT // RVCT 2.2 doesn't see recursive _static_ template function +void sort(T *array, int count, LessThan lessThan) +#else +static void sort(T *array, int count, LessThan lessThan) +#endif +{ + // If the number of elements fall below some threshold, use insertion sort. + const int INSERTION_SORT_LIMIT = 7; // About 7 is fastest on my computer... + if (count <= INSERTION_SORT_LIMIT) { + for (int i = 1; i < count; ++i) { + T temp = array[i]; + int j = i; + while (j > 0 && lessThan(temp, array[j - 1])) { + array[j] = array[j - 1]; + --j; + } + array[j] = temp; + } + return; + } + + int high = count - 1; + int low = 0; + int mid = high / 2; + if (lessThan(array[mid], array[low])) + qSwap(array[mid], array[low]); + if (lessThan(array[high], array[mid])) + qSwap(array[high], array[mid]); + if (lessThan(array[mid], array[low])) + qSwap(array[mid], array[low]); + + --high; + ++low; + qSwap(array[mid], array[high]); + int pivot = high; + --high; + + while (low <= high) { + while (!lessThan(array[pivot], array[low])) { + ++low; + if (low > high) + goto sort_loop_end; + } + while (!lessThan(array[high], array[pivot])) { + --high; + if (low > high) + goto sort_loop_end; + } + qSwap(array[low], array[high]); + ++low; + --high; + } +sort_loop_end: + if (low != pivot) + qSwap(array[pivot], array[low]); + sort(array, low, lessThan); + sort(array + low + 1, count - low - 1, lessThan); +} + +// Quick sort. +template <class T> +#ifdef Q_CC_RVCT +void sort(T *array, int count) // RVCT 2.2 doesn't see recursive _static_ template function +#else +static void sort(T *array, int count) +#endif +{ + // If the number of elements fall below some threshold, use insertion sort. + const int INSERTION_SORT_LIMIT = 25; // About 25 is fastest on my computer... + if (count <= INSERTION_SORT_LIMIT) { + for (int i = 1; i < count; ++i) { + T temp = array[i]; + int j = i; + while (j > 0 && (temp < array[j - 1])) { + array[j] = array[j - 1]; + --j; + } + array[j] = temp; + } + return; + } + + int high = count - 1; + int low = 0; + int mid = high / 2; + if ((array[mid] < array[low])) + qSwap(array[mid], array[low]); + if ((array[high] < array[mid])) + qSwap(array[high], array[mid]); + if ((array[mid] < array[low])) + qSwap(array[mid], array[low]); + + --high; + ++low; + qSwap(array[mid], array[high]); + int pivot = high; + --high; + + while (low <= high) { + while (!(array[pivot] < array[low])) { + ++low; + if (low > high) + goto sort_loop_end; + } + while (!(array[high] < array[pivot])) { + --high; + if (low > high) + goto sort_loop_end; + } + qSwap(array[low], array[high]); + ++low; + --high; + } +sort_loop_end: + if (low != pivot) + qSwap(array[pivot], array[low]); + sort(array, low); + sort(array + low + 1, count - low - 1); +} + +template<typename T> +struct QVertexSet +{ + inline QVertexSet() { } + inline QVertexSet(const QVertexSet<T> &other) : vertices(other.vertices), indices(other.indices) { } + QVertexSet<T> &operator = (const QVertexSet<T> &other) {vertices = other.vertices; indices = other.indices; return *this;} + + // The vertices of a triangle are given by: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ... + QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...] + QVector<T> indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...] +}; + +//============================================================================// +// QFraction // +//============================================================================// + +// Fraction must be in the range [0, 1) +struct QFraction +{ + // Comparison operators must not be called on invalid fractions. + inline bool operator < (const QFraction &other) const; + inline bool operator == (const QFraction &other) const; + inline bool operator != (const QFraction &other) const {return !(*this == other);} + inline bool operator > (const QFraction &other) const {return other < *this;} + inline bool operator >= (const QFraction &other) const {return !(*this < other);} + inline bool operator <= (const QFraction &other) const {return !(*this > other);} + + inline bool isValid() const {return denominator != 0;} + + // numerator and denominator must not have common denominators. + quint64 numerator, denominator; +}; + +static inline quint64 gcd(quint64 x, quint64 y) +{ + while (y != 0) { + quint64 z = y; + y = x % y; + x = z; + } + return x; +} + +static inline int compare(quint64 a, quint64 b) +{ + return (a > b) - (a < b); +} + +// Compare a/b with c/d. +// Return negative if less, 0 if equal, positive if greater. +// a < b, c < d +static int qCompareFractions(quint64 a, quint64 b, quint64 c, quint64 d) +{ + const quint64 LIMIT = Q_UINT64_C(0x100000000); + for (;;) { + // If the products 'ad' and 'bc' fit into 64 bits, they can be directly compared. + if (b < LIMIT && d < LIMIT) + return compare(a * d, b * c); + + if (a == 0 || c == 0) + return compare(a, c); + + // a/b < c/d <=> d/c < b/a + quint64 b_div_a = b / a; + quint64 d_div_c = d / c; + if (b_div_a != d_div_c) + return compare(d_div_c, b_div_a); + + // floor(d/c) == floor(b/a) + // frac(d/c) < frac(b/a) ? + // frac(x/y) = (x%y)/y + d -= d_div_c * c; //d %= c; + b -= b_div_a * a; //b %= a; + qSwap(a, d); + qSwap(b, c); + } +} + +// Fraction must be in the range [0, 1) +// Assume input is valid. +static QFraction qFraction(quint64 n, quint64 d) { + QFraction result; + if (n == 0) { + result.numerator = 0; + result.denominator = 1; + } else { + quint64 g = gcd(n, d); + result.numerator = n / g; + result.denominator = d / g; + } + return result; +} + +inline bool QFraction::operator < (const QFraction &other) const +{ + return qCompareFractions(numerator, denominator, other.numerator, other.denominator) < 0; +} + +inline bool QFraction::operator == (const QFraction &other) const +{ + return numerator == other.numerator && denominator == other.denominator; +} + +//============================================================================// +// QPodPoint // +//============================================================================// + +struct QPodPoint +{ + inline bool operator < (const QPodPoint &other) const + { + if (y != other.y) + return y < other.y; + return x < other.x; + } + + inline bool operator > (const QPodPoint &other) const {return other < *this;} + inline bool operator <= (const QPodPoint &other) const {return !(*this > other);} + inline bool operator >= (const QPodPoint &other) const {return !(*this < other);} + inline bool operator == (const QPodPoint &other) const {return x == other.x && y == other.y;} + inline bool operator != (const QPodPoint &other) const {return x != other.x || y != other.y;} + + inline QPodPoint &operator += (const QPodPoint &other) {x += other.x; y += other.y; return *this;} + inline QPodPoint &operator -= (const QPodPoint &other) {x -= other.x; y -= other.y; return *this;} + inline QPodPoint operator + (const QPodPoint &other) const {QPodPoint result = {x + other.x, y + other.y}; return result;} + inline QPodPoint operator - (const QPodPoint &other) const {QPodPoint result = {x - other.x, y - other.y}; return result;} + + int x; + int y; +}; + +static inline qint64 qCross(const QPodPoint &u, const QPodPoint &v) +{ + return qint64(u.x) * qint64(v.y) - qint64(u.y) * qint64(v.x); +} + +static inline qint64 qDot(const QPodPoint &u, const QPodPoint &v) +{ + return qint64(u.x) * qint64(v.x) + qint64(u.y) * qint64(v.y); +} + +// 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). +static inline qint64 qPointDistanceFromLine(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2) +{ + return qCross(v2 - v1, p - v1); +} + +static inline bool qPointIsLeftOfLine(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2) +{ + return QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(p, v1, v2) < 0; +} + +// Return: +// -1 if u < v +// 0 if u == v +// 1 if u > v +static int comparePoints(const QPodPoint &u, const QPodPoint &v) +{ + if (u.y < v.y) + return -1; + if (u.y > v.y) + return 1; + if (u.x < v.x) + return -1; + if (u.x > v.x) + return 1; + return 0; +} + +//============================================================================// +// QIntersectionPoint // +//============================================================================// + +struct QIntersectionPoint +{ + inline bool isValid() const {return xOffset.isValid() && yOffset.isValid();} + QPodPoint round() const; + inline bool isAccurate() const {return xOffset.numerator == 0 && yOffset.numerator == 0;} + bool operator < (const QIntersectionPoint &other) const; + bool operator == (const QIntersectionPoint &other) const; + inline bool operator != (const QIntersectionPoint &other) const {return !(*this == other);} + inline bool operator > (const QIntersectionPoint &other) const {return other < *this;} + inline bool operator >= (const QIntersectionPoint &other) const {return !(*this < other);} + inline bool operator <= (const QIntersectionPoint &other) const {return !(*this > other);} + bool isOnLine(const QPodPoint &u, const QPodPoint &v) const; + + QPodPoint upperLeft; + QFraction xOffset; + QFraction yOffset; +}; + +static inline QIntersectionPoint qIntersectionPoint(const QPodPoint &point) +{ + // upperLeft = point, xOffset = 0/1, yOffset = 0/1. + QIntersectionPoint p = {{point.x, point.y}, {0, 1}, {0, 1}}; + return p; +} + +static inline QIntersectionPoint qIntersectionPoint(int x, int y) +{ + // upperLeft = (x, y), xOffset = 0/1, yOffset = 0/1. + QIntersectionPoint p = {{x, y}, {0, 1}, {0, 1}}; + return p; +} + +static QIntersectionPoint qIntersectionPoint(const QPodPoint &u1, const QPodPoint &u2, const QPodPoint &v1, const QPodPoint &v2) +{ + QIntersectionPoint result = {{0, 0}, {0, 0}, {0, 0}}; + + QPodPoint u = u2 - u1; + QPodPoint v = v2 - v1; + qint64 d1 = qCross(u, v1 - u1); + qint64 d2 = qCross(u, v2 - u1); + qint64 det = d2 - d1; + qint64 d3 = qCross(v, u1 - v1); + qint64 d4 = d3 - det; //qCross(v, u2 - v1); + + // Check that the math is correct. + Q_ASSERT(d4 == qCross(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 21 bits per vector component. + // TODO: Make code path for 31 bits per vector component. + if (v.x >= 0) { + result.upperLeft.x = v1.x + (-v.x * d1) / det; + result.xOffset = qFraction(quint64(-v.x * d1) % quint64(det), quint64(det)); + } else { + result.upperLeft.x = v2.x + (-v.x * d2) / det; + result.xOffset = qFraction(quint64(-v.x * d2) % quint64(det), quint64(det)); + } + + if (v.y >= 0) { + result.upperLeft.y = v1.y + (-v.y * d1) / det; + result.yOffset = qFraction(quint64(-v.y * d1) % quint64(det), quint64(det)); + } else { + result.upperLeft.y = v2.y + (-v.y * d2) / det; + result.yOffset = qFraction(quint64(-v.y * d2) % quint64(det), quint64(det)); + } + + Q_ASSERT(result.xOffset.isValid()); + Q_ASSERT(result.yOffset.isValid()); + return result; +} + +QPodPoint QIntersectionPoint::round() const +{ + QPodPoint result = upperLeft; + if (2 * xOffset.numerator >= xOffset.denominator) + ++result.x; + if (2 * yOffset.numerator >= yOffset.denominator) + ++result.y; + return result; +} + +bool QIntersectionPoint::operator < (const QIntersectionPoint &other) const +{ + if (upperLeft.y != other.upperLeft.y) + return upperLeft.y < other.upperLeft.y; + if (yOffset != other.yOffset) + return yOffset < other.yOffset; + if (upperLeft.x != other.upperLeft.x) + return upperLeft.x < other.upperLeft.x; + return xOffset < other.xOffset; +} + +bool QIntersectionPoint::operator == (const QIntersectionPoint &other) const +{ + return upperLeft == other.upperLeft && xOffset == other.xOffset && yOffset == other.yOffset; +} + +// Returns true if this point is on the infinite line passing through 'u' and 'v'. +bool QIntersectionPoint::isOnLine(const QPodPoint &u, const QPodPoint &v) const +{ + // TODO: Make code path for coordinates with more than 21 bits. + const QPodPoint p = upperLeft - u; + const QPodPoint q = v - u; + bool isHorizontal = p.y == 0 && yOffset.numerator == 0; + bool isVertical = p.x == 0 && xOffset.numerator == 0; + if (isHorizontal && isVertical) + return true; + if (isHorizontal) + return q.y == 0; + if (q.y == 0) + return false; + if (isVertical) + return q.x == 0; + if (q.x == 0) + return false; + + // At this point, 'p+offset' and 'q' cannot lie on the x or y axis. + + if (((q.x < 0) == (q.y < 0)) != ((p.x < 0) == (p.y < 0))) + return false; // 'p + offset' and 'q' pass through different quadrants. + + // Move all coordinates into the first quadrant. + quint64 nx, ny; + if (p.x < 0) + nx = quint64(-p.x) * xOffset.denominator - xOffset.numerator; + else + nx = quint64(p.x) * xOffset.denominator + xOffset.numerator; + if (p.y < 0) + ny = quint64(-p.y) * yOffset.denominator - yOffset.numerator; + else + ny = quint64(p.y) * yOffset.denominator + yOffset.numerator; + + return qFraction(quint64(qAbs(q.x)) * xOffset.denominator, quint64(qAbs(q.y)) * yOffset.denominator) == qFraction(nx, ny); +} + +//============================================================================// +// QMaxHeap // +//============================================================================// + +template <class T> +class QMaxHeap +{ +public: + QMaxHeap() : m_data(0) {} + inline int size() const {return m_data.size();} + inline bool empty() const {return m_data.isEmpty();} + inline bool isEmpty() const {return m_data.isEmpty();} + void push(const T &x); + T pop(); + inline const T &top() const {return m_data.first();} +private: + static inline int parent(int i) {return (i - 1) / 2;} + static inline int left(int i) {return 2 * i + 1;} + static inline int right(int i) {return 2 * i + 2;} + + QDataBuffer<T> m_data; +}; + +template <class T> +void QMaxHeap<T>::push(const T &x) +{ + int current = m_data.size(); + int parent = QMaxHeap::parent(current); + m_data.add(x); + while (current != 0 && m_data.at(parent) < x) { + m_data.at(current) = m_data.at(parent); + current = parent; + parent = QMaxHeap::parent(current); + } + m_data.at(current) = x; +} + +template <class T> +T QMaxHeap<T>::pop() +{ + T result = m_data.first(); + T back = m_data.last(); + m_data.pop_back(); + if (!m_data.isEmpty()) { + int current = 0; + for (;;) { + int left = QMaxHeap::left(current); + int right = QMaxHeap::right(current); + if (left >= m_data.size()) + break; + int greater = left; + if (right < m_data.size() && m_data.at(left) < m_data.at(right)) + greater = right; + if (m_data.at(greater) < back) + break; + m_data.at(current) = m_data.at(greater); + current = greater; + } + m_data.at(current) = back; + } + return result; +} + +//============================================================================// +// QInt64Hash // +//============================================================================// + +// Copied from qhash.cpp +static const uchar prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 +}; + +// Copied from qhash.cpp +static inline int primeForNumBits(int numBits) +{ + return (1 << numBits) + prime_deltas[numBits]; +} + +static inline int primeForCount(int count) +{ + int low = 0; + int high = 32; + for (int i = 0; i < 5; ++i) { + int mid = (high + low) / 2; + if (count >= 1 << mid) + low = mid; + else + high = mid; + } + return primeForNumBits(high); +} + +// Hash set of quint64s. Elements cannot be removed without clearing the +// entire set. A value of -1 is used to mark unused entries. +class QInt64Set +{ +public: + inline QInt64Set(int capacity = 64); + inline ~QInt64Set() {if (m_array) delete[] m_array;} + inline bool isValid() const {return m_array;} + void insert(quint64 key); + bool contains(quint64 key) const; + inline void clear(); +private: + bool rehash(int capacity); + + static const quint64 UNUSED; + + quint64 *m_array; + int m_capacity; + int m_count; +}; + +const quint64 QInt64Set::UNUSED = quint64(-1); + +inline QInt64Set::QInt64Set(int capacity) +{ + m_capacity = primeForCount(capacity); + m_array = new quint64[m_capacity]; + if (m_array) + clear(); + else + m_capacity = 0; +} + +bool QInt64Set::rehash(int capacity) +{ + quint64 *oldArray = m_array; + int oldCapacity = m_capacity; + + m_capacity = capacity; + m_array = new quint64[m_capacity]; + if (m_array) { + clear(); + if (oldArray) { + for (int i = 0; i < oldCapacity; ++i) { + if (oldArray[i] != UNUSED) + insert(oldArray[i]); + } + delete[] oldArray; + } + return true; + } else { + m_capacity = oldCapacity; + m_array = oldArray; + return false; + } +} + +void QInt64Set::insert(quint64 key) +{ + if (m_count > 3 * m_capacity / 4) + rehash(primeForCount(2 * m_capacity)); + Q_ASSERT_X(m_array, "QInt64Hash<T>::insert", "Hash set not allocated."); + int index = int(key % m_capacity); + for (int i = 0; i < m_capacity; ++i) { + index += i; + if (index >= m_capacity) + index -= m_capacity; + if (m_array[index] == key) + return; + if (m_array[index] == UNUSED) { + ++m_count; + m_array[index] = key; + return; + } + } + Q_ASSERT_X(0, "QInt64Hash<T>::insert", "Hash set full."); +} + +bool QInt64Set::contains(quint64 key) const +{ + Q_ASSERT_X(m_array, "QInt64Hash<T>::contains", "Hash set not allocated."); + int index = int(key % m_capacity); + for (int i = 0; i < m_capacity; ++i) { + index += i; + if (index >= m_capacity) + index -= m_capacity; + if (m_array[index] == key) + return true; + if (m_array[index] == UNUSED) + return false; + } + return false; +} + +inline void QInt64Set::clear() +{ + Q_ASSERT_X(m_array, "QInt64Hash<T>::clear", "Hash set not allocated."); + for (int i = 0; i < m_capacity; ++i) + m_array[i] = UNUSED; + m_count = 0; +} + +//============================================================================// +// QRingBuffer // +//============================================================================// + +// T must be POD. +template <class T> +class QRingBuffer +{ +public: + inline QRingBuffer() : m_array(0), m_head(0), m_size(0), m_capacity(0) { } + inline ~QRingBuffer() {if (m_array) delete[] m_array;} + bool reallocate(int capacity); + inline const T &head() const {Q_ASSERT(m_size > 0); return m_array[m_head];} + inline const T &dequeue(); + inline void enqueue(const T &x); + inline bool isEmpty() const {return m_size == 0;} +private: + T *m_array; + int m_head; + int m_size; + int m_capacity; +}; + +template <class T> +bool QRingBuffer<T>::reallocate(int capacity) +{ + T *oldArray = m_array; + m_array = new T[capacity]; + if (m_array) { + if (oldArray) { + if (m_head + m_size > m_capacity) { + memcpy(m_array, oldArray + m_head, (m_capacity - m_head) * sizeof(T)); + memcpy(m_array + (m_capacity - m_head), oldArray, (m_head + m_size - m_capacity) * sizeof(T)); + } else { + memcpy(m_array, oldArray + m_head, m_size * sizeof(T)); + } + delete[] oldArray; + } + m_capacity = capacity; + m_head = 0; + return true; + } else { + m_array = oldArray; + return false; + } +} + +template <class T> +inline const T &QRingBuffer<T>::dequeue() +{ + Q_ASSERT(m_size > 0); + Q_ASSERT(m_array); + Q_ASSERT(m_capacity >= m_size); + int index = m_head; + if (++m_head >= m_capacity) + m_head -= m_capacity; + --m_size; + return m_array[index]; +} + +template <class T> +inline void QRingBuffer<T>::enqueue(const T &x) +{ + if (m_size == m_capacity) + reallocate(qMax(2 * m_capacity, 64)); + int index = m_head + m_size; + if (index >= m_capacity) + index -= m_capacity; + m_array[index] = x; + ++m_size; +} + +//============================================================================// +// QTriangulator // +//============================================================================// +template<typename T> +class QTriangulator +{ +public: + typedef QVarLengthArray<int, 6> ShortArray; + + //================================// + // QTriangulator::ComplexToSimple // + //================================// + friend class ComplexToSimple; + class ComplexToSimple + { + public: + inline ComplexToSimple(QTriangulator<T> *parent) : m_parent(parent), + m_edges(0), m_events(0), m_splits(0) { } + void decompose(); + private: + struct Edge + { + inline int &upper() {return pointingUp ? to : from;} + inline int &lower() {return pointingUp ? from : to;} + inline int upper() const {return pointingUp ? to : from;} + inline int lower() const {return pointingUp ? from : to;} + + QRBTree<int>::Node *node; + int from, to; // vertex + int next, previous; // edge + int winding; + bool mayIntersect; + bool pointingUp, originallyPointingUp; + }; + + friend class CompareEdges; + class CompareEdges + { + public: + inline CompareEdges(ComplexToSimple *parent) : m_parent(parent) { } + bool operator () (int i, int j) const; + private: + ComplexToSimple *m_parent; + }; + + struct Intersection + { + bool operator < (const Intersection &other) const {return other.intersectionPoint < intersectionPoint;} + + QIntersectionPoint intersectionPoint; + int vertex; + int leftEdge; + int rightEdge; + }; + + struct Split + { + int vertex; + int edge; + bool accurate; + }; + + struct Event + { + enum Type {Upper, Lower}; + inline bool operator < (const Event &other) const; + + QPodPoint point; + Type type; + int edge; + }; + +#ifdef Q_TRIANGULATOR_DEBUG + friend class DebugDialog; + friend class QTriangulator; + class DebugDialog : public QDialog + { + public: + DebugDialog(ComplexToSimple *parent, int currentVertex); + protected: + void paintEvent(QPaintEvent *); + void wheelEvent(QWheelEvent *); + void mouseMoveEvent(QMouseEvent *); + void mousePressEvent(QMouseEvent *); + private: + ComplexToSimple *m_parent; + QRectF m_window; + QPoint m_lastMousePos; + int m_vertex; + }; +#endif + + void initEdges(); + bool calculateIntersection(int left, int right); + bool edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const; + QRBTree<int>::Node *searchEdgeLeftOf(int edgeIndex) const; + QRBTree<int>::Node *searchEdgeLeftOf(int edgeIndex, QRBTree<int>::Node *after) const; + QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> bounds(const QPodPoint &point) const; + QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> outerBounds(const QPodPoint &point) const; + void splitEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost, int vertex, const QIntersectionPoint &intersectionPoint); + void reorderEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost); + void sortEdgeList(const QPodPoint eventPoint); + void fillPriorityQueue(); + void calculateIntersections(); + int splitEdge(int splitIndex); + bool splitEdgesAtIntersections(); + void insertEdgeIntoVectorIfWanted(ShortArray &orderedEdges, int i); + void removeUnwantedEdgesAndConnect(); + void removeUnusedPoints(); + + QTriangulator *m_parent; + QDataBuffer<Edge> m_edges; + QRBTree<int> m_edgeList; + QDataBuffer<Event> m_events; + QDataBuffer<Split> m_splits; + QMaxHeap<Intersection> m_topIntersection; + QInt64Set m_processedEdgePairs; + int m_initialPointCount; + }; +#ifdef Q_TRIANGULATOR_DEBUG + friend class ComplexToSimple::DebugDialog; +#endif + + //=================================// + // QTriangulator::SimpleToMonotone // + //=================================// + friend class SimpleToMonotone; + class SimpleToMonotone + { + public: + inline SimpleToMonotone(QTriangulator<T> *parent) : m_parent(parent), m_edges(0), m_upperVertex(0) { } + void decompose(); + private: + enum VertexType {MergeVertex, EndVertex, RegularVertex, StartVertex, SplitVertex}; + + struct Edge + { + QRBTree<int>::Node *node; + int helper, twin, next, previous; + T from, to; + VertexType type; + bool pointingUp; + int upper() const {return (pointingUp ? to : from);} + int lower() const {return (pointingUp ? from : to);} + }; + + friend class CompareVertices; + class CompareVertices + { + public: + CompareVertices(SimpleToMonotone *parent) : m_parent(parent) { } + bool operator () (int i, int j) const; + private: + SimpleToMonotone *m_parent; + }; + + void setupDataStructures(); + void removeZeroLengthEdges(); + void fillPriorityQueue(); + bool edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const; + // Returns the rightmost edge not to the right of the given edge. + QRBTree<int>::Node *searchEdgeLeftOfEdge(int edgeIndex) const; + // Returns the rightmost edge left of the given point. + QRBTree<int>::Node *searchEdgeLeftOfPoint(int pointIndex) const; + void classifyVertex(int i); + void classifyVertices(); + bool pointIsInSector(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2, const QPodPoint &v3); + bool pointIsInSector(int vertex, int sector); + int findSector(int edge, int vertex); + void createDiagonal(int lower, int upper); + void monotoneDecomposition(); + + QTriangulator *m_parent; + QRBTree<int> m_edgeList; + QDataBuffer<Edge> m_edges; + QDataBuffer<int> m_upperVertex; + bool m_clockwiseOrder; + }; + + //====================================// + // QTriangulator::MonotoneToTriangles // + //====================================// + friend class MonotoneToTriangles; + class MonotoneToTriangles + { + public: + inline MonotoneToTriangles(QTriangulator<T> *parent) : m_parent(parent) { } + void decompose(); + private: + inline T indices(int index) const {return m_parent->m_indices.at(index + m_first);} + inline int next(int index) const {return (index + 1) % m_length;} + inline int previous(int index) const {return (index + m_length - 1) % m_length;} + inline bool less(int i, int j) const {return m_parent->m_vertices.at((qint32)indices(i)) < m_parent->m_vertices.at(indices(j));} + inline bool leftOfEdge(int i, int j, int k) const + { + return qPointIsLeftOfLine(m_parent->m_vertices.at((qint32)indices(i)), + m_parent->m_vertices.at((qint32)indices(j)), m_parent->m_vertices.at((qint32)indices(k))); + } + + QTriangulator<T> *m_parent; + int m_first; + int m_length; + }; + + inline QTriangulator() : m_vertices(0) { } + + // Call this only once. + void initialize(const qreal *polygon, int count, uint hint, const QTransform &matrix); + // Call this only once. + void initialize(const QVectorPath &path, const QTransform &matrix, qreal lod); + // Call this only once. + void initialize(const QPainterPath &path, const QTransform &matrix, qreal lod); + // Call either triangulate() or polyline() only once. + QVertexSet<T> triangulate(); + QVertexSet<T> polyline(); +private: + QDataBuffer<QPodPoint> m_vertices; + QVector<T> m_indices; + uint m_hint; +}; + +//============================================================================// +// QTriangulator // +//============================================================================// + +template <typename T> +QVertexSet<T> QTriangulator<T>::triangulate() +{ + for (int i = 0; i < m_vertices.size(); ++i) { + Q_ASSERT(qAbs(m_vertices.at(i).x) < (1 << 21)); + Q_ASSERT(qAbs(m_vertices.at(i).y) < (1 << 21)); + } + + if (!(m_hint & (QVectorPath::OddEvenFill | QVectorPath::WindingFill))) + m_hint |= QVectorPath::OddEvenFill; + + if (m_hint & QVectorPath::NonConvexShapeMask) { + ComplexToSimple c2s(this); + c2s.decompose(); + SimpleToMonotone s2m(this); + s2m.decompose(); + } + MonotoneToTriangles m2t(this); + m2t.decompose(); + + QVertexSet<T> result; + result.indices = m_indices; + result.vertices.resize(2 * m_vertices.size()); + for (int i = 0; i < m_vertices.size(); ++i) { + result.vertices[2 * i + 0] = qreal(m_vertices.at(i).x) / Q_FIXED_POINT_SCALE; + result.vertices[2 * i + 1] = qreal(m_vertices.at(i).y) / Q_FIXED_POINT_SCALE; + } + return result; +} + +template <typename T> +QVertexSet<T> QTriangulator<T>::polyline() +{ + for (int i = 0; i < m_vertices.size(); ++i) { + Q_ASSERT(qAbs(m_vertices.at(i).x) < (1 << 21)); + Q_ASSERT(qAbs(m_vertices.at(i).y) < (1 << 21)); + } + + if (!(m_hint & (QVectorPath::OddEvenFill | QVectorPath::WindingFill))) + m_hint |= QVectorPath::OddEvenFill; + + if (m_hint & QVectorPath::NonConvexShapeMask) { + ComplexToSimple c2s(this); + c2s.decompose(); + } + + QVertexSet<T> result; + result.indices = m_indices; + result.vertices.resize(2 * m_vertices.size()); + for (int i = 0; i < m_vertices.size(); ++i) { + result.vertices[2 * i + 0] = qreal(m_vertices.at(i).x) / Q_FIXED_POINT_SCALE; + result.vertices[2 * i + 1] = qreal(m_vertices.at(i).y) / Q_FIXED_POINT_SCALE; + } + return result; +} + +template <typename T> +void QTriangulator<T>::initialize(const qreal *polygon, int count, uint hint, const QTransform &matrix) +{ + m_hint = hint; + m_vertices.resize(count); + m_indices.resize(count + 1); + for (int i = 0; i < count; ++i) { + qreal x, y; + matrix.map(polygon[2 * i + 0], polygon[2 * i + 1], &x, &y); + m_vertices.at(i).x = qRound(x * Q_FIXED_POINT_SCALE); + m_vertices.at(i).y = qRound(y * Q_FIXED_POINT_SCALE); + m_indices[i] = i; + } + m_indices[count] = T(-1); //Q_TRIANGULATE_END_OF_POLYGON +} + +template <typename T> +void QTriangulator<T>::initialize(const QVectorPath &path, const QTransform &matrix, qreal lod) +{ + m_hint = path.hints(); + // Curved paths will be converted to complex polygons. + m_hint &= ~QVectorPath::CurvedShapeMask; + + const qreal *p = path.points(); + const QPainterPath::ElementType *e = path.elements(); + if (e) { + for (int i = 0; i < path.elementCount(); ++i, ++e, p += 2) { + switch (*e) { + case QPainterPath::MoveToElement: + if (!m_indices.isEmpty()) + m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON + // Fall through. + case QPainterPath::LineToElement: + m_indices.push_back(T(m_vertices.size())); + m_vertices.resize(m_vertices.size() + 1); + qreal x, y; + matrix.map(p[0], p[1], &x, &y); + m_vertices.last().x = qRound(x * Q_FIXED_POINT_SCALE); + m_vertices.last().y = qRound(y * Q_FIXED_POINT_SCALE); + break; + case QPainterPath::CurveToElement: + { + qreal pts[8]; + for (int i = 0; i < 4; ++i) + matrix.map(p[2 * i - 2], p[2 * i - 1], &pts[2 * i + 0], &pts[2 * i + 1]); + for (int i = 0; i < 8; ++i) + pts[i] *= lod; + QBezier bezier = QBezier::fromPoints(QPointF(pts[0], pts[1]), QPointF(pts[2], pts[3]), QPointF(pts[4], pts[5]), QPointF(pts[6], pts[7])); + QPolygonF poly = bezier.toPolygon(); + // Skip first point, it already exists in 'm_vertices'. + for (int j = 1; j < poly.size(); ++j) { + m_indices.push_back(T(m_vertices.size())); + m_vertices.resize(m_vertices.size() + 1); + m_vertices.last().x = qRound(poly.at(j).x() * Q_FIXED_POINT_SCALE / lod); + m_vertices.last().y = qRound(poly.at(j).y() * Q_FIXED_POINT_SCALE / lod); + } + } + i += 2; + e += 2; + p += 4; + break; + default: + Q_ASSERT_X(0, "QTriangulator::triangulate", "Unexpected element type."); + break; + } + } + } else { + for (int i = 0; i < path.elementCount(); ++i, p += 2) { + m_indices.push_back(T(m_vertices.size())); + m_vertices.resize(m_vertices.size() + 1); + qreal x, y; + matrix.map(p[0], p[1], &x, &y); + m_vertices.last().x = qRound(x * Q_FIXED_POINT_SCALE); + m_vertices.last().y = qRound(y * Q_FIXED_POINT_SCALE); + } + } + m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON +} + +template <typename T> +void QTriangulator<T>::initialize(const QPainterPath &path, const QTransform &matrix, qreal lod) +{ + initialize(qtVectorPathForPath(path), matrix, lod); +} + +//============================================================================// +// QTriangulator::ComplexToSimple // +//============================================================================// +template <typename T> +void QTriangulator<T>::ComplexToSimple::decompose() +{ + m_initialPointCount = m_parent->m_vertices.size(); + initEdges(); + do { + calculateIntersections(); + } while (splitEdgesAtIntersections()); + + removeUnwantedEdgesAndConnect(); + removeUnusedPoints(); + + m_parent->m_indices.clear(); + QBitArray processed(m_edges.size(), false); + for (int first = 0; first < m_edges.size(); ++first) { + // If already processed, or if unused path, skip. + if (processed.at(first) || m_edges.at(first).next == -1) + continue; + + int i = first; + do { + Q_ASSERT(!processed.at(i)); + Q_ASSERT(m_edges.at(m_edges.at(i).next).previous == i); + m_parent->m_indices.push_back(m_edges.at(i).from); + processed.setBit(i); + i = m_edges.at(i).next; // CCW order + } while (i != first); + m_parent->m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON + } +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::initEdges() +{ + // Initialize edge structure. + // 'next' and 'previous' are not being initialized at this point. + int first = 0; + for (int i = 0; i < m_parent->m_indices.size(); ++i) { + if (m_parent->m_indices.at(i) == T(-1)) { // Q_TRIANGULATE_END_OF_POLYGON + if (m_edges.size() != first) + m_edges.last().to = m_edges.at(first).from; + first = m_edges.size(); + } else { + Q_ASSERT(i + 1 < m_parent->m_indices.size()); + // {node, from, to, next, previous, winding, mayIntersect, pointingUp, originallyPointingUp} + Edge edge = {0, m_parent->m_indices.at(i), m_parent->m_indices.at(i + 1), -1, -1, 0, true, false, false}; + m_edges.add(edge); + } + } + if (first != m_edges.size()) + m_edges.last().to = m_edges.at(first).from; + for (int i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).originallyPointingUp = m_edges.at(i).pointingUp = + m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from); + } +} + +// Return true if new intersection was found +template <typename T> +bool QTriangulator<T>::ComplexToSimple::calculateIntersection(int left, int right) +{ + const Edge &e1 = m_edges.at(left); + const Edge &e2 = m_edges.at(right); + + const QPodPoint &u1 = m_parent->m_vertices.at((qint32)e1.from); + const QPodPoint &u2 = m_parent->m_vertices.at((qint32)e1.to); + const QPodPoint &v1 = m_parent->m_vertices.at((qint32)e2.from); + const QPodPoint &v2 = m_parent->m_vertices.at((qint32)e2.to); + if (qMax(u1.x, u2.x) <= qMin(v1.x, v2.x)) + return false; + + quint64 key = (left > right ? (quint64(right) << 32) | quint64(left) : (quint64(left) << 32) | quint64(right)); + if (m_processedEdgePairs.contains(key)) + return false; + m_processedEdgePairs.insert(key); + + Intersection intersection; + intersection.leftEdge = left; + intersection.rightEdge = right; + intersection.intersectionPoint = QT_PREPEND_NAMESPACE(qIntersectionPoint)(u1, u2, v1, v2); + + if (!intersection.intersectionPoint.isValid()) + return false; + + Q_ASSERT(intersection.intersectionPoint.isOnLine(u1, u2)); + Q_ASSERT(intersection.intersectionPoint.isOnLine(v1, v2)); + + intersection.vertex = m_parent->m_vertices.size(); + m_topIntersection.push(intersection); + m_parent->m_vertices.add(intersection.intersectionPoint.round()); + return true; +} + +template <typename T> +bool QTriangulator<T>::ComplexToSimple::edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const +{ + const Edge &leftEdge = m_edges.at(leftEdgeIndex); + const Edge &rightEdge = m_edges.at(rightEdgeIndex); + const QPodPoint &u = m_parent->m_vertices.at(rightEdge.upper()); + const QPodPoint &l = m_parent->m_vertices.at(rightEdge.lower()); + const QPodPoint &upper = m_parent->m_vertices.at(leftEdge.upper()); + if (upper.x < qMin(l.x, u.x)) + return true; + if (upper.x > qMax(l.x, u.x)) + return false; + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(upper, l, u); + // d < 0: left, d > 0: right, d == 0: on top + if (d == 0) + d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(leftEdge.lower()), l, u); + return d < 0; +} + +template <typename T> +QRBTree<int>::Node *QTriangulator<T>::ComplexToSimple::searchEdgeLeftOf(int edgeIndex) const +{ + QRBTree<int>::Node *current = m_edgeList.root; + QRBTree<int>::Node *result = 0; + while (current) { + if (edgeIsLeftOfEdge(edgeIndex, current->data)) { + current = current->left; + } else { + result = current; + current = current->right; + } + } + return result; +} + +template <typename T> +QRBTree<int>::Node *QTriangulator<T>::ComplexToSimple::searchEdgeLeftOf(int edgeIndex, QRBTree<int>::Node *after) const +{ + if (!m_edgeList.root) + return after; + QRBTree<int>::Node *result = after; + QRBTree<int>::Node *current = (after ? m_edgeList.next(after) : m_edgeList.front(m_edgeList.root)); + while (current) { + if (edgeIsLeftOfEdge(edgeIndex, current->data)) + return result; + result = current; + current = m_edgeList.next(current); + } + return result; +} + +template <typename T> +QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> QTriangulator<T>::ComplexToSimple::bounds(const QPodPoint &point) const +{ + QRBTree<int>::Node *current = m_edgeList.root; + QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> result(0, 0); + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); + if (d == 0) { + result.first = result.second = current; + break; + } + current = (d < 0 ? current->left : current->right); + } + if (current == 0) + return result; + + current = result.first->left; + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); + Q_ASSERT(d >= 0); + if (d == 0) { + result.first = current; + current = current->left; + } else { + current = current->right; + } + } + + current = result.second->right; + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); + Q_ASSERT(d <= 0); + if (d == 0) { + result.second = current; + current = current->right; + } else { + current = current->left; + } + } + + return result; +} + +template <typename T> +QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> QTriangulator<T>::ComplexToSimple::outerBounds(const QPodPoint &point) const +{ + QRBTree<int>::Node *current = m_edgeList.root; + QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> result(0, 0); + + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); + if (d == 0) + break; + if (d < 0) { + result.second = current; + current = current->left; + } else { + result.first = current; + current = current->right; + } + } + + if (!current) + return result; + + QRBTree<int>::Node *mid = current; + + current = mid->left; + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); + Q_ASSERT(d >= 0); + if (d == 0) { + current = current->left; + } else { + result.first = current; + current = current->right; + } + } + + current = mid->right; + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); + Q_ASSERT(d <= 0); + if (d == 0) { + current = current->right; + } else { + result.second = current; + current = current->left; + } + } + + return result; +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::splitEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost, int vertex, const QIntersectionPoint &intersectionPoint) +{ + Q_ASSERT(leftmost && rightmost); + + // Split. + for (;;) { + const QPodPoint &u = m_parent->m_vertices.at(m_edges.at(leftmost->data).from); + const QPodPoint &v = m_parent->m_vertices.at(m_edges.at(leftmost->data).to); + Q_ASSERT(intersectionPoint.isOnLine(u, v)); + const Split split = {vertex, leftmost->data, intersectionPoint.isAccurate()}; + if (intersectionPoint.xOffset.numerator != 0 || intersectionPoint.yOffset.numerator != 0 || (intersectionPoint.upperLeft != u && intersectionPoint.upperLeft != v)) + m_splits.add(split); + if (leftmost == rightmost) + break; + leftmost = m_edgeList.next(leftmost); + } +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::reorderEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost) +{ + Q_ASSERT(leftmost && rightmost); + + QRBTree<int>::Node *storeLeftmost = leftmost; + QRBTree<int>::Node *storeRightmost = rightmost; + + // Reorder. + while (leftmost != rightmost) { + Edge &left = m_edges.at(leftmost->data); + Edge &right = m_edges.at(rightmost->data); + qSwap(left.node, right.node); + qSwap(leftmost->data, rightmost->data); + leftmost = m_edgeList.next(leftmost); + if (leftmost == rightmost) + break; + rightmost = m_edgeList.previous(rightmost); + } + + rightmost = m_edgeList.next(storeRightmost); + leftmost = m_edgeList.previous(storeLeftmost); + if (leftmost) + calculateIntersection(leftmost->data, storeLeftmost->data); + if (rightmost) + calculateIntersection(storeRightmost->data, rightmost->data); +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::sortEdgeList(const QPodPoint eventPoint) +{ + QIntersectionPoint eventPoint2 = QT_PREPEND_NAMESPACE(qIntersectionPoint)(eventPoint); + while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint < eventPoint2) { + Intersection intersection = m_topIntersection.pop(); + + QIntersectionPoint currentIntersectionPoint = intersection.intersectionPoint; + int currentVertex = intersection.vertex; + + QRBTree<int>::Node *leftmost = m_edges.at(intersection.leftEdge).node; + QRBTree<int>::Node *rightmost = m_edges.at(intersection.rightEdge).node; + + for (;;) { + QRBTree<int>::Node *previous = m_edgeList.previous(leftmost); + if (!previous) + break; + const Edge &edge = m_edges.at(previous->data); + const QPodPoint &u = m_parent->m_vertices.at((qint32)edge.from); + const QPodPoint &v = m_parent->m_vertices.at((qint32)edge.to); + if (!currentIntersectionPoint.isOnLine(u, v)) { + Q_ASSERT(!currentIntersectionPoint.isAccurate() || qCross(currentIntersectionPoint.upperLeft - u, v - u) != 0); + break; + } + leftmost = previous; + } + + for (;;) { + QRBTree<int>::Node *next = m_edgeList.next(rightmost); + if (!next) + break; + const Edge &edge = m_edges.at(next->data); + const QPodPoint &u = m_parent->m_vertices.at((qint32)edge.from); + const QPodPoint &v = m_parent->m_vertices.at((qint32)edge.to); + if (!currentIntersectionPoint.isOnLine(u, v)) { + Q_ASSERT(!currentIntersectionPoint.isAccurate() || qCross(currentIntersectionPoint.upperLeft - u, v - u) != 0); + break; + } + rightmost = next; + } + + Q_ASSERT(leftmost && rightmost); + splitEdgeListRange(leftmost, rightmost, currentVertex, currentIntersectionPoint); + reorderEdgeListRange(leftmost, rightmost); + + while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint <= currentIntersectionPoint) + m_topIntersection.pop(); + +#ifdef Q_TRIANGULATOR_DEBUG + DebugDialog dialog(this, intersection.vertex); + dialog.exec(); +#endif + + } +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::fillPriorityQueue() +{ + m_events.reset(); + m_events.reserve(m_edges.size() * 2); + for (int i = 0; i < m_edges.size(); ++i) { + Q_ASSERT(m_edges.at(i).previous == -1 && m_edges.at(i).next == -1); + Q_ASSERT(m_edges.at(i).node == 0); + Q_ASSERT(m_edges.at(i).pointingUp == m_edges.at(i).originallyPointingUp); + Q_ASSERT(m_edges.at(i).pointingUp == (m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from))); + // Ignore zero-length edges. + if (m_parent->m_vertices.at(m_edges.at(i).to) != m_parent->m_vertices.at(m_edges.at(i).from)) { + QPodPoint upper = m_parent->m_vertices.at(m_edges.at(i).upper()); + QPodPoint lower = m_parent->m_vertices.at(m_edges.at(i).lower()); + Event upperEvent = {{upper.x, upper.y}, Event::Upper, i}; + Event lowerEvent = {{lower.x, lower.y}, Event::Lower, i}; + m_events.add(upperEvent); + m_events.add(lowerEvent); + } + } + //qSort(m_events.data(), m_events.data() + m_events.size()); + sort(m_events.data(), m_events.size()); +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::calculateIntersections() +{ + fillPriorityQueue(); + + Q_ASSERT(m_topIntersection.empty()); + Q_ASSERT(m_edgeList.root == 0); + + // Find all intersection points. + while (!m_events.isEmpty()) { + Event event = m_events.last(); + sortEdgeList(event.point); + + // Find all edges in the edge list that contain the current vertex and mark them to be split later. + QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> range = bounds(event.point); + QRBTree<int>::Node *leftNode = range.first ? m_edgeList.previous(range.first) : 0; + int vertex = (event.type == Event::Upper ? m_edges.at(event.edge).upper() : m_edges.at(event.edge).lower()); + QIntersectionPoint eventPoint = QT_PREPEND_NAMESPACE(qIntersectionPoint)(event.point); + + if (range.first != 0) { + splitEdgeListRange(range.first, range.second, vertex, eventPoint); + reorderEdgeListRange(range.first, range.second); + } + + // Handle the edges with start or end point in the current vertex. + while (!m_events.isEmpty() && m_events.last().point == event.point) { + event = m_events.last(); + m_events.pop_back(); + int i = event.edge; + + if (m_edges.at(i).node) { + // Remove edge from edge list. + Q_ASSERT(event.type == Event::Lower); + QRBTree<int>::Node *left = m_edgeList.previous(m_edges.at(i).node); + QRBTree<int>::Node *right = m_edgeList.next(m_edges.at(i).node); + m_edgeList.deleteNode(m_edges.at(i).node); + if (!left || !right) + continue; + calculateIntersection(left->data, right->data); + } else { + // Insert edge into edge list. + Q_ASSERT(event.type == Event::Upper); + QRBTree<int>::Node *left = searchEdgeLeftOf(i, leftNode); + m_edgeList.attachAfter(left, m_edges.at(i).node = m_edgeList.newNode()); + m_edges.at(i).node->data = i; + QRBTree<int>::Node *right = m_edgeList.next(m_edges.at(i).node); + if (left) + calculateIntersection(left->data, i); + if (right) + calculateIntersection(i, right->data); + } + } + while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint <= eventPoint) + m_topIntersection.pop(); +#ifdef Q_TRIANGULATOR_DEBUG + DebugDialog dialog(this, vertex); + dialog.exec(); +#endif + } + m_processedEdgePairs.clear(); +} + +// Split an edge into two pieces at the given point. +// The upper piece is pushed to the end of the 'm_edges' vector. +// The lower piece replaces the old edge. +// Return the edge whose 'from' is 'pointIndex'. +template <typename T> +int QTriangulator<T>::ComplexToSimple::splitEdge(int splitIndex) +{ + const Split &split = m_splits.at(splitIndex); + Edge &lowerEdge = m_edges.at(split.edge); + Q_ASSERT(lowerEdge.node == 0); + Q_ASSERT(lowerEdge.previous == -1 && lowerEdge.next == -1); + + if (lowerEdge.from == split.vertex) + return split.edge; + if (lowerEdge.to == split.vertex) + return lowerEdge.next; + + // Check that angle >= 90 degrees. + //Q_ASSERT(qDot(m_points.at(m_edges.at(edgeIndex).from) - m_points.at(pointIndex), + // m_points.at(m_edges.at(edgeIndex).to) - m_points.at(pointIndex)) <= 0); + + Edge upperEdge = lowerEdge; + upperEdge.mayIntersect |= !split.accurate; // The edge may have been split before at an inaccurate split point. + lowerEdge.mayIntersect = !split.accurate; + if (lowerEdge.pointingUp) { + lowerEdge.to = upperEdge.from = split.vertex; + m_edges.add(upperEdge); + return m_edges.size() - 1; + } else { + lowerEdge.from = upperEdge.to = split.vertex; + m_edges.add(upperEdge); + return split.edge; + } +} + +template <typename T> +bool QTriangulator<T>::ComplexToSimple::splitEdgesAtIntersections() +{ + for (int i = 0; i < m_edges.size(); ++i) + m_edges.at(i).mayIntersect = false; + bool checkForNewIntersections = false; + for (int i = 0; i < m_splits.size(); ++i) { + splitEdge(i); + checkForNewIntersections |= !m_splits.at(i).accurate; + } + for (int i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).originallyPointingUp = m_edges.at(i).pointingUp = + m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from); + } + m_splits.reset(); + return checkForNewIntersections; +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::insertEdgeIntoVectorIfWanted(ShortArray &orderedEdges, int i) +{ + // Edges with zero length should not reach this part. + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(i).from) != m_parent->m_vertices.at(m_edges.at(i).to)); + + // Skip edges with unwanted winding number. + int windingNumber = m_edges.at(i).winding; + if (m_edges.at(i).originallyPointingUp) + ++windingNumber; + + // Make sure exactly one fill rule is specified. + Q_ASSERT(((m_parent->m_hint & QVectorPath::WindingFill) != 0) != ((m_parent->m_hint & QVectorPath::OddEvenFill) != 0)); + + if ((m_parent->m_hint & QVectorPath::WindingFill) && windingNumber != 0 && windingNumber != 1) + return; + + // Skip cancelling edges. + if (!orderedEdges.isEmpty()) { + int j = orderedEdges[orderedEdges.size() - 1]; + // If the last edge is already connected in one end, it should not be cancelled. + if (m_edges.at(j).next == -1 && m_edges.at(j).previous == -1 + && (m_parent->m_vertices.at(m_edges.at(i).from) == m_parent->m_vertices.at(m_edges.at(j).to)) + && (m_parent->m_vertices.at(m_edges.at(i).to) == m_parent->m_vertices.at(m_edges.at(j).from))) { + orderedEdges.removeLast(); + return; + } + } + orderedEdges.append(i); +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::removeUnwantedEdgesAndConnect() +{ + Q_ASSERT(m_edgeList.root == 0); + // Initialize priority queue. + fillPriorityQueue(); + + ShortArray orderedEdges; + + while (!m_events.isEmpty()) { + Event event = m_events.last(); + int edgeIndex = event.edge; + + // Check that all the edges in the list crosses the current scanline + //if (m_edgeList.root) { + // for (QRBTree<int>::Node *node = m_edgeList.front(m_edgeList.root); node; node = m_edgeList.next(node)) { + // Q_ASSERT(event.point <= m_points.at(m_edges.at(node->data).lower())); + // } + //} + + orderedEdges.clear(); + QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> b = outerBounds(event.point); + if (m_edgeList.root) { + QRBTree<int>::Node *current = (b.first ? m_edgeList.next(b.first) : m_edgeList.front(m_edgeList.root)); + // Process edges that are going to be removed from the edge list at the current event point. + while (current != b.second) { + Q_ASSERT(current); + Q_ASSERT(m_edges.at(current->data).node == current); + Q_ASSERT(QT_PREPEND_NAMESPACE(qIntersectionPoint)(event.point).isOnLine(m_parent->m_vertices.at(m_edges.at(current->data).from), m_parent->m_vertices.at(m_edges.at(current->data).to))); + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(current->data).from) == event.point || m_parent->m_vertices.at(m_edges.at(current->data).to) == event.point); + insertEdgeIntoVectorIfWanted(orderedEdges, current->data); + current = m_edgeList.next(current); + } + } + + // Remove edges above the event point, insert edges below the event point. + do { + event = m_events.last(); + m_events.pop_back(); + edgeIndex = event.edge; + + // Edges with zero length should not reach this part. + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(edgeIndex).from) != m_parent->m_vertices.at(m_edges.at(edgeIndex).to)); + + if (m_edges.at(edgeIndex).node) { + Q_ASSERT(event.type == Event::Lower); + Q_ASSERT(event.point == m_parent->m_vertices.at(m_edges.at(event.edge).lower())); + m_edgeList.deleteNode(m_edges.at(edgeIndex).node); + } else { + Q_ASSERT(event.type == Event::Upper); + Q_ASSERT(event.point == m_parent->m_vertices.at(m_edges.at(event.edge).upper())); + QRBTree<int>::Node *left = searchEdgeLeftOf(edgeIndex, b.first); + m_edgeList.attachAfter(left, m_edges.at(edgeIndex).node = m_edgeList.newNode()); + m_edges.at(edgeIndex).node->data = edgeIndex; + } + } while (!m_events.isEmpty() && m_events.last().point == event.point); + + if (m_edgeList.root) { + QRBTree<int>::Node *current = (b.first ? m_edgeList.next(b.first) : m_edgeList.front(m_edgeList.root)); + + // Calculate winding number and turn counter-clockwise. + int currentWindingNumber = (b.first ? m_edges.at(b.first->data).winding : 0); + while (current != b.second) { + Q_ASSERT(current); + //Q_ASSERT(b.second == 0 || m_edgeList.order(current, b.second) < 0); + int i = current->data; + Q_ASSERT(m_edges.at(i).node == current); + + // Winding number. + int ccwWindingNumber = m_edges.at(i).winding = currentWindingNumber; + if (m_edges.at(i).originallyPointingUp) { + --m_edges.at(i).winding; + } else { + ++m_edges.at(i).winding; + ++ccwWindingNumber; + } + currentWindingNumber = m_edges.at(i).winding; + + // Turn counter-clockwise. + if ((ccwWindingNumber & 1) == 0) { + Q_ASSERT(m_edges.at(i).previous == -1 && m_edges.at(i).next == -1); + qSwap(m_edges.at(i).from, m_edges.at(i).to); + m_edges.at(i).pointingUp = !m_edges.at(i).pointingUp; + } + + current = m_edgeList.next(current); + } + + // Process edges that were inserted into the edge list at the current event point. + current = (b.second ? m_edgeList.previous(b.second) : m_edgeList.back(m_edgeList.root)); + while (current != b.first) { + Q_ASSERT(current); + Q_ASSERT(m_edges.at(current->data).node == current); + insertEdgeIntoVectorIfWanted(orderedEdges, current->data); + current = m_edgeList.previous(current); + } + } + if (orderedEdges.isEmpty()) + continue; + + Q_ASSERT((orderedEdges.size() & 1) == 0); + + // Connect edges. + // First make sure the first edge point towards the current point. + int i; + if (m_parent->m_vertices.at(m_edges.at(orderedEdges[0]).from) == event.point) { + i = 1; + int copy = orderedEdges[0]; // Make copy in case the append() will cause a reallocation. + orderedEdges.append(copy); + } else { + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[0]).to) == event.point); + i = 0; + } + + // Remove references to duplicate points. First find the point with lowest index. + int pointIndex = INT_MAX; + for (int j = i; j < orderedEdges.size(); j += 2) { + Q_ASSERT(j + 1 < orderedEdges.size()); + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[j]).to) == event.point); + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[j + 1]).from) == event.point); + if (m_edges.at(orderedEdges[j]).to < pointIndex) + pointIndex = m_edges.at(orderedEdges[j]).to; + if (m_edges.at(orderedEdges[j + 1]).from < pointIndex) + pointIndex = m_edges.at(orderedEdges[j + 1]).from; + } + + for (; i < orderedEdges.size(); i += 2) { + // Remove references to duplicate points by making all edges reference one common point. + m_edges.at(orderedEdges[i]).to = m_edges.at(orderedEdges[i + 1]).from = pointIndex; + + Q_ASSERT(m_edges.at(orderedEdges[i]).pointingUp || m_edges.at(orderedEdges[i]).previous != -1); + Q_ASSERT(!m_edges.at(orderedEdges[i + 1]).pointingUp || m_edges.at(orderedEdges[i + 1]).next != -1); + + m_edges.at(orderedEdges[i]).next = orderedEdges[i + 1]; + m_edges.at(orderedEdges[i + 1]).previous = orderedEdges[i]; + } + } // end while +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::removeUnusedPoints() { + QBitArray used(m_parent->m_vertices.size(), false); + for (int i = 0; i < m_edges.size(); ++i) { + Q_ASSERT((m_edges.at(i).previous == -1) == (m_edges.at(i).next == -1)); + if (m_edges.at(i).next != -1) + used.setBit(m_edges.at(i).from); + } + QDataBuffer<quint32> newMapping(m_parent->m_vertices.size()); + newMapping.resize(m_parent->m_vertices.size()); + int count = 0; + for (int i = 0; i < m_parent->m_vertices.size(); ++i) { + if (used.at(i)) { + m_parent->m_vertices.at(count) = m_parent->m_vertices.at(i); + newMapping.at(i) = count; + ++count; + } + } + m_parent->m_vertices.resize(count); + for (int i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).from = newMapping.at(m_edges.at(i).from); + m_edges.at(i).to = newMapping.at(m_edges.at(i).to); + } +} + +template <typename T> +bool QTriangulator<T>::ComplexToSimple::CompareEdges::operator () (int i, int j) const +{ + int cmp = comparePoints(m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(i).from), + m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(j).from)); + if (cmp == 0) { + cmp = comparePoints(m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(i).to), + m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(j).to)); + } + return cmp > 0; +} + +template <typename T> +inline bool QTriangulator<T>::ComplexToSimple::Event::operator < (const Event &other) const +{ + if (point == other.point) + return type < other.type; // 'Lower' has higher priority than 'Upper'. + return other.point < point; +} + +//============================================================================// +// QTriangulator::ComplexToSimple::DebugDialog // +//============================================================================// + +#ifdef Q_TRIANGULATOR_DEBUG +template <typename T> +QTriangulator<T>::ComplexToSimple::DebugDialog::DebugDialog(ComplexToSimple *parent, int currentVertex) + : m_parent(parent), m_vertex(currentVertex) +{ + QDataBuffer<QPodPoint> &vertices = m_parent->m_parent->m_vertices; + if (vertices.isEmpty()) + return; + + int minX, maxX, minY, maxY; + minX = maxX = vertices.at(0).x; + minY = maxY = vertices.at(0).y; + for (int i = 1; i < vertices.size(); ++i) { + minX = qMin(minX, vertices.at(i).x); + maxX = qMax(maxX, vertices.at(i).x); + minY = qMin(minY, vertices.at(i).y); + maxY = qMax(maxY, vertices.at(i).y); + } + int w = maxX - minX; + int h = maxY - minY; + qreal border = qMin(w, h) / 10.0; + m_window = QRectF(minX - border, minY - border, (maxX - minX + 2 * border), (maxY - minY + 2 * border)); +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::DebugDialog::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing, true); + p.fillRect(rect(), Qt::black); + QDataBuffer<QPodPoint> &vertices = m_parent->m_parent->m_vertices; + if (vertices.isEmpty()) + return; + + qreal halfPointSize = qMin(m_window.width(), m_window.height()) / 300.0; + p.setWindow(m_window.toRect()); + + p.setPen(Qt::white); + + QDataBuffer<Edge> &edges = m_parent->m_edges; + for (int i = 0; i < edges.size(); ++i) { + QPodPoint u = vertices.at(edges.at(i).from); + QPodPoint v = vertices.at(edges.at(i).to); + p.drawLine(u.x, u.y, v.x, v.y); + } + + for (int i = 0; i < vertices.size(); ++i) { + QPodPoint q = vertices.at(i); + p.fillRect(QRectF(q.x - halfPointSize, q.y - halfPointSize, 2 * halfPointSize, 2 * halfPointSize), Qt::red); + } + + Qt::GlobalColor colors[6] = {Qt::red, Qt::green, Qt::blue, Qt::cyan, Qt::magenta, Qt::yellow}; + p.setOpacity(0.5); + int count = 0; + if (m_parent->m_edgeList.root) { + QRBTree<int>::Node *current = m_parent->m_edgeList.front(m_parent->m_edgeList.root); + while (current) { + p.setPen(colors[count++ % 6]); + QPodPoint u = vertices.at(edges.at(current->data).from); + QPodPoint v = vertices.at(edges.at(current->data).to); + p.drawLine(u.x, u.y, v.x, v.y); + current = m_parent->m_edgeList.next(current); + } + } + + p.setOpacity(1.0); + QPodPoint q = vertices.at(m_vertex); + p.fillRect(QRectF(q.x - halfPointSize, q.y - halfPointSize, 2 * halfPointSize, 2 * halfPointSize), Qt::green); + + p.setPen(Qt::gray); + QDataBuffer<Split> &splits = m_parent->m_splits; + for (int i = 0; i < splits.size(); ++i) { + QPodPoint q = vertices.at(splits.at(i).vertex); + QPodPoint u = vertices.at(edges.at(splits.at(i).edge).from) - q; + QPodPoint v = vertices.at(edges.at(splits.at(i).edge).to) - q; + qreal uLen = sqrt(qreal(qDot(u, u))); + qreal vLen = sqrt(qreal(qDot(v, v))); + if (uLen) { + u.x *= 2 * halfPointSize / uLen; + u.y *= 2 * halfPointSize / uLen; + } + if (vLen) { + v.x *= 2 * halfPointSize / vLen; + v.y *= 2 * halfPointSize / vLen; + } + u += q; + v += q; + p.drawLine(u.x, u.y, v.x, v.y); + } +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::DebugDialog::wheelEvent(QWheelEvent *event) +{ + qreal scale = exp(-0.001 * event->delta()); + QPointF center = m_window.center(); + QPointF delta = scale * (m_window.bottomRight() - center); + m_window = QRectF(center - delta, center + delta); + event->accept(); + update(); +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::DebugDialog::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton) { + QPointF delta = event->pos() - m_lastMousePos; + delta.setX(delta.x() * m_window.width() / width()); + delta.setY(delta.y() * m_window.height() / height()); + m_window.translate(-delta.x(), -delta.y()); + m_lastMousePos = event->pos(); + event->accept(); + update(); + } +} + +template <typename T> +void QTriangulator<T>::ComplexToSimple::DebugDialog::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + m_lastMousePos = event->pos(); + event->accept(); +} + + +#endif + +//============================================================================// +// QTriangulator::SimpleToMonotone // +//============================================================================// +template <typename T> +void QTriangulator<T>::SimpleToMonotone::decompose() +{ + setupDataStructures(); + removeZeroLengthEdges(); + monotoneDecomposition(); + + m_parent->m_indices.clear(); + QBitArray processed(m_edges.size(), false); + for (int first = 0; first < m_edges.size(); ++first) { + if (processed.at(first)) + continue; + int i = first; + do { + Q_ASSERT(!processed.at(i)); + Q_ASSERT(m_edges.at(m_edges.at(i).next).previous == i); + m_parent->m_indices.push_back(m_edges.at(i).from); + processed.setBit(i); + i = m_edges.at(i).next; + } while (i != first); + if (m_parent->m_indices.size() > 0 && m_parent->m_indices.back() != T(-1)) // Q_TRIANGULATE_END_OF_POLYGON + m_parent->m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON + } +} + +template <typename T> +void QTriangulator<T>::SimpleToMonotone::setupDataStructures() +{ + int i = 0; + Edge e; + e.node = 0; + e.twin = -1; + + while (i + 3 <= m_parent->m_indices.size()) { + int start = m_edges.size(); + + do { + e.from = m_parent->m_indices.at(i); + e.type = RegularVertex; + e.next = m_edges.size() + 1; + e.previous = m_edges.size() - 1; + m_edges.add(e); + ++i; + Q_ASSERT(i < m_parent->m_indices.size()); + } while (m_parent->m_indices.at(i) != T(-1)); // Q_TRIANGULATE_END_OF_POLYGON + + m_edges.last().next = start; + m_edges.at(start).previous = m_edges.size() - 1; + ++i; // Skip Q_TRIANGULATE_END_OF_POLYGON. + } + + for (i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).to = m_edges.at(m_edges.at(i).next).from; + m_edges.at(i).pointingUp = m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from); + m_edges.at(i).helper = -1; // Not initialized here. + } +} + +template <typename T> +void QTriangulator<T>::SimpleToMonotone::removeZeroLengthEdges() +{ + for (int i = 0; i < m_edges.size(); ++i) { + if (m_parent->m_vertices.at(m_edges.at(i).from) == m_parent->m_vertices.at(m_edges.at(i).to)) { + m_edges.at(m_edges.at(i).previous).next = m_edges.at(i).next; + m_edges.at(m_edges.at(i).next).previous = m_edges.at(i).previous; + m_edges.at(m_edges.at(i).next).from = m_edges.at(i).from; + m_edges.at(i).next = -1; // Mark as removed. + } + } + + QDataBuffer<int> newMapping(m_edges.size()); + newMapping.resize(m_edges.size()); + int count = 0; + for (int i = 0; i < m_edges.size(); ++i) { + if (m_edges.at(i).next != -1) { + m_edges.at(count) = m_edges.at(i); + newMapping.at(i) = count; + ++count; + } + } + m_edges.resize(count); + for (int i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).next = newMapping.at(m_edges.at(i).next); + m_edges.at(i).previous = newMapping.at(m_edges.at(i).previous); + } +} + +template <typename T> +void QTriangulator<T>::SimpleToMonotone::fillPriorityQueue() +{ + m_upperVertex.reset(); + m_upperVertex.reserve(m_edges.size()); + for (int i = 0; i < m_edges.size(); ++i) + m_upperVertex.add(i); + CompareVertices cmp(this); + //qSort(m_upperVertex.data(), m_upperVertex.data() + m_upperVertex.size(), cmp); + sort(m_upperVertex.data(), m_upperVertex.size(), cmp); + //for (int i = 1; i < m_upperVertex.size(); ++i) { + // Q_ASSERT(!cmp(m_upperVertex.at(i), m_upperVertex.at(i - 1))); + //} +} + +template <typename T> +bool QTriangulator<T>::SimpleToMonotone::edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const +{ + const Edge &leftEdge = m_edges.at(leftEdgeIndex); + const Edge &rightEdge = m_edges.at(rightEdgeIndex); + const QPodPoint &u = m_parent->m_vertices.at(rightEdge.upper()); + const QPodPoint &l = m_parent->m_vertices.at(rightEdge.lower()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(leftEdge.upper()), l, u); + // d < 0: left, d > 0: right, d == 0: on top + if (d == 0) + d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(leftEdge.lower()), l, u); + return d < 0; +} + +// Returns the rightmost edge not to the right of the given edge. +template <typename T> +QRBTree<int>::Node *QTriangulator<T>::SimpleToMonotone::searchEdgeLeftOfEdge(int edgeIndex) const +{ + QRBTree<int>::Node *current = m_edgeList.root; + QRBTree<int>::Node *result = 0; + while (current) { + if (edgeIsLeftOfEdge(edgeIndex, current->data)) { + current = current->left; + } else { + result = current; + current = current->right; + } + } + return result; +} + +// Returns the rightmost edge left of the given point. +template <typename T> +QRBTree<int>::Node *QTriangulator<T>::SimpleToMonotone::searchEdgeLeftOfPoint(int pointIndex) const +{ + QRBTree<int>::Node *current = m_edgeList.root; + QRBTree<int>::Node *result = 0; + while (current) { + const QPodPoint &p1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &p2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(pointIndex), p1, p2); + if (d <= 0) { + current = current->left; + } else { + result = current; + current = current->right; + } + } + return result; +} + +template <typename T> +void QTriangulator<T>::SimpleToMonotone::classifyVertex(int i) +{ + Edge &e2 = m_edges.at(i); + const Edge &e1 = m_edges.at(e2.previous); + + bool startOrSplit = (e1.pointingUp && !e2.pointingUp); + bool endOrMerge = (!e1.pointingUp && e2.pointingUp); + + const QPodPoint &p1 = m_parent->m_vertices.at(e1.from); + const QPodPoint &p2 = m_parent->m_vertices.at(e2.from); + const QPodPoint &p3 = m_parent->m_vertices.at(e2.to); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(p1, p2, p3); + Q_ASSERT(d != 0 || (!startOrSplit && !endOrMerge)); + + e2.type = RegularVertex; + + if (m_clockwiseOrder) { + if (startOrSplit) + e2.type = (d < 0 ? SplitVertex : StartVertex); + else if (endOrMerge) + e2.type = (d < 0 ? MergeVertex : EndVertex); + } else { + if (startOrSplit) + e2.type = (d > 0 ? SplitVertex : StartVertex); + else if (endOrMerge) + e2.type = (d > 0 ? MergeVertex : EndVertex); + } +} + +template <typename T> +void QTriangulator<T>::SimpleToMonotone::classifyVertices() +{ + for (int i = 0; i < m_edges.size(); ++i) + classifyVertex(i); +} + +template <typename T> +bool QTriangulator<T>::SimpleToMonotone::pointIsInSector(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2, const QPodPoint &v3) +{ + bool leftOfPreviousEdge = !qPointIsLeftOfLine(p, v2, v1); + bool leftOfNextEdge = !qPointIsLeftOfLine(p, v3, v2); + + if (qPointIsLeftOfLine(v1, v2, v3)) + return leftOfPreviousEdge && leftOfNextEdge; + else + return leftOfPreviousEdge || leftOfNextEdge; +} + +template <typename T> +bool QTriangulator<T>::SimpleToMonotone::pointIsInSector(int vertex, int sector) +{ + const QPodPoint ¢er = m_parent->m_vertices.at(m_edges.at(sector).from); + // Handle degenerate edges. + while (m_parent->m_vertices.at(m_edges.at(vertex).from) == center) + vertex = m_edges.at(vertex).next; + int next = m_edges.at(sector).next; + while (m_parent->m_vertices.at(m_edges.at(next).from) == center) + next = m_edges.at(next).next; + int previous = m_edges.at(sector).previous; + while (m_parent->m_vertices.at(m_edges.at(previous).from) == center) + previous = m_edges.at(previous).previous; + + const QPodPoint &p = m_parent->m_vertices.at(m_edges.at(vertex).from); + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(previous).from); + const QPodPoint &v3 = m_parent->m_vertices.at(m_edges.at(next).from); + if (m_clockwiseOrder) + return pointIsInSector(p, v3, center, v1); + else + return pointIsInSector(p, v1, center, v3); +} + +template <typename T> +int QTriangulator<T>::SimpleToMonotone::findSector(int edge, int vertex) +{ + while (!pointIsInSector(vertex, edge)) { + edge = m_edges.at(m_edges.at(edge).previous).twin; + Q_ASSERT(edge != -1); + } + return edge; +} + +template <typename T> +void QTriangulator<T>::SimpleToMonotone::createDiagonal(int lower, int upper) +{ + lower = findSector(lower, upper); + upper = findSector(upper, lower); + + int prevLower = m_edges.at(lower).previous; + int prevUpper = m_edges.at(upper).previous; + + Edge e; + + e.twin = m_edges.size() + 1; + e.next = upper; + e.previous = prevLower; + e.from = m_edges.at(lower).from; + e.to = m_edges.at(upper).from; + m_edges.at(upper).previous = m_edges.at(prevLower).next = int(m_edges.size()); + m_edges.add(e); + + e.twin = m_edges.size() - 1; + e.next = lower; + e.previous = prevUpper; + e.from = m_edges.at(upper).from; + e.to = m_edges.at(lower).from; + m_edges.at(lower).previous = m_edges.at(prevUpper).next = int(m_edges.size()); + m_edges.add(e); +} + +template <typename T> +void QTriangulator<T>::SimpleToMonotone::monotoneDecomposition() +{ + if (m_edges.isEmpty()) + return; + + Q_ASSERT(!m_edgeList.root); + QDataBuffer<QPair<int, int> > diagonals(m_upperVertex.size()); + + int i = 0; + for (int index = 1; index < m_edges.size(); ++index) { + if (m_parent->m_vertices.at(m_edges.at(index).from) < m_parent->m_vertices.at(m_edges.at(i).from)) + i = index; + } + Q_ASSERT(i < m_edges.size()); + int j = m_edges.at(i).previous; + Q_ASSERT(j < m_edges.size()); + m_clockwiseOrder = qPointIsLeftOfLine(m_parent->m_vertices.at((quint32)m_edges.at(i).from), + m_parent->m_vertices.at((quint32)m_edges.at(j).from), m_parent->m_vertices.at((quint32)m_edges.at(i).to)); + + classifyVertices(); + fillPriorityQueue(); + + // debug: set helpers explicitly (shouldn't be necessary) + //for (int i = 0; i < m_edges.size(); ++i) + // m_edges.at(i).helper = m_edges.at(i).upper(); + + while (!m_upperVertex.isEmpty()) { + i = m_upperVertex.last(); + Q_ASSERT(i < m_edges.size()); + m_upperVertex.pop_back(); + j = m_edges.at(i).previous; + Q_ASSERT(j < m_edges.size()); + + QRBTree<int>::Node *leftEdgeNode = 0; + + switch (m_edges.at(i).type) { + case RegularVertex: + // If polygon interior is to the right of the vertex... + if (m_edges.at(i).pointingUp == m_clockwiseOrder) { + if (m_edges.at(i).node) { + Q_ASSERT(!m_edges.at(j).node); + if (m_edges.at(m_edges.at(i).helper).type == MergeVertex) + diagonals.add(QPair<int, int>(i, m_edges.at(i).helper)); + m_edges.at(j).node = m_edges.at(i).node; + m_edges.at(i).node = 0; + m_edges.at(j).node->data = j; + m_edges.at(j).helper = i; + } else if (m_edges.at(j).node) { + Q_ASSERT(!m_edges.at(i).node); + if (m_edges.at(m_edges.at(j).helper).type == MergeVertex) + diagonals.add(QPair<int, int>(i, m_edges.at(j).helper)); + m_edges.at(i).node = m_edges.at(j).node; + m_edges.at(j).node = 0; + m_edges.at(i).node->data = i; + m_edges.at(i).helper = i; + } else { + qWarning("Inconsistent polygon. (#1)"); + } + } else { + leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from); + if (leftEdgeNode) { + if (m_edges.at(m_edges.at(leftEdgeNode->data).helper).type == MergeVertex) + diagonals.add(QPair<int, int>(i, m_edges.at(leftEdgeNode->data).helper)); + m_edges.at(leftEdgeNode->data).helper = i; + } else { + qWarning("Inconsistent polygon. (#2)"); + } + } + break; + case SplitVertex: + leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from); + if (leftEdgeNode) { + diagonals.add(QPair<int, int>(i, m_edges.at(leftEdgeNode->data).helper)); + m_edges.at(leftEdgeNode->data).helper = i; + } else { + qWarning("Inconsistent polygon. (#3)"); + } + // Fall through. + case StartVertex: + if (m_clockwiseOrder) { + leftEdgeNode = searchEdgeLeftOfEdge(j); + QRBTree<int>::Node *node = m_edgeList.newNode(); + node->data = j; + m_edges.at(j).node = node; + m_edges.at(j).helper = i; + m_edgeList.attachAfter(leftEdgeNode, node); + Q_ASSERT(m_edgeList.validate()); + } else { + leftEdgeNode = searchEdgeLeftOfEdge(i); + QRBTree<int>::Node *node = m_edgeList.newNode(); + node->data = i; + m_edges.at(i).node = node; + m_edges.at(i).helper = i; + m_edgeList.attachAfter(leftEdgeNode, node); + Q_ASSERT(m_edgeList.validate()); + } + break; + case MergeVertex: + leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from); + if (leftEdgeNode) { + if (m_edges.at(m_edges.at(leftEdgeNode->data).helper).type == MergeVertex) + diagonals.add(QPair<int, int>(i, m_edges.at(leftEdgeNode->data).helper)); + m_edges.at(leftEdgeNode->data).helper = i; + } else { + qWarning("Inconsistent polygon. (#4)"); + } + // Fall through. + case EndVertex: + if (m_clockwiseOrder) { + if (m_edges.at(m_edges.at(i).helper).type == MergeVertex) + diagonals.add(QPair<int, int>(i, m_edges.at(i).helper)); + if (m_edges.at(i).node) { + m_edgeList.deleteNode(m_edges.at(i).node); + Q_ASSERT(m_edgeList.validate()); + } else { + qWarning("Inconsistent polygon. (#5)"); + } + } else { + if (m_edges.at(m_edges.at(j).helper).type == MergeVertex) + diagonals.add(QPair<int, int>(i, m_edges.at(j).helper)); + if (m_edges.at(j).node) { + m_edgeList.deleteNode(m_edges.at(j).node); + Q_ASSERT(m_edgeList.validate()); + } else { + qWarning("Inconsistent polygon. (#6)"); + } + } + break; + } + } + + for (int i = 0; i < diagonals.size(); ++i) + createDiagonal(diagonals.at(i).first, diagonals.at(i).second); +} + +template <typename T> +bool QTriangulator<T>::SimpleToMonotone::CompareVertices::operator () (int i, int j) const +{ + if (m_parent->m_edges.at(i).from == m_parent->m_edges.at(j).from) + return m_parent->m_edges.at(i).type > m_parent->m_edges.at(j).type; + return m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(i).from) > + m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(j).from); +} + +//============================================================================// +// QTriangulator::MonotoneToTriangles // +//============================================================================// +template <typename T> +void QTriangulator<T>::MonotoneToTriangles::decompose() +{ + QVector<T> result; + QDataBuffer<int> stack(m_parent->m_indices.size()); + m_first = 0; + // Require at least three more indices. + while (m_first + 3 <= m_parent->m_indices.size()) { + m_length = 0; + while (m_parent->m_indices.at(m_first + m_length) != T(-1)) { // Q_TRIANGULATE_END_OF_POLYGON + ++m_length; + Q_ASSERT(m_first + m_length < m_parent->m_indices.size()); + } + if (m_length < 3) { + m_first += m_length + 1; + continue; + } + + int minimum = 0; + while (less(next(minimum), minimum)) + minimum = next(minimum); + while (less(previous(minimum), minimum)) + minimum = previous(minimum); + + stack.reset(); + stack.add(minimum); + int left = previous(minimum); + int right = next(minimum); + bool stackIsOnLeftSide; + bool clockwiseOrder = leftOfEdge(minimum, left, right); + + if (less(left, right)) { + stack.add(left); + left = previous(left); + stackIsOnLeftSide = true; + } else { + stack.add(right); + right = next(right); + stackIsOnLeftSide = false; + } + + for (int count = 0; count + 2 < m_length; ++count) + { + Q_ASSERT(stack.size() >= 2); + if (less(left, right)) { + if (stackIsOnLeftSide == false) { + for (int i = 0; i + 1 < stack.size(); ++i) { + result.push_back(indices(stack.at(i + 1))); + result.push_back(indices(left)); + result.push_back(indices(stack.at(i))); + } + stack.first() = stack.last(); + stack.resize(1); + } else { + while (stack.size() >= 2 && (clockwiseOrder ^ !leftOfEdge(left, stack.at(stack.size() - 2), stack.last()))) { + result.push_back(indices(stack.at(stack.size() - 2))); + result.push_back(indices(left)); + result.push_back(indices(stack.last())); + stack.pop_back(); + } + } + stack.add(left); + left = previous(left); + stackIsOnLeftSide = true; + } else { + if (stackIsOnLeftSide == true) { + for (int i = 0; i + 1 < stack.size(); ++i) { + result.push_back(indices(stack.at(i))); + result.push_back(indices(right)); + result.push_back(indices(stack.at(i + 1))); + } + stack.first() = stack.last(); + stack.resize(1); + } else { + while (stack.size() >= 2 && (clockwiseOrder ^ !leftOfEdge(right, stack.last(), stack.at(stack.size() - 2)))) { + result.push_back(indices(stack.last())); + result.push_back(indices(right)); + result.push_back(indices(stack.at(stack.size() - 2))); + stack.pop_back(); + } + } + stack.add(right); + right = next(right); + stackIsOnLeftSide = false; + } + } + + m_first += m_length + 1; + } + m_parent->m_indices = result; +} + +//============================================================================// +// qTriangulate // +//============================================================================// + +Q_GUI_EXPORT QTriangleSet qTriangulate(const qreal *polygon, + int count, uint hint, const QTransform &matrix) +{ + QTriangleSet triangleSet; +#if 0 + if (QOpenGLExtensions::glExtensions() & QOpenGLExtensions::ElementIndexUint) { + QTriangulator<quint32> triangulator; + triangulator.initialize(polygon, count, hint, matrix); + QVertexSet<quint32> vertexSet = triangulator.triangulate(); + triangleSet.vertices = vertexSet.vertices; + triangleSet.indices.setDataUint(vertexSet.indices); + + } else { +#endif + QTriangulator<quint16> triangulator; + triangulator.initialize(polygon, count, hint, matrix); + QVertexSet<quint16> vertexSet = triangulator.triangulate(); + triangleSet.vertices = vertexSet.vertices; + triangleSet.indices.setDataUshort(vertexSet.indices); +// } + return triangleSet; +} + +Q_GUI_EXPORT QTriangleSet qTriangulate(const QVectorPath &path, + const QTransform &matrix, qreal lod) +{ + QTriangleSet triangleSet; +#if 0 + if (QOpenGLExtensions::glExtensions() & QOpenGLExtensions::ElementIndexUint) { + QTriangulator<quint32> triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet<quint32> vertexSet = triangulator.triangulate(); + triangleSet.vertices = vertexSet.vertices; + triangleSet.indices.setDataUint(vertexSet.indices); + } else { +#endif + QTriangulator<quint16> triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet<quint16> vertexSet = triangulator.triangulate(); + triangleSet.vertices = vertexSet.vertices; + triangleSet.indices.setDataUshort(vertexSet.indices); +// } + return triangleSet; +} + +QTriangleSet qTriangulate(const QPainterPath &path, + const QTransform &matrix, qreal lod) +{ + QTriangleSet triangleSet; +#if 0 + if (QOpenGLExtensions::glExtensions() & QOpenGLExtensions::ElementIndexUint) { + QTriangulator<quint32> triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet<quint32> vertexSet = triangulator.triangulate(); + triangleSet.vertices = vertexSet.vertices; + triangleSet.indices.setDataUint(vertexSet.indices); + } else { +#endif + QTriangulator<quint16> triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet<quint16> vertexSet = triangulator.triangulate(); + triangleSet.vertices = vertexSet.vertices; + triangleSet.indices.setDataUshort(vertexSet.indices); +// } + return triangleSet; +} + +QPolylineSet qPolyline(const QVectorPath &path, + const QTransform &matrix, qreal lod) +{ + QPolylineSet polyLineSet; +#if 0 + if (QOpenGLExtensions::glExtensions() & QOpenGLExtensions::ElementIndexUint) { + QTriangulator<quint32> triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet<quint32> vertexSet = triangulator.polyline(); + polyLineSet.vertices = vertexSet.vertices; + polyLineSet.indices.setDataUint(vertexSet.indices); + } else { +#endif + QTriangulator<quint16> triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet<quint16> vertexSet = triangulator.polyline(); + polyLineSet.vertices = vertexSet.vertices; + polyLineSet.indices.setDataUshort(vertexSet.indices); +// } + return polyLineSet; +} + +QPolylineSet qPolyline(const QPainterPath &path, + const QTransform &matrix, qreal lod) +{ + QPolylineSet polyLineSet; +#if 0 + if (QOpenGLExtensions::glExtensions() & QOpenGLExtensions::ElementIndexUint) { + QTriangulator<quint32> triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet<quint32> vertexSet = triangulator.polyline(); + polyLineSet.vertices = vertexSet.vertices; + polyLineSet.indices.setDataUint(vertexSet.indices); + } else { +#endif + QTriangulator<quint16> triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet<quint16> vertexSet = triangulator.polyline(); + polyLineSet.vertices = vertexSet.vertices; + polyLineSet.indices.setDataUshort(vertexSet.indices); +// } + return polyLineSet; +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qtriangulator_p.h b/src/gui/opengl/qtriangulator_p.h new file mode 100644 index 0000000000..8f95d58e23 --- /dev/null +++ b/src/gui/opengl/qtriangulator_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** 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 QtGui 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 QTRIANGULATOR_P_H +#define QTRIANGULATOR_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 <QtCore/qvector.h> +#include <QtGui/private/qvectorpath_p.h> + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QVertexIndexVector +{ +public: + enum Type { + UnsignedInt, + UnsignedShort + }; + + inline Type type() const { return t; } + + inline void setDataUint(const QVector<quint32> &data) + { + t = UnsignedInt; + indices32 = data; + } + + inline void setDataUshort(const QVector<quint16> &data) + { + t = UnsignedShort; + indices16 = data; + } + + inline const void* data() const + { + if (t == UnsignedInt) + return indices32.data(); + return indices16.data(); + } + + inline int size() const + { + if (t == UnsignedInt) + return indices32.size(); + return indices16.size(); + } + + inline QVertexIndexVector &operator = (const QVertexIndexVector &other) + { + if (t == UnsignedInt) + indices32 = other.indices32; + else + indices16 = other.indices16; + + return *this; + } + +private: + + Type t; + QVector<quint32> indices32; + QVector<quint16> indices16; +}; + +struct Q_GUI_EXPORT QTriangleSet +{ + inline QTriangleSet() { } + inline QTriangleSet(const QTriangleSet &other) : vertices(other.vertices), indices(other.indices) { } + QTriangleSet &operator = (const QTriangleSet &other) {vertices = other.vertices; indices = other.indices; return *this;} + + // The vertices of a triangle are given by: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ... + QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...] + QVertexIndexVector indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...] +}; + +struct Q_GUI_EXPORT QPolylineSet +{ + inline QPolylineSet() { } + inline QPolylineSet(const QPolylineSet &other) : vertices(other.vertices), indices(other.indices) { } + QPolylineSet &operator = (const QPolylineSet &other) {vertices = other.vertices; indices = other.indices; return *this;} + + QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...] + QVertexIndexVector indices; // End of polyline is marked with -1. +}; + +// The vertex coordinates of the returned triangle set will be rounded to a grid with a mesh size +// of 1/32. The polygon is first transformed, then scaled by 32, the coordinates are rounded to +// integers, the polygon is triangulated, and then scaled back by 1/32. +// 'hint' should be a combination of QVectorPath::Hints. +// 'lod' is the level of detail. Default is 1. Curves are split into more lines when 'lod' is higher. +QTriangleSet Q_GUI_EXPORT qTriangulate(const qreal *polygon, int count, uint hint = QVectorPath::PolygonHint | QVectorPath::OddEvenFill, const QTransform &matrix = QTransform()); +QTriangleSet Q_GUI_EXPORT qTriangulate(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); +QTriangleSet Q_GUI_EXPORT qTriangulate(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); +QPolylineSet qPolyline(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); +QPolylineSet Q_GUI_EXPORT qPolyline(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); + +QT_END_NAMESPACE + +#endif |