summaryrefslogtreecommitdiffstats
path: root/src/gui/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/opengl')
-rw-r--r--src/gui/opengl/opengl.pri44
-rw-r--r--src/gui/opengl/qopengl.cpp109
-rw-r--r--src/gui/opengl/qopengl.h73
-rw-r--r--src/gui/opengl/qopengl2pexvertexarray.cpp175
-rw-r--r--src/gui/opengl/qopengl2pexvertexarray_p.h169
-rw-r--r--src/gui/opengl/qopengl_p.h200
-rw-r--r--src/gui/opengl/qopenglbuffer.cpp582
-rw-r--r--src/gui/opengl/qopenglbuffer.h132
-rw-r--r--src/gui/opengl/qopenglcolormap.cpp297
-rw-r--r--src/gui/opengl/qopenglcolormap.h105
-rw-r--r--src/gui/opengl/qopenglcustomshaderstage.cpp138
-rw-r--r--src/gui/opengl/qopenglcustomshaderstage_p.h93
-rw-r--r--src/gui/opengl/qopenglengineshadermanager.cpp881
-rw-r--r--src/gui/opengl/qopenglengineshadermanager_p.h514
-rw-r--r--src/gui/opengl/qopenglengineshadersource_p.h529
-rw-r--r--src/gui/opengl/qopenglextensions_p.h676
-rw-r--r--src/gui/opengl/qopenglframebufferobject.cpp1354
-rw-r--r--src/gui/opengl/qopenglframebufferobject.h158
-rw-r--r--src/gui/opengl/qopenglframebufferobject_p.h164
-rw-r--r--src/gui/opengl/qopenglfunctions.cpp2452
-rw-r--r--src/gui/opengl/qopenglfunctions.h2427
-rw-r--r--src/gui/opengl/qopenglgradientcache.cpp227
-rw-r--r--src/gui/opengl/qopenglgradientcache_p.h101
-rw-r--r--src/gui/opengl/qopenglpaintdevice.cpp153
-rw-r--r--src/gui/opengl/qopenglpaintdevice_p.h119
-rw-r--r--src/gui/opengl/qopenglshadercache_meego_p.h457
-rw-r--r--src/gui/opengl/qopenglshadercache_p.h98
-rw-r--r--src/gui/opengl/qopenglshaderprogram.cpp3171
-rw-r--r--src/gui/opengl/qopenglshaderprogram.h317
-rw-r--r--src/gui/opengl/qpaintengineex_opengl2.cpp2454
-rw-r--r--src/gui/opengl/qpaintengineex_opengl2_p.h338
-rw-r--r--src/gui/opengl/qrbtree_p.h573
-rw-r--r--src/gui/opengl/qtextureglyphcache_gl.cpp396
-rw-r--r--src/gui/opengl/qtextureglyphcache_gl_p.h169
-rw-r--r--src/gui/opengl/qtriangulatingstroker.cpp588
-rw-r--r--src/gui/opengl/qtriangulatingstroker_p.h157
-rw-r--r--src/gui/opengl/qtriangulator.cpp2622
-rw-r--r--src/gui/opengl/qtriangulator_p.h148
38 files changed, 23360 insertions, 0 deletions
diff --git a/src/gui/opengl/opengl.pri b/src/gui/opengl/opengl.pri
new file mode 100644
index 0000000000..0401769ceb
--- /dev/null
+++ b/src/gui/opengl/opengl.pri
@@ -0,0 +1,44 @@
+# 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/qpaintengineex_opengl2_p.h \
+ opengl/qopenglengineshadersource_p.h \
+ opengl/qopenglcustomshaderstage_p.h \
+ opengl/qtriangulatingstroker_p.h \
+ opengl/qtriangulator_p.h \
+ opengl/qrbtree_p.h \
+ opengl/qtextureglyphcache_gl_p.h \
+ opengl/qopenglshadercache_p.h \
+ opengl/qopenglshadercache_meego_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/qpaintengineex_opengl2.cpp \
+ opengl/qopenglcustomshaderstage.cpp \
+ opengl/qtriangulatingstroker.cpp \
+ opengl/qtriangulator.cpp \
+ opengl/qtextureglyphcache_gl.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..7d75794956
--- /dev/null
+++ b/src/gui/opengl/qopengl.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the 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
+# 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..612938df16
--- /dev/null
+++ b/src/gui/opengl/qopenglbuffer.cpp
@@ -0,0 +1,582 @@
+/****************************************************************************
+**
+** 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 4.7
+ \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();
+}
+
+#define ctx QOpenGLContext::currentContext();
+
+/*!
+ 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;
+}
+
+#undef ctx
+
+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;
+}
+
+#define ctx QOpenGLContext::currentContext()
+
+/*!
+ 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);
+}
+
+#undef ctx
+
+/*!
+ 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) {
+ QOpenGLFunctions(ctx).glBindBuffer(GLenum(type), 0);
+ }
+}
+
+#define ctx QOpenGLContext::currentContext()
+
+/*!
+ 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;
+}
+
+#ifndef GL_BUFFER_SIZE
+#define GL_BUFFER_SIZE 0x8764
+#endif
+
+/*!
+ 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..cbbf318249
--- /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 "qpaintengineex_opengl2_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..36ad307848
--- /dev/null
+++ b/src/gui/opengl/qopenglcustomshaderstage_p.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** 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 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..91262dc1ce
--- /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 "qpaintengineex_opengl2_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..a4d88a059e
--- /dev/null
+++ b/src/gui/opengl/qopenglextensions_p.h
@@ -0,0 +1,676 @@
+/****************************************************************************
+**
+** 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
+
+struct 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, GLintptr offset, GLsizeiptr size, GLvoid *data);
+
+private:
+ static bool isInitialized(const QOpenGLFunctionsPrivate *d) { return d != 0; }
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLExtensions::OpenGLExtensions)
+
+struct QOpenGLExtensionsPrivate : public QOpenGLFunctionsPrivate
+{
+ 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, GLintptr offset, 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, GLintptr offset, 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..543a48fc08
--- /dev/null
+++ b/src/gui/opengl/qopenglframebufferobject.cpp
@@ -0,0 +1,1354 @@
+/****************************************************************************
+**
+** 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/qpaintengineex_opengl2_p.h>
+
+#include <qwindow.h>
+#include <qlibrary.h>
+#include <qimage.h>
+
+QT_BEGIN_NAMESPACE
+
+#define QOPENGL_FUNC_CONTEXT QOpenGLContext *ctx = QOpenGLContext::currentContext();
+#define QOPENGL_FUNCP_CONTEXT QOpenGLContext *ctx = QOpenGLContext::currentContext();
+
+#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 4.6
+
+ \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;
+}
+
+/*!
+ \since 4.8
+
+ 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;
+}
+
+/*!
+ \since 4.8
+
+ 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
+{
+ QOPENGL_FUNCP_CONTEXT;
+ if (!ctx)
+ return false; // Context no longer exists.
+ GLenum status = QOpenGLFunctions(ctx).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;
+#if 1
+ 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;
+#endif
+ 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 4.2
+
+ \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
+ \since 4.3
+
+ 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);
+ QOPENGL_FUNC_CONTEXT;
+ if (!ctx)
+ return false; // Context no longer exists.
+ QOpenGLContext *current = QOpenGLContext::currentContext();
+#ifdef QT_DEBUG
+ if (!current || 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;
+ QOPENGL_FUNC_CONTEXT;
+ if (!ctx)
+ return false; // Context no longer exists.
+
+ QOpenGLContext *current = QOpenGLContext::currentContext();
+
+#ifdef QT_DEBUG
+ Q_D(QOpenGLFramebufferObject);
+ if (!current || 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;
+}
+
+/*!
+ \since 4.5
+
+ 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()
+
+ \since 4.6
+
+ 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);
+}
+
+/*!
+ \since 4.6
+
+ 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..d1bd09c629
--- /dev/null
+++ b/src/gui/opengl/qopenglframebufferobject.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** 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>
+
+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..4ef80bfa89
--- /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 4.8
+ \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, GLintptr offset, 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/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..a36982162a
--- /dev/null
+++ b/src/gui/opengl/qopenglshaderprogram.cpp
@@ -0,0 +1,3171 @@
+/****************************************************************************
+**
+** 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>
+
+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 4.6
+ \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.
+*/
+
+#ifndef GL_FRAGMENT_SHADER
+#define GL_FRAGMENT_SHADER 0x8B30
+#endif
+#ifndef GL_VERTEX_SHADER
+#define GL_VERTEX_SHADER 0x8B31
+#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_CURRENT_VERTEX_ATTRIB
+#define GL_CURRENT_VERTEX_ATTRIB 0x8626
+#endif
+#ifndef GL_SHADER_SOURCE_LENGTH
+#define GL_SHADER_SOURCE_LENGTH 0x8B88
+#endif
+#ifndef GL_SHADER_BINARY_FORMATS
+#define GL_SHADER_BINARY_FORMATS 0x8DF8
+#endif
+#ifndef GL_NUM_SHADER_BINARY_FORMATS
+#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9
+#endif
+
+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()
+{
+#ifndef QT_NO_DEBUG
+ Q_D(QOpenGLShaderProgram);
+ 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()
+ \since 4.7
+*/
+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()
+ \since 4.7
+*/
+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()
+ \since 4.7
+*/
+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()
+ \since 4.7
+*/
+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()
+ \since 4.7
+*/
+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()
+ \since 4.7
+*/
+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()
+ \since 4.7
+*/
+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()
+ \since 4.7
+*/
+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);
+}
+
+#undef ctx
+
+/*!
+ Returns the hardware limit for how many vertices a geometry shader
+ can output.
+
+ \since 4.7
+
+ \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.
+
+ \since 4.7
+
+ 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.
+
+ \since 4.7
+
+ 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.
+
+ \since 4.7
+ */
+
+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.
+
+ \since 4.7
+*/
+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.
+
+ \since 4.7
+ */
+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);
+}
+
+
+#undef ctx
+#undef context
+
+/*!
+ 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.
+
+ \since 4.7
+*/
+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/qpaintengineex_opengl2.cpp b/src/gui/opengl/qpaintengineex_opengl2.cpp
new file mode 100644
index 0000000000..7bdac27336
--- /dev/null
+++ b/src/gui/opengl/qpaintengineex_opengl2.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 "qpaintengineex_opengl2_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 "qtriangulatingstroker_p.h"
+#include "qtextureglyphcache_gl_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)
+{
+// 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);
+
+ 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);
+
+ 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);
+
+ 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);
+ 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);
+
+ 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);
+
+ 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);
+ 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);
+
+ 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) {
+
+ 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);
+ }
+
+ 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/qpaintengineex_opengl2_p.h b/src/gui/opengl/qpaintengineex_opengl2_p.h
new file mode 100644
index 0000000000..cea5cc1302
--- /dev/null
+++ b/src/gui/opengl/qpaintengineex_opengl2_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 QGRAPHICSCONTEXT_OPENGL2_P_H
+#define QGRAPHICSCONTEXT_OPENGL2_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/qtriangulatingstroker_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/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/qtextureglyphcache_gl.cpp b/src/gui/opengl/qtextureglyphcache_gl.cpp
new file mode 100644
index 0000000000..6da72d41ce
--- /dev/null
+++ b/src/gui/opengl/qtextureglyphcache_gl.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 "qtextureglyphcache_gl_p.h"
+#include "qpaintengineex_opengl2_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/qtextureglyphcache_gl_p.h b/src/gui/opengl/qtextureglyphcache_gl_p.h
new file mode 100644
index 0000000000..cf157e07df
--- /dev/null
+++ b/src/gui/opengl/qtextureglyphcache_gl_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$
+**
+****************************************************************************/
+
+#ifndef QTEXTUREGLYPHCACHE_GL_P_H
+#define QTEXTUREGLYPHCACHE_GL_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;
+
+struct QOpenGLGlyphTexture : public QOpenGLSharedResource
+{
+ 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/qtriangulatingstroker.cpp b/src/gui/opengl/qtriangulatingstroker.cpp
new file mode 100644
index 0000000000..2bd01a4307
--- /dev/null
+++ b/src/gui/opengl/qtriangulatingstroker.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 "qtriangulatingstroker_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/qtriangulatingstroker_p.h b/src/gui/opengl/qtriangulatingstroker_p.h
new file mode 100644
index 0000000000..3716ea4e0f
--- /dev/null
+++ b/src/gui/opengl/qtriangulatingstroker_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 QTRIANGULATINGSTROKER_P_H
+#define QTRIANGULATINGSTROKER_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/qtriangulator.cpp b/src/gui/opengl/qtriangulator.cpp
new file mode 100644
index 0000000000..dbbae4d938
--- /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 &center = 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 //
+//============================================================================//
+
+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;
+}
+
+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..192bd9cb19
--- /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 qTriangulate(const qreal *polygon, int count, uint hint = QVectorPath::PolygonHint | QVectorPath::OddEvenFill, const QTransform &matrix = QTransform());
+QTriangleSet 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