summaryrefslogtreecommitdiffstats
path: root/src/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'src/opengl')
-rw-r--r--src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp175
-rw-r--r--src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h169
-rw-r--r--src/opengl/gl2paintengineex/qglcustomshaderstage.cpp138
-rw-r--r--src/opengl/gl2paintengineex/qglcustomshaderstage_p.h93
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager.cpp838
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager_p.h513
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadersource_p.h519
-rw-r--r--src/opengl/gl2paintengineex/qglgradientcache.cpp206
-rw-r--r--src/opengl/gl2paintengineex/qglgradientcache_p.h98
-rw-r--r--src/opengl/gl2paintengineex/qglshadercache_meego_p.h457
-rw-r--r--src/opengl/gl2paintengineex/qglshadercache_p.h98
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp2458
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h332
-rw-r--r--src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp396
-rw-r--r--src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h166
-rw-r--r--src/opengl/gl2paintengineex/qtriangulatingstroker.cpp588
-rw-r--r--src/opengl/gl2paintengineex/qtriangulatingstroker_p.h157
-rw-r--r--src/opengl/gl2paintengineex/qtriangulator.cpp3114
-rw-r--r--src/opengl/gl2paintengineex/qtriangulator_p.h148
-rw-r--r--src/opengl/opengl.pro181
-rw-r--r--src/opengl/opengl.pro.user.2.1pre183
-rw-r--r--src/opengl/qgl.cpp6057
-rw-r--r--src/opengl/qgl.h668
-rw-r--r--src/opengl/qgl_egl.cpp351
-rw-r--r--src/opengl/qgl_egl_p.h69
-rw-r--r--src/opengl/qgl_mac.mm996
-rw-r--r--src/opengl/qgl_p.h903
-rw-r--r--src/opengl/qgl_qpa.cpp394
-rw-r--r--src/opengl/qgl_qws.cpp318
-rw-r--r--src/opengl/qgl_symbian.cpp472
-rw-r--r--src/opengl/qgl_win.cpp1601
-rw-r--r--src/opengl/qgl_wince.cpp636
-rw-r--r--src/opengl/qgl_x11.cpp1908
-rw-r--r--src/opengl/qgl_x11egl.cpp548
-rw-r--r--src/opengl/qglbuffer.cpp576
-rw-r--r--src/opengl/qglbuffer.h132
-rw-r--r--src/opengl/qglcolormap.cpp297
-rw-r--r--src/opengl/qglcolormap.h105
-rw-r--r--src/opengl/qglextensions.cpp430
-rw-r--r--src/opengl/qglextensions_p.h897
-rw-r--r--src/opengl/qglframebufferobject.cpp1398
-rw-r--r--src/opengl/qglframebufferobject.h180
-rw-r--r--src/opengl/qglframebufferobject_p.h161
-rw-r--r--src/opengl/qglfunctions.cpp3705
-rw-r--r--src/opengl/qglfunctions.h2295
-rw-r--r--src/opengl/qglpaintdevice.cpp246
-rw-r--r--src/opengl/qglpaintdevice_p.h115
-rw-r--r--src/opengl/qglpixelbuffer.cpp628
-rw-r--r--src/opengl/qglpixelbuffer.h122
-rw-r--r--src/opengl/qglpixelbuffer_egl.cpp224
-rw-r--r--src/opengl/qglpixelbuffer_mac.mm331
-rw-r--r--src/opengl/qglpixelbuffer_p.h209
-rw-r--r--src/opengl/qglpixelbuffer_stub.cpp84
-rw-r--r--src/opengl/qglpixelbuffer_win.cpp403
-rw-r--r--src/opengl/qglpixelbuffer_x11.cpp290
-rw-r--r--src/opengl/qglpixmapfilter.cpp621
-rw-r--r--src/opengl/qglpixmapfilter_p.h94
-rw-r--r--src/opengl/qglscreen_qws.cpp242
-rw-r--r--src/opengl/qglscreen_qws.h127
-rw-r--r--src/opengl/qglshaderprogram.cpp3354
-rw-r--r--src/opengl/qglshaderprogram.h344
-rw-r--r--src/opengl/qgltexturepool.cpp244
-rw-r--r--src/opengl/qgltexturepool_p.h147
-rw-r--r--src/opengl/qglwindowsurface_qws.cpp133
-rw-r--r--src/opengl/qglwindowsurface_qws_p.h90
-rw-r--r--src/opengl/qgraphicsshadereffect.cpp314
-rw-r--r--src/opengl/qgraphicsshadereffect_p.h94
-rw-r--r--src/opengl/qgraphicssystem_gl.cpp114
-rw-r--r--src/opengl/qgraphicssystem_gl_p.h80
-rw-r--r--src/opengl/qpaintengine_opengl.cpp5680
-rw-r--r--src/opengl/qpaintengine_opengl_p.h157
-rw-r--r--src/opengl/qpixmapdata_gl.cpp829
-rw-r--r--src/opengl/qpixmapdata_gl_p.h247
-rw-r--r--src/opengl/qpixmapdata_poolgl.cpp934
-rw-r--r--src/opengl/qpixmapdata_x11gl_egl.cpp403
-rw-r--r--src/opengl/qpixmapdata_x11gl_p.h98
-rw-r--r--src/opengl/qwindowsurface_gl.cpp1149
-rw-r--r--src/opengl/qwindowsurface_gl_p.h129
-rw-r--r--src/opengl/qwindowsurface_x11gl.cpp213
-rw-r--r--src/opengl/qwindowsurface_x11gl_p.h83
-rw-r--r--src/opengl/util/README-GLSL18
-rw-r--r--src/opengl/util/brush_painter.glsl7
-rw-r--r--src/opengl/util/brushes.conf6
-rw-r--r--src/opengl/util/composition_mode_colorburn.glsl13
-rw-r--r--src/opengl/util/composition_mode_colordodge.glsl15
-rw-r--r--src/opengl/util/composition_mode_darken.glsl9
-rw-r--r--src/opengl/util/composition_mode_difference.glsl9
-rw-r--r--src/opengl/util/composition_mode_exclusion.glsl9
-rw-r--r--src/opengl/util/composition_mode_hardlight.glsl14
-rw-r--r--src/opengl/util/composition_mode_lighten.glsl9
-rw-r--r--src/opengl/util/composition_mode_multiply.glsl9
-rw-r--r--src/opengl/util/composition_mode_overlay.glsl13
-rw-r--r--src/opengl/util/composition_mode_screen.glsl6
-rw-r--r--src/opengl/util/composition_mode_softlight.glsl22
-rw-r--r--src/opengl/util/composition_modes.conf12
-rw-r--r--src/opengl/util/conical_brush.glsl27
-rw-r--r--src/opengl/util/ellipse_aa.glsl58
-rw-r--r--src/opengl/util/fast_painter.glsl19
-rw-r--r--src/opengl/util/fragmentprograms_p.h7287
-rw-r--r--src/opengl/util/generator.cpp500
-rw-r--r--src/opengl/util/generator.pro13
-rwxr-xr-xsrc/opengl/util/glsl_to_include.sh73
-rw-r--r--src/opengl/util/linear_brush.glsl22
-rw-r--r--src/opengl/util/masks.conf2
-rw-r--r--src/opengl/util/meego/main.cpp89
-rw-r--r--src/opengl/util/meego/shader-cache-introspector.pro7
-rw-r--r--src/opengl/util/painter.glsl21
-rw-r--r--src/opengl/util/painter_nomask.glsl9
-rw-r--r--src/opengl/util/pattern_brush.glsl23
-rw-r--r--src/opengl/util/radial_brush.glsl28
-rw-r--r--src/opengl/util/simple_porter_duff.glsl16
-rw-r--r--src/opengl/util/solid_brush.glsl4
-rw-r--r--src/opengl/util/texture_brush.glsl21
-rw-r--r--src/opengl/util/trap_exact_aa.glsl58
114 files changed, 63044 insertions, 0 deletions
diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp
new file mode 100644
index 0000000000..4677cc47b4
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray.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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgl2pexvertexarray_p.h"
+
+#include <private/qbezier_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QGL2PEXVertexArray::clear()
+{
+ vertexArray.reset();
+ vertexArrayStops.reset();
+ boundingRectDirty = true;
+}
+
+
+QGLRect QGL2PEXVertexArray::boundingRect() const
+{
+ if (boundingRectDirty)
+ return QGLRect(0.0, 0.0, 0.0, 0.0);
+ else
+ return QGLRect(minX, minY, maxX, maxY);
+}
+
+void QGL2PEXVertexArray::addClosingLine(int index)
+{
+ QPointF point(vertexArray.at(index));
+ if (point != QPointF(vertexArray.last()))
+ vertexArray.add(point);
+}
+
+void QGL2PEXVertexArray::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 QGL2PEXVertexArray::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 QGL2PEXVertexArray::lineToArray(const GLfloat x, const GLfloat y)
+{
+ vertexArray.add(QGLPoint(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/opengl/gl2paintengineex/qgl2pexvertexarray_p.h b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
new file mode 100644
index 0000000000..99cd8d84b0
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray_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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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 QGL2PEXVERTEXARRAY_P_H
+#define QGL2PEXVERTEXARRAY_P_H
+
+#include <QRectF>
+
+#include <private/qdatabuffer_p.h>
+#include <private/qvectorpath_p.h>
+#include <private/qgl_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGLPoint
+{
+public:
+ QGLPoint(GLfloat new_x, GLfloat new_y) :
+ x(new_x), y(new_y) {};
+
+ QGLPoint(const QPointF &p) :
+ x(p.x()), y(p.y()) {};
+
+ QGLPoint(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 QGLRect
+{
+ QGLRect(const QRectF &r)
+ : left(r.left()), top(r.top()), right(r.right()), bottom(r.bottom()) {}
+
+ QGLRect(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 QGL2PEXVertexArray
+{
+public:
+ QGL2PEXVertexArray() :
+ 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 << QGLPoint(left, top)
+ << QGLPoint(right, top)
+ << QGLPoint(right, bottom)
+ << QGLPoint(right, bottom)
+ << QGLPoint(left, bottom)
+ << QGLPoint(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 << QGLPoint(left, top)
+ << QGLPoint(right, top)
+ << QGLPoint(left, bottom)
+ << QGLPoint(right, bottom);
+
+ }
+
+ inline void addVertex(const GLfloat x, const GLfloat y)
+ {
+ vertexArray.add(QGLPoint(x, y));
+ }
+
+ void addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline = true);
+ void clear();
+
+ QGLPoint* data() {return vertexArray.data();}
+ int *stops() const { return vertexArrayStops.data(); }
+ int stopCount() const { return vertexArrayStops.size(); }
+ QGLRect boundingRect() const;
+
+ int vertexCount() const { return vertexArray.size(); }
+
+ void lineToArray(const GLfloat x, const GLfloat y);
+
+private:
+ QDataBuffer<QGLPoint> 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/opengl/gl2paintengineex/qglcustomshaderstage.cpp b/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp
new file mode 100644
index 0000000000..85f1ff257b
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglcustomshaderstage.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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglcustomshaderstage_p.h"
+#include "qglengineshadermanager_p.h"
+#include "qpaintengineex_opengl2_p.h"
+#include <private/qpainter_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGLCustomShaderStagePrivate
+{
+public:
+ QGLCustomShaderStagePrivate() :
+ m_manager(0) {}
+
+ QPointer<QGLEngineShaderManager> m_manager;
+ QByteArray m_source;
+};
+
+
+
+
+QGLCustomShaderStage::QGLCustomShaderStage()
+ : d_ptr(new QGLCustomShaderStagePrivate)
+{
+}
+
+QGLCustomShaderStage::~QGLCustomShaderStage()
+{
+ Q_D(QGLCustomShaderStage);
+ if (d->m_manager) {
+ d->m_manager->removeCustomStage();
+ d->m_manager->sharedShaders->cleanupCustomStage(this);
+ }
+}
+
+void QGLCustomShaderStage::setUniformsDirty()
+{
+ Q_D(QGLCustomShaderStage);
+ if (d->m_manager)
+ d->m_manager->setDirty(); // ### Probably a bit overkill!
+}
+
+bool QGLCustomShaderStage::setOnPainter(QPainter* p)
+{
+ Q_D(QGLCustomShaderStage);
+ if (p->paintEngine()->type() != QPaintEngine::OpenGL2) {
+ qWarning("QGLCustomShaderStage::setOnPainter() - paint engine not OpenGL2");
+ return false;
+ }
+ if (d->m_manager)
+ qWarning("Custom shader is already set on a painter");
+
+ QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx*>(p->paintEngine());
+ d->m_manager = QGL2PaintEngineExPrivate::shaderManagerForEngine(engine);
+ Q_ASSERT(d->m_manager);
+
+ d->m_manager->setCustomStage(this);
+ return true;
+}
+
+void QGLCustomShaderStage::removeFromPainter(QPainter* p)
+{
+ Q_D(QGLCustomShaderStage);
+ if (p->paintEngine()->type() != QPaintEngine::OpenGL2)
+ return;
+
+ QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx*>(p->paintEngine());
+ d->m_manager = QGL2PaintEngineExPrivate::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 QGLCustomShaderStage::source() const
+{
+ Q_D(const QGLCustomShaderStage);
+ return d->m_source;
+}
+
+// Called by the shader manager if another custom shader is attached or
+// the manager is deleted
+void QGLCustomShaderStage::setInactive()
+{
+ Q_D(QGLCustomShaderStage);
+ d->m_manager = 0;
+}
+
+void QGLCustomShaderStage::setSource(const QByteArray& s)
+{
+ Q_D(QGLCustomShaderStage);
+ d->m_source = s;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h
new file mode 100644
index 0000000000..2a7fc5dd09
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglcustomshaderstage_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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGL_CUSTOM_SHADER_STAGE_H
+#define QGL_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 <QGLShaderProgram>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLCustomShaderStagePrivate;
+class Q_OPENGL_EXPORT QGLCustomShaderStage
+{
+ Q_DECLARE_PRIVATE(QGLCustomShaderStage)
+public:
+ QGLCustomShaderStage();
+ virtual ~QGLCustomShaderStage();
+ virtual void setUniforms(QGLShaderProgram*) {}
+
+ void setUniformsDirty();
+
+ bool setOnPainter(QPainter*);
+ void removeFromPainter(QPainter*);
+ QByteArray source() const;
+
+ void setInactive();
+protected:
+ void setSource(const QByteArray&);
+
+private:
+ QGLCustomShaderStagePrivate* d_ptr;
+};
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+
+#endif
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
new file mode 100644
index 0000000000..8068aa8524
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
@@ -0,0 +1,838 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglengineshadermanager_p.h"
+#include "qglengineshadersource_p.h"
+#include "qpaintengineex_opengl2_p.h"
+#include "qglshadercache_p.h"
+
+#if defined(QT_DEBUG)
+#include <QMetaEnum>
+#endif
+
+// #define QT_GL_SHARED_SHADER_DEBUG
+
+QT_BEGIN_NAMESPACE
+
+class QGLShaderStorage
+{
+public:
+ QGLEngineSharedShaders *shadersForThread(const QGLContext *context) {
+ QGLContextGroupResource<QGLEngineSharedShaders> *&shaders = m_storage.localData();
+ if (!shaders)
+ shaders = new QGLContextGroupResource<QGLEngineSharedShaders>();
+ return shaders->value(context);
+ }
+
+private:
+ QThreadStorage<QGLContextGroupResource<QGLEngineSharedShaders> *> m_storage;
+};
+
+Q_GLOBAL_STATIC(QGLShaderStorage, qt_shader_storage);
+
+QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLContext *context)
+{
+ return qt_shader_storage()->shadersForThread(context);
+}
+
+const char* QGLEngineSharedShaders::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
+};
+
+QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
+ : ctxGuard(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] = qglslMainVertexShader;
+ code[MainWithTexCoordsVertexShader] = qglslMainWithTexCoordsVertexShader;
+ code[MainWithTexCoordsAndOpacityVertexShader] = qglslMainWithTexCoordsAndOpacityVertexShader;
+
+ code[UntransformedPositionVertexShader] = qglslUntransformedPositionVertexShader;
+ code[PositionOnlyVertexShader] = qglslPositionOnlyVertexShader;
+ code[ComplexGeometryPositionOnlyVertexShader] = qglslComplexGeometryPositionOnlyVertexShader;
+ code[PositionWithPatternBrushVertexShader] = qglslPositionWithPatternBrushVertexShader;
+ code[PositionWithLinearGradientBrushVertexShader] = qglslPositionWithLinearGradientBrushVertexShader;
+ code[PositionWithConicalGradientBrushVertexShader] = qglslPositionWithConicalGradientBrushVertexShader;
+ code[PositionWithRadialGradientBrushVertexShader] = qglslPositionWithRadialGradientBrushVertexShader;
+ code[PositionWithTextureBrushVertexShader] = qglslPositionWithTextureBrushVertexShader;
+ code[AffinePositionWithPatternBrushVertexShader] = qglslAffinePositionWithPatternBrushVertexShader;
+ code[AffinePositionWithLinearGradientBrushVertexShader] = qglslAffinePositionWithLinearGradientBrushVertexShader;
+ code[AffinePositionWithConicalGradientBrushVertexShader] = qglslAffinePositionWithConicalGradientBrushVertexShader;
+ code[AffinePositionWithRadialGradientBrushVertexShader] = qglslAffinePositionWithRadialGradientBrushVertexShader;
+ code[AffinePositionWithTextureBrushVertexShader] = qglslAffinePositionWithTextureBrushVertexShader;
+
+ code[MainFragmentShader_CMO] = qglslMainFragmentShader_CMO;
+ code[MainFragmentShader_CM] = qglslMainFragmentShader_CM;
+ code[MainFragmentShader_MO] = qglslMainFragmentShader_MO;
+ code[MainFragmentShader_M] = qglslMainFragmentShader_M;
+ code[MainFragmentShader_CO] = qglslMainFragmentShader_CO;
+ code[MainFragmentShader_C] = qglslMainFragmentShader_C;
+ code[MainFragmentShader_O] = qglslMainFragmentShader_O;
+ code[MainFragmentShader] = qglslMainFragmentShader;
+ code[MainFragmentShader_ImageArrays] = qglslMainFragmentShader_ImageArrays;
+
+ code[ImageSrcFragmentShader] = qglslImageSrcFragmentShader;
+ code[ImageSrcWithPatternFragmentShader] = qglslImageSrcWithPatternFragmentShader;
+ code[NonPremultipliedImageSrcFragmentShader] = qglslNonPremultipliedImageSrcFragmentShader;
+ code[CustomImageSrcFragmentShader] = qglslCustomSrcFragmentShader; // Calls "customShader", which must be appended
+ code[SolidBrushSrcFragmentShader] = qglslSolidBrushSrcFragmentShader;
+ code[TextureBrushSrcFragmentShader] = qglslTextureBrushSrcFragmentShader;
+ code[TextureBrushSrcWithPatternFragmentShader] = qglslTextureBrushSrcWithPatternFragmentShader;
+ code[PatternBrushSrcFragmentShader] = qglslPatternBrushSrcFragmentShader;
+ code[LinearGradientBrushSrcFragmentShader] = qglslLinearGradientBrushSrcFragmentShader;
+ code[RadialGradientBrushSrcFragmentShader] = qglslRadialGradientBrushSrcFragmentShader;
+ code[ConicalGradientBrushSrcFragmentShader] = qglslConicalGradientBrushSrcFragmentShader;
+ code[ShockingPinkSrcFragmentShader] = qglslShockingPinkSrcFragmentShader;
+
+ code[NoMaskFragmentShader] = "";
+ code[MaskFragmentShader] = qglslMaskFragmentShader;
+ code[RgbMaskFragmentShaderPass1] = qglslRgbMaskFragmentShaderPass1;
+ code[RgbMaskFragmentShaderPass2] = qglslRgbMaskFragmentShaderPass2;
+ 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;
+ }
+
+ QGLShader* fragShader;
+ QGLShader* 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 QGLShaderProgram(context, 0);
+
+ CachedShader simpleShaderCache(fragSource, vertexSource);
+
+ bool inCache = simpleShaderCache.load(simpleShaderProg, context);
+
+ if (!inCache) {
+ vertexShader = new QGLShader(QGLShader::Vertex, context, 0);
+ shaders.append(vertexShader);
+ if (!vertexShader->compileSourceCode(vertexSource))
+ qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile");
+
+ fragShader = new QGLShader(QGLShader::Fragment, context, 0);
+ 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 QGLShaderProgram(context, 0);
+
+ CachedShader blitShaderCache(fragSource, vertexSource);
+
+ inCache = blitShaderCache.load(blitShaderProg, context);
+
+ if (!inCache) {
+ vertexShader = new QGLShader(QGLShader::Vertex, context, 0);
+ shaders.append(vertexShader);
+ if (!vertexShader->compileSourceCode(vertexSource))
+ qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile");
+
+ fragShader = new QGLShader(QGLShader::Fragment, context, 0);
+ 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(" -> QGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread());
+#endif
+}
+
+QGLEngineSharedShaders::~QGLEngineSharedShaders()
+{
+#ifdef QT_GL_SHARED_SHADER_DEBUG
+ qDebug(" -> ~QGLEngineSharedShaders() %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 QGLEngineSharedShaders::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.
+QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineShaderProg &prog)
+{
+ for (int i = 0; i < cachedPrograms.size(); ++i) {
+ QGLEngineShaderProg *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<QGLEngineShaderProg> 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<QGLShaderProgram> shaderProgram(new QGLShaderProgram(ctxGuard.context(), 0));
+
+ CachedShader shaderCache(fragSource, vertexSource);
+ bool inCache = shaderCache.load(shaderProgram.data(), ctxGuard.context());
+
+ if (!inCache) {
+
+ QScopedPointer<QGLShader> fragShader(new QGLShader(QGLShader::Fragment, ctxGuard.context(), 0));
+ 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<QGLShader> vertexShader(new QGLShader(QGLShader::Vertex, ctxGuard.context(), 0));
+#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 QGLEngineShaderProg(prog));
+ newProg->program = shaderProgram.take();
+
+ newProg->program->link();
+ if (newProg->program->isLinked()) {
+ if (!inCache)
+ shaderCache.store(newProg->program, ctxGuard.context());
+ } 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) {
+ QGLShader *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 != QGLEngineSharedShaders::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 QGLEngineSharedShaders::cleanupCustomStage(QGLCustomShaderStage* stage)
+{
+ // Remove any shader programs which has this as the custom shader src:
+ for (int i = 0; i < cachedPrograms.size(); ++i) {
+ QGLEngineShaderProg *cachedProg = cachedPrograms[i];
+ if (cachedProg->customStageSource == stage->source()) {
+ delete cachedProg;
+ cachedPrograms.removeAt(i);
+ i--;
+ }
+ }
+}
+
+
+QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context)
+ : ctx(context),
+ shaderProgNeedsChanging(true),
+ complexGeometry(false),
+ srcPixelType(Qt::NoBrush),
+ opacityMode(NoOpacity),
+ maskType(NoMask),
+ compositionMode(QPainter::CompositionMode_SourceOver),
+ customSrcStage(0),
+ currentShaderProg(0)
+{
+ sharedShaders = QGLEngineSharedShaders::shadersForContext(context);
+}
+
+QGLEngineShaderManager::~QGLEngineShaderManager()
+{
+ //###
+ removeCustomStage();
+}
+
+GLuint QGLEngineShaderManager::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",
+ "invertedTextureSize",
+ "brushTransform",
+ "brushTexture",
+ "matrix"
+ };
+
+ if (uniformLocations.at(id) == GLuint(-1))
+ uniformLocations[id] = currentShaderProg->program->uniformLocation(uniformNames[id]);
+
+ return uniformLocations.at(id);
+}
+
+
+void QGLEngineShaderManager::optimiseForBrushTransform(QTransform::TransformationType transformType)
+{
+ Q_UNUSED(transformType); // Currently ignored
+}
+
+void QGLEngineShaderManager::setDirty()
+{
+ shaderProgNeedsChanging = true;
+}
+
+void QGLEngineShaderManager::setSrcPixelType(Qt::BrushStyle style)
+{
+ Q_ASSERT(style != Qt::NoBrush);
+ if (srcPixelType == PixelSrcType(style))
+ return;
+
+ srcPixelType = style;
+ shaderProgNeedsChanging = true; //###
+}
+
+void QGLEngineShaderManager::setSrcPixelType(PixelSrcType type)
+{
+ if (srcPixelType == type)
+ return;
+
+ srcPixelType = type;
+ shaderProgNeedsChanging = true; //###
+}
+
+void QGLEngineShaderManager::setOpacityMode(OpacityMode mode)
+{
+ if (opacityMode == mode)
+ return;
+
+ opacityMode = mode;
+ shaderProgNeedsChanging = true; //###
+}
+
+void QGLEngineShaderManager::setMaskType(MaskType type)
+{
+ if (maskType == type)
+ return;
+
+ maskType = type;
+ shaderProgNeedsChanging = true; //###
+}
+
+void QGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode)
+{
+ if (compositionMode == mode)
+ return;
+
+ compositionMode = mode;
+ shaderProgNeedsChanging = true; //###
+}
+
+void QGLEngineShaderManager::setCustomStage(QGLCustomShaderStage* stage)
+{
+ if (customSrcStage)
+ removeCustomStage();
+ customSrcStage = stage;
+ shaderProgNeedsChanging = true;
+}
+
+void QGLEngineShaderManager::removeCustomStage()
+{
+ if (customSrcStage)
+ customSrcStage->setInactive();
+ customSrcStage = 0;
+ shaderProgNeedsChanging = true;
+}
+
+QGLShaderProgram* QGLEngineShaderManager::currentProgram()
+{
+ if (currentShaderProg)
+ return currentShaderProg->program;
+ else
+ return sharedShaders->simpleProgram();
+}
+
+void QGLEngineShaderManager::useSimpleProgram()
+{
+ sharedShaders->simpleProgram()->bind();
+ QGLContextPrivate* ctx_d = ctx->d_func();
+ ctx_d->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
+ ctx_d->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false);
+ ctx_d->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false);
+ shaderProgNeedsChanging = true;
+}
+
+void QGLEngineShaderManager::useBlitProgram()
+{
+ sharedShaders->blitProgram()->bind();
+ QGLContextPrivate* ctx_d = ctx->d_func();
+ ctx_d->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
+ ctx_d->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, true);
+ ctx_d->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false);
+ shaderProgNeedsChanging = true;
+}
+
+QGLShaderProgram* QGLEngineShaderManager::simpleProgram()
+{
+ return sharedShaders->simpleProgram();
+}
+
+QGLShaderProgram* QGLEngineShaderManager::blitProgram()
+{
+ return sharedShaders->blitProgram();
+}
+
+
+
+// Select & use the correct shader program using the current state.
+// Returns true if program needed changing.
+bool QGLEngineShaderManager::useCorrectShaderProg()
+{
+ if (!shaderProgNeedsChanging)
+ return false;
+
+ bool useCustomSrc = customSrcStage != 0;
+ if (useCustomSrc && srcPixelType != QGLEngineShaderManager::ImageSrc && srcPixelType != Qt::TexturePattern) {
+ useCustomSrc = false;
+ qWarning("QGLEngineShaderManager - Ignoring custom shader stage for non image src");
+ }
+
+ QGLEngineShaderProg 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 = QGLEngineSharedShaders::InvalidSnippetName;
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::InvalidSnippetName;
+ bool isAffine = brushTransform.isAffine();
+ if ( (srcPixelType >= Qt::Dense1Pattern) && (srcPixelType <= Qt::DiagCrossPattern) ) {
+ if (isAffine)
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader;
+ else
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionWithPatternBrushVertexShader;
+
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::PatternBrushSrcFragmentShader;
+ }
+ else switch (srcPixelType) {
+ default:
+ case Qt::NoBrush:
+ qFatal("QGLEngineShaderManager::useCorrectShaderProg() - Qt::NoBrush style is set");
+ break;
+ case QGLEngineShaderManager::ImageSrc:
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ImageSrcFragmentShader;
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader;
+ texCoords = true;
+ break;
+ case QGLEngineShaderManager::NonPremultipliedImageSrc:
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader;
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader;
+ texCoords = true;
+ break;
+ case QGLEngineShaderManager::PatternSrc:
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ImageSrcWithPatternFragmentShader;
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader;
+ texCoords = true;
+ break;
+ case QGLEngineShaderManager::TextureSrcWithPattern:
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader;
+ requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
+ : QGLEngineSharedShaders::PositionWithTextureBrushVertexShader;
+ break;
+ case Qt::SolidPattern:
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::SolidBrushSrcFragmentShader;
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader;
+ break;
+ case Qt::LinearGradientPattern:
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader;
+ requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader
+ : QGLEngineSharedShaders::PositionWithLinearGradientBrushVertexShader;
+ break;
+ case Qt::ConicalGradientPattern:
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader;
+ requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader
+ : QGLEngineSharedShaders::PositionWithConicalGradientBrushVertexShader;
+ break;
+ case Qt::RadialGradientPattern:
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader;
+ requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader
+ : QGLEngineSharedShaders::PositionWithRadialGradientBrushVertexShader;
+ break;
+ case Qt::TexturePattern:
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::TextureBrushSrcFragmentShader;
+ requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
+ : QGLEngineSharedShaders::PositionWithTextureBrushVertexShader;
+ break;
+ };
+
+ if (useCustomSrc) {
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::CustomImageSrcFragmentShader;
+ requiredProgram.customStageSource = customSrcStage->source();
+ }
+
+ const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus;
+ const bool hasMask = maskType != QGLEngineShaderManager::NoMask;
+
+ // Choose fragment shader main function:
+ if (opacityMode == AttributeOpacity) {
+ Q_ASSERT(!hasCompose && !hasMask);
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_ImageArrays;
+ } else {
+ bool useGlobalOpacity = (opacityMode == UniformOpacity);
+ if (hasCompose && hasMask && useGlobalOpacity)
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CMO;
+ if (hasCompose && hasMask && !useGlobalOpacity)
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CM;
+ if (!hasCompose && hasMask && useGlobalOpacity)
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_MO;
+ if (!hasCompose && hasMask && !useGlobalOpacity)
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_M;
+ if (hasCompose && !hasMask && useGlobalOpacity)
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CO;
+ if (hasCompose && !hasMask && !useGlobalOpacity)
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_C;
+ if (!hasCompose && !hasMask && useGlobalOpacity)
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_O;
+ if (!hasCompose && !hasMask && !useGlobalOpacity)
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader;
+ }
+
+ if (hasMask) {
+ if (maskType == PixelMask) {
+ requiredProgram.maskFragShader = QGLEngineSharedShaders::MaskFragmentShader;
+ texCoords = true;
+ } else if (maskType == SubPixelMaskPass1) {
+ requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskFragmentShaderPass1;
+ texCoords = true;
+ } else if (maskType == SubPixelMaskPass2) {
+ requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskFragmentShaderPass2;
+ texCoords = true;
+ } else if (maskType == SubPixelWithGammaMask) {
+ requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskWithGammaFragmentShader;
+ texCoords = true;
+ } else {
+ qCritical("QGLEngineShaderManager::useCorrectShaderProg() - Unknown mask type");
+ }
+ } else {
+ requiredProgram.maskFragShader = QGLEngineSharedShaders::NoMaskFragmentShader;
+ }
+
+ if (hasCompose) {
+ switch (compositionMode) {
+ case QPainter::CompositionMode_Multiply:
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::MultiplyCompositionModeFragmentShader;
+ break;
+ case QPainter::CompositionMode_Screen:
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::ScreenCompositionModeFragmentShader;
+ break;
+ case QPainter::CompositionMode_Overlay:
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::OverlayCompositionModeFragmentShader;
+ break;
+ case QPainter::CompositionMode_Darken:
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::DarkenCompositionModeFragmentShader;
+ break;
+ case QPainter::CompositionMode_Lighten:
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::LightenCompositionModeFragmentShader;
+ break;
+ case QPainter::CompositionMode_ColorDodge:
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader;
+ break;
+ case QPainter::CompositionMode_ColorBurn:
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader;
+ break;
+ case QPainter::CompositionMode_HardLight:
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::HardLightCompositionModeFragmentShader;
+ break;
+ case QPainter::CompositionMode_SoftLight:
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::SoftLightCompositionModeFragmentShader;
+ break;
+ case QPainter::CompositionMode_Difference:
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::DifferenceCompositionModeFragmentShader;
+ break;
+ case QPainter::CompositionMode_Exclusion:
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::ExclusionCompositionModeFragmentShader;
+ break;
+ default:
+ qWarning("QGLEngineShaderManager::useCorrectShaderProg() - Unsupported composition mode");
+ }
+ } else {
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::NoCompositionModeFragmentShader;
+ }
+
+ // Choose vertex shader main function
+ if (opacityMode == AttributeOpacity) {
+ Q_ASSERT(texCoords);
+ requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader;
+ } else if (texCoords) {
+ requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainWithTexCoordsVertexShader;
+ } else {
+ requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainVertexShader;
+ }
+ requiredProgram.useTextureCoords = texCoords;
+ requiredProgram.useOpacityAttribute = (opacityMode == AttributeOpacity);
+ if (complexGeometry && srcPixelType == Qt::SolidPattern) {
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::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)
+ QGLContextPrivate* ctx_d = ctx->d_func();
+ ctx_d->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
+ ctx_d->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, currentShaderProg && currentShaderProg->useTextureCoords);
+ ctx_d->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, currentShaderProg && currentShaderProg->useOpacityAttribute);
+
+ shaderProgNeedsChanging = false;
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
new file mode 100644
index 0000000000..7cc9dc3bd4
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
@@ -0,0 +1,513 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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, QGLShader & QGLShaderProgram will make partial
+ shaders work by concatenating the source in each QGLShader and compiling
+ it as a single shader. This is abstracted nicely by QGLShaderProgram 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:
+ qglslMainVertexShader
+ qglslMainWithTexCoordsVertexShader
+
+ And the the following position vertex shaders:
+ qglslPositionOnlyVertexShader
+ qglslPositionWithTextureBrushVertexShader
+ qglslPositionWithPatternBrushVertexShader
+ qglslPositionWithLinearGradientBrushVertexShader
+ qglslPositionWithRadialGradientBrushVertexShader
+ qglslPositionWithConicalGradientBrushVertexShader
+ qglslAffinePositionWithTextureBrushVertexShader
+ qglslAffinePositionWithPatternBrushVertexShader
+ qglslAffinePositionWithLinearGradientBrushVertexShader
+ qglslAffinePositionWithRadialGradientBrushVertexShader
+ qglslAffinePositionWithConicalGradientBrushVertexShader
+
+ 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()":
+ qglslImageSrcFragShader
+ qglslImageSrcWithPatternFragShader
+ qglslNonPremultipliedImageSrcFragShader
+ qglslSolidBrushSrcFragShader
+ qglslTextureBrushSrcFragShader
+ qglslTextureBrushWithPatternFragShader
+ qglslPatternBrushSrcFragShader
+ qglslLinearGradientBrushSrcFragShader
+ qglslRadialGradientBrushSrcFragShader
+ qglslConicalGradientBrushSrcFragShader
+ NOTE: It is assumed the colour returned by srcPixel() is pre-multiplied
+
+ Masks are implementations of "qcolorp vec4 applyMask(qcolorp vec4 src)":
+ qglslMaskFragmentShader
+ qglslRgbMaskFragmentShaderPass1
+ qglslRgbMaskFragmentShaderPass2
+ qglslRgbMaskWithGammaFragmentShader
+
+ Composition modes are "qcolorp vec4 compose(qcolorp vec4 src)":
+ qglslColorBurnCompositionModeFragmentShader
+ qglslColorDodgeCompositionModeFragmentShader
+ qglslDarkenCompositionModeFragmentShader
+ qglslDifferenceCompositionModeFragmentShader
+ qglslExclusionCompositionModeFragmentShader
+ qglslHardLightCompositionModeFragmentShader
+ qglslLightenCompositionModeFragmentShader
+ qglslMultiplyCompositionModeFragmentShader
+ qglslOverlayCompositionModeFragmentShader
+ qglslScreenCompositionModeFragmentShader
+ qglslSoftLightCompositionModeFragmentShader
+
+
+ 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:
+ qglslMainFragmentShader_CMO
+ qglslMainFragmentShader_CM
+ qglslMainFragmentShader_MO
+ qglslMainFragmentShader_M
+ qglslMainFragmentShader_CO
+ qglslMainFragmentShader_C
+ qglslMainFragmentShader_O
+ qglslMainFragmentShader
+
+ 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
+ (QGLCustomShaderStage). 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 QGLENGINE_SHADER_MANAGER_H
+#define QGLENGINE_SHADER_MANAGER_H
+
+#include <QGLShader>
+#include <QGLShaderProgram>
+#include <QPainter>
+#include <private/qgl_p.h>
+#include <private/qglcustomshaderstage_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+
+/*
+struct QGLEngineCachedShaderProg
+{
+ QGLEngineCachedShaderProg(QGLEngineShaderManager::ShaderName vertexMain,
+ QGLEngineShaderManager::ShaderName vertexPosition,
+ QGLEngineShaderManager::ShaderName fragMain,
+ QGLEngineShaderManager::ShaderName pixelSrc,
+ QGLEngineShaderManager::ShaderName mask,
+ QGLEngineShaderManager::ShaderName composition);
+
+ int cacheKey;
+ QGLShaderProgram* 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 QGLEngineShaderProg;
+
+class Q_OPENGL_EXPORT QGLEngineSharedShaders
+{
+ 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;
+*/
+
+ QGLEngineSharedShaders(const QGLContext *context);
+ ~QGLEngineSharedShaders();
+
+ QGLShaderProgram *simpleProgram() { return simpleShaderProg; }
+ QGLShaderProgram *blitProgram() { return blitShaderProg; }
+ // Compile the program if it's not already in the cache, return the item in the cache.
+ QGLEngineShaderProg *findProgramInCache(const QGLEngineShaderProg &prog);
+ // Compile the custom shader if it's not already in the cache, return the item in the cache.
+
+ static QGLEngineSharedShaders *shadersForContext(const QGLContext *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(QGLCustomShaderStage* stage);
+
+private:
+ QGLSharedResourceGuard ctxGuard;
+ QGLShaderProgram *blitShaderProg;
+ QGLShaderProgram *simpleShaderProg;
+ QList<QGLEngineShaderProg*> cachedPrograms;
+ QList<QGLShader *> shaders;
+
+ static const char* qShaderSnippets[TotalSnippetCount];
+};
+
+
+class QGLEngineShaderProg
+{
+public:
+ QGLEngineShaderProg() : program(0) {}
+
+ ~QGLEngineShaderProg() {
+ if (program)
+ delete program;
+ }
+
+ QGLEngineSharedShaders::SnippetName mainVertexShader;
+ QGLEngineSharedShaders::SnippetName positionVertexShader;
+ QGLEngineSharedShaders::SnippetName mainFragShader;
+ QGLEngineSharedShaders::SnippetName srcPixelFragShader;
+ QGLEngineSharedShaders::SnippetName maskFragShader;
+ QGLEngineSharedShaders::SnippetName compositionFragShader;
+
+ QByteArray customStageSource; //TODO: Decent cache key for custom stages
+ QGLShaderProgram* program;
+
+ QVector<uint> uniformLocations;
+
+ bool useTextureCoords;
+ bool useOpacityAttribute;
+ bool usePmvMatrixAttribute;
+
+ bool operator==(const QGLEngineShaderProg& 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_OPENGL_EXPORT QGLEngineShaderManager : public QObject
+{
+ Q_OBJECT
+public:
+ QGLEngineShaderManager(QGLContext* context);
+ ~QGLEngineShaderManager();
+
+ 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,
+ 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(QGLCustomShaderStage* 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;
+ }
+
+ QGLShaderProgram* currentProgram(); // Returns pointer to the shader the manager has chosen
+ QGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers
+ QGLShaderProgram* blitProgram(); // Used to blit a texture into the framebuffer
+
+ QGLEngineSharedShaders* sharedShaders;
+
+private:
+ QGLContext* 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;
+ QGLCustomShaderStage* customSrcStage;
+
+ QGLEngineShaderProg* currentShaderProg;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //QGLENGINE_SHADER_MANAGER_H
diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
new file mode 100644
index 0000000000..fc8b9ef4db
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
@@ -0,0 +1,519 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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 QGL_ENGINE_SHADER_SOURCE_H
+#define QGL_ENGINE_SHADER_SOURCE_H
+
+#include "qglengineshadermanager_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+
+static const char* const qglslMainVertexShader = "\n\
+ void setPosition(); \n\
+ void main(void) \n\
+ { \n\
+ setPosition(); \n\
+ }\n";
+
+static const char* const qglslMainWithTexCoordsVertexShader = "\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 qglslMainWithTexCoordsAndOpacityVertexShader = "\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 qglslPositionOnlyVertexShader = "\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 qglslComplexGeometryPositionOnlyVertexShader = "\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 qglslUntransformedPositionVertexShader = "\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 qglslPositionWithPatternBrushVertexShader = "\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 qglslAffinePositionWithPatternBrushVertexShader
+ = qglslPositionWithPatternBrushVertexShader;
+
+static const char* const qglslPatternBrushSrcFragmentShader = "\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 qglslPositionWithLinearGradientBrushVertexShader = "\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 qglslAffinePositionWithLinearGradientBrushVertexShader
+ = qglslPositionWithLinearGradientBrushVertexShader;
+
+static const char* const qglslLinearGradientBrushSrcFragmentShader = "\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 qglslPositionWithConicalGradientBrushVertexShader = "\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 qglslAffinePositionWithConicalGradientBrushVertexShader
+ = qglslPositionWithConicalGradientBrushVertexShader;
+
+static const char* const qglslConicalGradientBrushSrcFragmentShader = "\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 qglslPositionWithRadialGradientBrushVertexShader = "\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\
+ 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 = 2.0 * dot(A, fmp); \n\
+ }\n";
+
+static const char* const qglslAffinePositionWithRadialGradientBrushVertexShader
+ = qglslPositionWithRadialGradientBrushVertexShader;
+
+static const char* const qglslRadialGradientBrushSrcFragmentShader = "\n\
+ uniform sampler2D brushTexture; \n\
+ uniform highp float fmp2_m_radius2; \n\
+ uniform highp float inverse_2_fmp2_m_radius2; \n\
+ varying highp float b; \n\
+ varying highp vec2 A; \n\
+ lowp vec4 srcPixel() \n\
+ { \n\
+ highp float c = -dot(A, A); \n\
+ highp vec2 val = vec2((-b + sqrt(b*b - 4.0*fmp2_m_radius2*c)) * inverse_2_fmp2_m_radius2, 0.5); \n\
+ return texture2D(brushTexture, val); \n\
+ }\n";
+
+
+// Texture Brush
+static const char* const qglslPositionWithTextureBrushVertexShader = "\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 qglslAffinePositionWithTextureBrushVertexShader
+ = qglslPositionWithTextureBrushVertexShader;
+
+#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 qglslTextureBrushSrcFragmentShader = "\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 qglslTextureBrushSrcFragmentShader = "\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 qglslTextureBrushSrcWithPatternFragmentShader = "\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 qglslSolidBrushSrcFragmentShader = "\n\
+ uniform lowp vec4 fragmentColor; \n\
+ lowp vec4 srcPixel() \n\
+ { \n\
+ return fragmentColor; \n\
+ }\n";
+
+static const char* const qglslImageSrcFragmentShader = "\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 qglslCustomSrcFragmentShader = "\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 qglslImageSrcWithPatternFragmentShader = "\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 qglslNonPremultipliedImageSrcFragmentShader = "\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 qglslShockingPinkSrcFragmentShader = "\n\
+ lowp vec4 srcPixel() \n\
+ { \n\
+ return vec4(0.98, 0.06, 0.75, 1.0); \n\
+ }\n";
+
+static const char* const qglslMainFragmentShader_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 qglslMainFragmentShader_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 qglslMainFragmentShader_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 qglslMainFragmentShader_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 qglslMainFragmentShader_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 qglslMainFragmentShader_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 qglslMainFragmentShader_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 qglslMainFragmentShader_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 qglslMainFragmentShader = "\n\
+ lowp vec4 srcPixel(); \n\
+ void main() \n\
+ { \n\
+ gl_FragColor = srcPixel(); \n\
+ }\n";
+
+static const char* const qglslMaskFragmentShader = "\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 qglslRgbMaskFragmentShaderPass1 = "\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 qglslRgbMaskFragmentShaderPass2 = "\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/opengl/gl2paintengineex/qglgradientcache.cpp b/src/opengl/gl2paintengineex/qglgradientcache.cpp
new file mode 100644
index 0000000000..075f7d15ea
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglgradientcache.cpp
@@ -0,0 +1,206 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglgradientcache_p.h"
+#include <private/qdrawhelper_p.h>
+#include <private/qgl_p.h>
+#include <QtCore/qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGL2GradientCacheWrapper
+{
+public:
+ QGL2GradientCache *cacheForContext(const QGLContext *context) {
+ QMutexLocker lock(&m_mutex);
+ return m_resource.value(context);
+ }
+
+private:
+ QGLContextGroupResource<QGL2GradientCache> m_resource;
+ QMutex m_mutex;
+};
+
+Q_GLOBAL_STATIC(QGL2GradientCacheWrapper, qt_gradient_caches)
+
+QGL2GradientCache *QGL2GradientCache::cacheForContext(const QGLContext *context)
+{
+ return qt_gradient_caches()->cacheForContext(context);
+}
+
+void QGL2GradientCache::cleanCache()
+{
+ QMutexLocker lock(&m_mutex);
+ QGLGradientColorTableHash::const_iterator it = cache.constBegin();
+ for (; it != cache.constEnd(); ++it) {
+ const CacheInfo &cache_info = it.value();
+ glDeleteTextures(1, &cache_info.texId);
+ }
+ cache.clear();
+}
+
+GLuint QGL2GradientCache::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();
+
+ QGLGradientColorTableHash::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 QGL2GradientCache::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:
+ QGLGradientColorTableHash::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 QGL2GradientCache::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/opengl/gl2paintengineex/qglgradientcache_p.h b/src/opengl/gl2paintengineex/qglgradientcache_p.h
new file mode 100644
index 0000000000..d1d56a178a
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglgradientcache_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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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 <QtOpenGL/QtOpenGL>
+#include <private/qgl_p.h>
+#include <QtCore/qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGL2GradientCache
+{
+ 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> QGLGradientColorTableHash;
+
+public:
+ static QGL2GradientCache *cacheForContext(const QGLContext *context);
+
+ QGL2GradientCache(const QGLContext *) {}
+ ~QGL2GradientCache() { cleanCache(); }
+
+ GLuint getBuffer(const QGradient &gradient, qreal opacity);
+ inline int paletteSize() const { return 1024; }
+
+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();
+
+ QGLGradientColorTableHash cache;
+ QMutex m_mutex;
+};
+
+QT_END_NAMESPACE
+
diff --git a/src/opengl/gl2paintengineex/qglshadercache_meego_p.h b/src/opengl/gl2paintengineex/qglshadercache_meego_p.h
new file mode 100644
index 0000000000..d20c731fc3
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglshadercache_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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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 QGLSHADERCACHE_MEEGO_P_H
+#define QGLSHADERCACHE_MEEGO_P_H
+
+#include <QtCore/qglobal.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(QGLShaderProgram *program, const QGLContext *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 QGLShaderProgram *shader, const QGLContext *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(OpenGL)
+
+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(QGLShaderProgram *program, const QGLContext *ctx)
+ {
+ if (cacheIndex() == -1)
+ return false;
+ return qt_cached_shader(program, ctx, cacheIdx);
+ }
+
+ bool store(QGLShaderProgram *program, const QGLContext *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/opengl/gl2paintengineex/qglshadercache_p.h b/src/opengl/gl2paintengineex/qglshadercache_p.h
new file mode 100644
index 0000000000..6e496abf7c
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglshadercache_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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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 QGLSHADERCACHE_P_H
+#define QGLSHADERCACHE_P_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(QT_MEEGO_EXPERIMENTAL_SHADERCACHE) && defined(QT_OPENGL_ES_2)
+# include "qglshadercache_meego_p.h"
+#else
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLShaderProgram;
+class QGLContext;
+
+class CachedShader
+{
+public:
+ inline CachedShader(const QByteArray &, const QByteArray &)
+ {}
+
+ inline bool isCached()
+ {
+ return false;
+ }
+
+ inline bool load(QGLShaderProgram *, const QGLContext *)
+ {
+ return false;
+ }
+
+ inline bool store(QGLShaderProgram *, const QGLContext *)
+ {
+ return false;
+ }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+#endif
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
new file mode 100644
index 0000000000..18c684ff1b
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -0,0 +1,2458 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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 "qglgradientcache_p.h"
+#include "qpaintengineex_opengl2_p.h"
+
+#include <string.h> //for memcpy
+#include <qmath.h>
+
+#include <private/qgl_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/qpixmapdata_gl_p.h>
+#include <private/qdatabuffer_p.h>
+#include <private/qstatictext_p.h>
+#include <private/qtriangulator_p.h>
+
+#include "qglengineshadermanager_p.h"
+#include "qgl2pexvertexarray_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
+
+#if !defined(QT_MAX_CACHED_GLYPH_SIZE)
+# define QT_MAX_CACHED_GLYPH_SIZE 64
+#endif
+
+Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert);
+
+////////////////////////////////// Private Methods //////////////////////////////////////////
+
+QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate()
+{
+ 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) {
+ glDeleteBuffers(1, &elementIndicesVBOId);
+ elementIndicesVBOId = 0;
+ }
+}
+
+void QGL2PaintEngineExPrivate::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 QGL2PaintEngineExPrivate::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(QGLEngineShaderManager::TextureSrcWithPattern);
+ } else {
+ shaderManager->setSrcPixelType(newStyle);
+ }
+ shaderManager->optimiseForBrushTransform(currentBrush.transform().type());
+}
+
+
+void QGL2PaintEngineExPrivate::useSimpleShader()
+{
+ shaderManager->useSimpleProgram();
+
+ if (matrixDirty)
+ updateMatrix();
+}
+
+void QGL2PaintEngineExPrivate::updateBrushTexture()
+{
+ Q_Q(QGL2PaintEngineEx);
+// qDebug("QGL2PaintEngineExPrivate::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, QGLContext::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 = QGL2GradientCache::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_IBM, 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);
+ QGLTexture *tex = ctx->d_func()->bindTexture(currentBrushPixmap, GL_TEXTURE_2D, GL_RGBA,
+ QGLContext::InternalBindOption |
+ QGLContext::CanFlipNativePixmapBindOption);
+ updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
+ textureInvertedY = tex->options & QGLContext::InvertedYBindOption ? -1 : 1;
+ }
+ brushTextureDirty = false;
+}
+
+
+void QGL2PaintEngineExPrivate::updateBrushUniforms()
+{
+// qDebug("QGL2PaintEngineExPrivate::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(QGLEngineShaderManager::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(QGLEngineShaderManager::PatternColor), col);
+
+ QVector2D halfViewportSize(width*0.5, height*0.5);
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::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(QGLEngineShaderManager::LinearData), linearData);
+
+ QVector2D halfViewportSize(width*0.5, height*0.5);
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::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(QGLEngineShaderManager::Angle), angle);
+
+ QVector2D halfViewportSize(width*0.5, height*0.5);
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::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->radius();
+ translationPoint = realFocal;
+
+ QPointF fmp = realCenter - realFocal;
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Fmp), fmp);
+
+ GLfloat fmp2_m_radius2 = -fmp.x() * fmp.x() - fmp.y() * fmp.y() + realRadius*realRadius;
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Fmp2MRadius2), fmp2_m_radius2);
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Inverse2Fmp2MRadius2),
+ GLfloat(1.0 / (2.0*fmp2_m_radius2)));
+
+ QVector2D halfViewportSize(width*0.5, height*0.5);
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::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(QGLEngineShaderManager::PatternColor), col);
+ }
+
+ QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 / texPixmap.height());
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::InvertedTextureSize), invertedTextureSize);
+
+ QVector2D halfViewportSize(width*0.5, height*0.5);
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
+ }
+ else
+ qWarning("QGL2PaintEngineEx: 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(QGLEngineShaderManager::BrushTransform), inv_matrix);
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTexture), QT_BRUSH_TEXTURE_UNIT);
+ }
+ brushUniformsDirty = false;
+}
+
+
+// This assumes the shader manager has already setup the correct shader program
+void QGL2PaintEngineExPrivate::updateMatrix()
+{
+// qDebug("QGL2PaintEngineExPrivate::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.
+ glVertexAttrib3fv(QT_PMV_MATRIX_1_ATTR, pmvMatrix[0]);
+ glVertexAttrib3fv(QT_PMV_MATRIX_2_ATTR, pmvMatrix[1]);
+ glVertexAttrib3fv(QT_PMV_MATRIX_3_ATTR, pmvMatrix[2]);
+
+ dasher.setInvScale(inverseScale);
+ stroker.setInvScale(inverseScale);
+}
+
+
+void QGL2PaintEngineExPrivate::updateCompositionMode()
+{
+ // NOTE: The entire paint engine works on pre-multiplied data - which is why some of these
+ // composition modes look odd.
+// qDebug() << "QGL2PaintEngineExPrivate::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 QGLRect &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 QGL2PaintEngineExPrivate::drawTexture(const QGLRect& dest, const QGLRect& src, const QSize &textureSize, bool opaque, bool pattern)
+{
+ // Setup for texture drawing
+ currentBrush = noBrush;
+ shaderManager->setSrcPixelType(pattern ? QGLEngineShaderManager::PatternSrc : QGLEngineShaderManager::ImageSrc);
+
+ if (snapToPixelGrid) {
+ snapToPixelGrid = false;
+ matrixDirty = true;
+ }
+
+ if (prepareForDraw(opaque))
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
+
+ if (pattern) {
+ QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity);
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
+ }
+
+ GLfloat dx = 1.0 / textureSize.width();
+ GLfloat dy = 1.0 / textureSize.height();
+
+ QGLRect 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 QGL2PaintEngineEx::beginNativePainting()
+{
+ Q_D(QGL2PaintEngineEx);
+ ensureActive();
+ d->transferMode(BrushDrawingMode);
+
+ d->nativePaintingActive = true;
+
+ QGLContext *ctx = d->ctx;
+ glUseProgram(0);
+
+ // Disable all the vertex attribute arrays:
+ for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i)
+ glDisableVertexAttribArray(i);
+
+#ifndef QT_OPENGL_ES_2
+ const QGLFormat &fmt = d->device->format();
+ if (fmt.majorVersion() < 3 || (fmt.majorVersion() == 3 && fmt.minorVersion() < 1)
+ || fmt.profile() == QGLFormat::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]);
+ }
+#else
+ Q_UNUSED(ctx);
+#endif
+
+ d->lastTextureUsed = GLuint(-1);
+ d->dirtyStencilRegion = QRect(0, 0, d->width, d->height);
+ d->resetGLState();
+
+ d->shaderManager->setDirty();
+
+ d->needsSync = true;
+}
+
+void QGL2PaintEngineExPrivate::resetGLState()
+{
+ glDisable(GL_BLEND);
+ glActiveTexture(GL_TEXTURE0);
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glDepthMask(true);
+ glDepthFunc(GL_LESS);
+ glClearDepth(1);
+ glStencilMask(0xff);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ glStencilFunc(GL_ALWAYS, 0, 0xff);
+ ctx->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false);
+ ctx->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, false);
+ ctx->d_func()->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 };
+ glVertexAttrib4fv(3, color);
+#endif
+}
+
+void QGL2PaintEngineEx::endNativePainting()
+{
+ Q_D(QGL2PaintEngineEx);
+ d->needsSync = true;
+ d->nativePaintingActive = false;
+}
+
+void QGL2PaintEngineEx::invalidateState()
+{
+ Q_D(QGL2PaintEngineEx);
+ d->needsSync = true;
+}
+
+bool QGL2PaintEngineEx::isNativePaintingActive() const {
+ Q_D(const QGL2PaintEngineEx);
+ return d->nativePaintingActive;
+}
+
+void QGL2PaintEngineExPrivate::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(QGLEngineShaderManager::NoMask);
+
+ mode = newMode;
+}
+
+struct QGL2PEVectorPathCache
+{
+#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 QGL2PaintEngineExPrivate::cleanupVectorPath(QPaintEngineEx *engine, void *data)
+{
+ QGL2PEVectorPathCache *c = (QGL2PEVectorPathCache *) data;
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+ Q_ASSERT(engine->type() == QPaintEngine::OpenGL2);
+ static_cast<QGL2PaintEngineEx *>(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 QGL2PaintEngineExPrivate::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) {
+ QGLRect 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);
+ QGL2PEVectorPathCache *cache;
+
+ bool updateCache = false;
+
+ if (data) {
+ cache = (QGL2PEVectorPathCache *) 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 QGL2PEVectorPathCache;
+ 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);
+ QGL2PEVectorPathCache *cache;
+
+ bool updateCache = false;
+
+ if (data) {
+ cache = (QGL2PEVectorPathCache *) 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 QGL2PEVectorPathCache;
+ 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 (QGLExtensions::glExtensions() & QGLExtensions::ElementIndexUint)
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint32) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW);
+ else
+ 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));
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
+#else
+ cache->vertices = (float *) qMalloc(sizeof(float) * polys.vertices.size());
+ if (QGLExtensions::glExtensions() & QGLExtensions::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 (QGLExtensions::glExtensions() & QGLExtensions::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 (QGLExtensions::glExtensions() & QGLExtensions::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().stencil()) {
+ // 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 (QGLExtensions::glExtensions() & QGLExtensions::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 QGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data,
+ int count,
+ int *stops,
+ int stopCount,
+ const QGLRect &bounds,
+ StencilFillMode mode)
+{
+ Q_ASSERT(count || stops);
+
+// qDebug("QGL2PaintEngineExPrivate::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
+ glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP);
+ // Dec. for back-facing "holes"
+ 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 QGL2PaintEngineExPrivate::resetClipIfNeeded()
+{
+ if (maxClip != (GL_STENCIL_HIGH_BIT - 1))
+ return;
+
+ Q_Q(QGL2PaintEngineEx);
+
+ 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));
+ QGLRect 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 QGL2PaintEngineExPrivate::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);
+ }
+
+ QGLEngineShaderManager::OpacityMode opacityMode;
+ if (mode == ImageArrayDrawingMode) {
+ opacityMode = QGLEngineShaderManager::AttributeOpacity;
+ } else {
+ opacityMode = stateHasOpacity ? QGLEngineShaderManager::UniformOpacity
+ : QGLEngineShaderManager::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 = QGLEngineShaderManager::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 == QGLEngineShaderManager::UniformOpacity && opacityUniformDirty) {
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::GlobalOpacity), (GLfloat)q->state()->opacity);
+ opacityUniformDirty = false;
+ }
+
+ if (matrixUniformDirty && shaderManager->hasComplexGeometry()) {
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Matrix),
+ pmvMatrix);
+ matrixUniformDirty = false;
+ }
+
+ return changed;
+}
+
+void QGL2PaintEngineExPrivate::composite(const QGLRect& 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 QGL2PaintEngineExPrivate::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 //////////////////////////////////////////
+
+QGL2PaintEngineEx::QGL2PaintEngineEx()
+ : QPaintEngineEx(*(new QGL2PaintEngineExPrivate(this)))
+{
+}
+
+QGL2PaintEngineEx::~QGL2PaintEngineEx()
+{
+}
+
+void QGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush)
+{
+ Q_D(QGL2PaintEngineEx);
+
+ 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 QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
+{
+ Q_D(QGL2PaintEngineEx);
+
+ 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 QGL2PaintEngineExPrivate::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, QGL2PaintEngineExPrivate::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 QGL2PaintEngineEx::penChanged() { }
+void QGL2PaintEngineEx::brushChanged() { }
+void QGL2PaintEngineEx::brushOriginChanged() { }
+
+void QGL2PaintEngineEx::opacityChanged()
+{
+// qDebug("QGL2PaintEngineEx::opacityChanged()");
+ Q_D(QGL2PaintEngineEx);
+ state()->opacityChanged = true;
+
+ Q_ASSERT(d->shaderManager);
+ d->brushUniformsDirty = true;
+ d->opacityUniformDirty = true;
+}
+
+void QGL2PaintEngineEx::compositionModeChanged()
+{
+// qDebug("QGL2PaintEngineEx::compositionModeChanged()");
+ Q_D(QGL2PaintEngineEx);
+ state()->compositionModeChanged = true;
+ d->compositionModeDirty = true;
+}
+
+void QGL2PaintEngineEx::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(QGL2PaintEngineEx);
+ d->lastTextureUsed = GLuint(-1);
+ d->brushTextureDirty = true;
+// qDebug("QGL2PaintEngineEx::renderHintsChanged() not implemented!");
+}
+
+void QGL2PaintEngineEx::transformChanged()
+{
+ Q_D(QGL2PaintEngineEx);
+ 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 QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, const QRectF & src)
+{
+ Q_D(QGL2PaintEngineEx);
+ QGLContext *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);
+
+ QGLContext::BindOptions bindOptions = QGLContext::InternalBindOption|QGLContext::CanFlipNativePixmapBindOption;
+#ifdef QGL_USE_TEXTURE_POOL
+ bindOptions |= QGLContext::TemporarilyCachedBindOption;
+#endif
+
+ glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
+ QGLTexture *texture =
+ ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, bindOptions);
+
+ GLfloat top = texture->options & QGLContext::InvertedYBindOption ? (pixmap.height() - src.top()) : src.top();
+ GLfloat bottom = texture->options & QGLContext::InvertedYBindOption ? (pixmap.height() - src.bottom()) : src.bottom();
+ QGLRect 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);
+
+ if (texture->options&QGLContext::TemporarilyCachedBindOption) {
+ // pixmap was temporarily cached as a QImage texture by pooling system
+ // and should be destroyed immediately
+ QGLTextureCache::instance()->remove(ctx, texture->id);
+ }
+}
+
+void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src,
+ Qt::ImageConversionFlags)
+{
+ Q_D(QGL2PaintEngineEx);
+ QGLContext *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);
+
+ QGLContext::BindOptions bindOptions = QGLContext::InternalBindOption;
+#ifdef QGL_USE_TEXTURE_POOL
+ bindOptions |= QGLContext::TemporarilyCachedBindOption;
+#endif
+
+ QGLTexture *texture = 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());
+
+ if (texture->options&QGLContext::TemporarilyCachedBindOption) {
+ // image was temporarily cached by texture pooling system
+ // and should be destroyed immediately
+ QGLTextureCache::instance()->remove(ctx, texture->id);
+ }
+}
+
+void QGL2PaintEngineEx::drawStaticTextItem(QStaticTextItem *textItem)
+{
+ Q_D(QGL2PaintEngineEx);
+
+ ensureActive();
+
+ QFontEngineGlyphCache::Type glyphType = textItem->fontEngine()->glyphFormat >= 0
+ ? QFontEngineGlyphCache::Type(textItem->fontEngine()->glyphFormat)
+ : d->glyphCacheType;
+ if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) {
+ if (d->device->alphaRequested() || state()->matrix.type() > QTransform::TxTranslate
+ || (state()->composition_mode != QPainter::CompositionMode_Source
+ && state()->composition_mode != QPainter::CompositionMode_SourceOver))
+ {
+ glyphType = QFontEngineGlyphCache::Raster_A8;
+ }
+ }
+
+ d->drawCachedGlyphs(glyphType, textItem);
+}
+
+bool QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src)
+{
+ Q_D(QGL2PaintEngineEx);
+ if (!d->shaderManager)
+ return false;
+
+ ensureActive();
+ d->transferMode(ImageDrawingMode);
+
+#ifndef QT_OPENGL_ES_2
+ QGLContext *ctx = d->ctx;
+#endif
+ glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+
+ QGLRect 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 QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QGL2PaintEngineEx);
+
+ 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 (pixelSize * pixelSize * qAbs(det) >= QT_MAX_CACHED_GLYPH_SIZE * QT_MAX_CACHED_GLYPH_SIZE ||
+ 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;
+ QGL2PEXVertexArray vertexCoordinateArray;
+ QGL2PEXVertexArray textureCoordinateArray;
+ QFontEngineGlyphCache::Type glyphType;
+ int cacheSerialNumber;
+ };
+
+}
+
+// #define QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO
+
+void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyphType,
+ QStaticTextItem *staticTextItem)
+{
+ Q_Q(QGL2PaintEngineEx);
+
+ QOpenGL2PaintEngineState *s = q->state();
+
+ void *cacheKey = const_cast<QGLContext *>(QGLContextPrivate::contextGroup(ctx)->context());
+ bool recreateVertexArrays = false;
+
+ QGLTextureGlyphCache *cache =
+ (QGLTextureGlyphCache *) staticTextItem->fontEngine()->glyphCache(cacheKey, glyphType, QTransform());
+ if (!cache || cache->cacheType() != glyphType || cache->context() == 0) {
+ cache = new QGLTextureGlyphCache(ctx, glyphType, QTransform());
+ staticTextItem->fontEngine()->setGlyphCache(cacheKey, cache);
+ cache->insert(ctx, 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
+ QGL2PEXVertexArray *vertexCoordinates = &vertexCoordinateArray;
+ QGL2PEXVertexArray *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 (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(QGLEngineShaderManager::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(QGLEngineShaderManager::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(QGLEngineShaderManager::PixelMask);
+ prepareForDraw(false); // Text always causes src pixels to be transparent
+ }
+ //### TODO: Gamma correction
+
+ QGLTextureGlyphCache::FilterMode filterMode = (s->matrix.type() > QTransform::TxTranslate)?QGLTextureGlyphCache::Linear:QGLTextureGlyphCache::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 == QGLTextureGlyphCache::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);
+ }
+ }
+
+#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
+}
+
+void QGL2PaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap,
+ QPainter::PixmapFragmentHints hints)
+{
+ Q_D(QGL2PaintEngineEx);
+ // 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 QGL2PaintEngineExPrivate::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;
+ QGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c);
+ QGLPoint 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);
+
+ QGLRect 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);
+ QGLTexture *texture = ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA,
+ QGLContext::InternalBindOption
+ | QGLContext::CanFlipNativePixmapBindOption);
+
+ if (texture->options & QGLContext::InvertedYBindOption) {
+ // Flip texture y-coordinate.
+ QGLPoint *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 ? QGLEngineShaderManager::PatternSrc
+ : QGLEngineShaderManager::ImageSrc);
+ if (prepareForDraw(isOpaque))
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
+
+ if (isBitmap) {
+ QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity);
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
+ }
+
+ glDrawArrays(GL_TRIANGLES, 0, 6 * fragmentCount);
+}
+
+bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
+{
+ Q_D(QGL2PaintEngineEx);
+
+// qDebug("QGL2PaintEngineEx::begin()");
+ if (pdev->devType() == QInternal::OpenGL)
+ d->device = static_cast<QGLPaintDevice*>(pdev);
+ else
+ d->device = QGLPaintDevice::getDevice(pdev);
+
+ if (!d->device)
+ return false;
+
+ d->ctx = d->device->context();
+ d->ctx->d_ptr->active_engine = this;
+
+ 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();
+
+#if !defined(QT_OPENGL_ES_2)
+ bool success = qt_resolve_version_2_0_functions(d->ctx)
+ && qt_resolve_buffer_extensions(d->ctx);
+ Q_ASSERT(success);
+ Q_UNUSED(success);
+#endif
+
+ d->shaderManager = new QGLEngineShaderManager(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)
+#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().sampleBuffers();
+#else
+ d->multisamplingAlwaysEnabled = false;
+#endif
+
+ return true;
+}
+
+bool QGL2PaintEngineEx::end()
+{
+ Q_D(QGL2PaintEngineEx);
+ QGLContext *ctx = d->ctx;
+
+ glUseProgram(0);
+ d->transferMode(BrushDrawingMode);
+ d->device->endPaint();
+
+#if defined(Q_WS_X11)
+ // On some (probably all) drivers, deleting an X pixmap which has been bound to a texture
+ // before calling glFinish/swapBuffers renders garbage. Presumably this is because X deletes
+ // the pixmap behind the driver's back before it's had a chance to use it. To fix this, we
+ // reference all QPixmaps which have been bound to stop them being deleted and only deref
+ // them here, after swapBuffers, where they can be safely deleted.
+ ctx->d_func()->boundPixmaps.clear();
+#endif
+ d->ctx->d_ptr->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 QGL2PaintEngineEx::ensureActive()
+{
+ Q_D(QGL2PaintEngineEx);
+ QGLContext *ctx = d->ctx;
+
+ if (isActive() && ctx->d_ptr->active_engine != this) {
+ ctx->d_ptr->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->ctx->d_func()->syncGlState();
+ for (int i = 0; i < 3; ++i)
+ d->vertexAttribPointers[i] = (GLfloat*)-1; // Assume the pointers are clobbered
+ setState(state());
+ }
+}
+
+void QGL2PaintEngineExPrivate::updateClipScissorTest()
+{
+ Q_Q(QGL2PaintEngineEx);
+ 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 QGL2PaintEngineExPrivate::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 QGL2PaintEngineEx::clipEnabledChanged()
+{
+ Q_D(QGL2PaintEngineEx);
+
+ state()->clipChanged = true;
+
+ if (painter()->hasClipping())
+ d->regenerateClip();
+ else
+ d->systemStateChanged();
+}
+
+void QGL2PaintEngineExPrivate::clearClip(uint value)
+{
+ dirtyStencilRegion -= currentScissorBounds;
+
+ glStencilMask(0xff);
+ glClearStencil(value);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ glStencilMask(0x0);
+
+ q->state()->needsClipBufferClear = false;
+}
+
+void QGL2PaintEngineExPrivate::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 QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+// qDebug("QGL2PaintEngineEx::clip()");
+ Q_D(QGL2PaintEngineEx);
+
+ 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;
+ case Qt::UniteClip: {
+ d->resetClipIfNeeded();
+ ++d->maxClip;
+ if (state()->rectangleClip.isValid()) {
+ QPainterPath path;
+ path.addRect(state()->rectangleClip);
+
+ // flush the existing clip rectangle to the depth buffer
+ d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(path)), d->maxClip);
+ }
+
+ state()->clipTestEnabled = false;
+#ifndef QT_GL_NO_SCISSOR_TEST
+ QRect oldRectangleClip = state()->rectangleClip;
+
+ state()->rectangleClip = state()->rectangleClip.united(pathRect);
+ d->updateClipScissorTest();
+
+ QRegion extendRegion = QRegion(state()->rectangleClip) - oldRectangleClip;
+
+ if (!extendRegion.isEmpty()) {
+ QPainterPath extendPath;
+ extendPath.addRegion(extendRegion);
+
+ // first clear the depth buffer in the extended region
+ d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(extendPath)), 0);
+ }
+#endif
+ // now write the clip path
+ d->writeClip(path, d->maxClip);
+ state()->canRestoreClip = false;
+ state()->currentClip = d->maxClip;
+ state()->clipTestEnabled = true;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void QGL2PaintEngineExPrivate::regenerateClip()
+{
+ systemStateChanged();
+ replayClipOperations();
+}
+
+void QGL2PaintEngineExPrivate::systemStateChanged()
+{
+ Q_Q(QGL2PaintEngineEx);
+
+ q->state()->clipChanged = true;
+
+ if (systemClip.isEmpty()) {
+ useSystemClip = false;
+ } else {
+ if (q->paintDevice()->devType() == QInternal::Widget && currentClipWidget) {
+ QWidgetPrivate *widgetPrivate = qt_widget_private(currentClipWidget->window());
+ useSystemClip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter;
+ } 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 QGL2PaintEngineEx::setState(QPainterState *new_state)
+{
+ // qDebug("QGL2PaintEngineEx::setState()");
+
+ Q_D(QGL2PaintEngineEx);
+
+ 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 *QGL2PaintEngineEx::createState(QPainterState *orig) const
+{
+ if (orig)
+ const_cast<QGL2PaintEngineEx *>(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()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
new file mode 100644
index 0000000000..f7ec5f5c2f
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
@@ -0,0 +1,332 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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/qglengineshadermanager_p.h>
+#include <private/qgl2pexvertexarray_p.h>
+#include <private/qglpaintdevice_p.h>
+#include <private/qglpixmapfilter_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qdatabuffer_p.h>
+#include <private/qtriangulatingstroker_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 QGL2PaintEngineExPrivate;
+
+
+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_OPENGL_EXPORT QGL2PaintEngineEx : public QPaintEngineEx
+{
+ Q_DECLARE_PRIVATE(QGL2PaintEngineEx)
+public:
+ QGL2PaintEngineEx();
+ ~QGL2PaintEngineEx();
+
+ 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();
+
+ QPixmapFilter *pixmapFilter(int type, const QPixmapFilter *prototype);
+
+ void setRenderTextActive(bool);
+
+ bool isNativePaintingActive() const;
+private:
+ Q_DISABLE_COPY(QGL2PaintEngineEx)
+};
+
+class QGL2PaintEngineExPrivate : public QPaintEngineExPrivate
+{
+ Q_DECLARE_PUBLIC(QGL2PaintEngineEx)
+public:
+ enum StencilFillMode {
+ OddEvenFillMode,
+ WindingFillMode,
+ TriStripStrokeFillMode
+ };
+
+ QGL2PaintEngineExPrivate(QGL2PaintEngineEx *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)
+ { }
+
+ ~QGL2PaintEngineExPrivate();
+
+ 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 QGLRect& dest, const QGLRect& 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(QGL2PEXVertexArray &vertexArray, GLenum primitive) {
+ drawVertexArrays((const float *) vertexArray.data(), vertexArray.stops(), vertexArray.stopCount(), primitive);
+ }
+
+ // Composites the bounding rect onto dest buffer:
+ void composite(const QGLRect& boundingRect);
+
+ // Calls drawVertexArrays to render into stencil buffer:
+ void fillStencilWithVertexArray(const float *data, int count, int *stops, int stopCount, const QGLRect &bounds, StencilFillMode mode);
+ void fillStencilWithVertexArray(QGL2PEXVertexArray& 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 QGLEngineShaderManager::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();
+
+
+ static QGLEngineShaderManager* shaderManagerForEngine(QGL2PaintEngineEx *engine) { return engine->d_func()->shaderManager; }
+ static QGL2PaintEngineExPrivate *getData(QGL2PaintEngineEx *engine) { return engine->d_func(); }
+ static void cleanupVectorPath(QPaintEngineEx *engine, void *data);
+
+
+ QGL2PaintEngineEx* q;
+ QGLEngineShaderManager* shaderManager;
+ QGLPaintDevice* device;
+ int width, height;
+ QGLContext *ctx;
+ EngineMode mode;
+ QFontEngineGlyphCache::Type glyphCacheType;
+
+ // 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;
+
+ QGL2PEXVertexArray vertexCoordinateArray;
+ QGL2PEXVertexArray 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;
+
+ QScopedPointer<QPixmapFilter> convolutionFilter;
+ QScopedPointer<QPixmapFilter> colorizeFilter;
+ QScopedPointer<QPixmapFilter> blurFilter;
+ QScopedPointer<QPixmapFilter> dropShadowFilter;
+
+ QSet<QVectorPath::CacheEntry *> pathCaches;
+ QVector<GLuint> unusedVBOSToClean;
+ QVector<GLuint> unusedIBOSToClean;
+
+ const GLfloat *vertexAttribPointers[3];
+};
+
+
+void QGL2PaintEngineExPrivate::setVertexAttributePointer(unsigned int arrayIndex, const GLfloat *pointer)
+{
+ Q_ASSERT(arrayIndex < 3);
+ if (pointer == vertexAttribPointers[arrayIndex])
+ return;
+
+ vertexAttribPointers[arrayIndex] = pointer;
+ if (arrayIndex == QT_OPACITY_ATTR)
+ glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, pointer);
+ else
+ glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, pointer);
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp
new file mode 100644
index 0000000000..c867d60a72
--- /dev/null
+++ b/src/opengl/gl2paintengineex/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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextureglyphcache_gl_p.h"
+#include "qpaintengineex_opengl2_p.h"
+#include "private/qglengineshadersource_p.h"
+
+#if defined QT_OPENGL_ES_2 && !defined(QT_NO_EGL)
+#include "private/qeglcontext_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_WIN
+extern Q_GUI_EXPORT bool qt_cleartype_enabled;
+#endif
+
+QBasicAtomicInt qgltextureglyphcache_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+QGLTextureGlyphCache::QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix)
+ : QImageTextureGlyphCache(type, matrix), QGLContextGroupResourceBase()
+ , ctx(0)
+ , pex(0)
+ , m_blitProgram(0)
+ , m_filterMode(Nearest)
+ , m_serialNumber(qgltextureglyphcache_serial_number.fetchAndAddRelaxed(1))
+{
+#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG
+ qDebug(" -> QGLTextureGlyphCache() %p for context %p.", this, ctx);
+#endif
+ setContext(context);
+
+ 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;
+}
+
+QGLTextureGlyphCache::~QGLTextureGlyphCache()
+{
+#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG
+ qDebug(" -> ~QGLTextureGlyphCache() %p.", this);
+#endif
+ delete m_blitProgram;
+}
+
+void QGLTextureGlyphCache::setContext(const QGLContext *context)
+{
+ ctx = context;
+ m_h = 0;
+}
+
+void QGLTextureGlyphCache::createTextureData(int width, int height)
+{
+ if (ctx == 0) {
+ qWarning("QGLTextureGlyphCache::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_ptr->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;
+
+ QGLGlyphTexture *glyphTexture = m_textureResource.value(ctx);
+ glGenTextures(1, &glyphTexture->m_texture);
+ glBindTexture(GL_TEXTURE_2D, glyphTexture->m_texture);
+
+ glyphTexture->m_width = width;
+ glyphTexture->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 QGLTextureGlyphCache::resizeTextureData(int width, int height)
+{
+ if (ctx == 0) {
+ qWarning("QGLTextureGlyphCache::resizeTextureData: Called with no context");
+ return;
+ }
+ QGLGlyphTexture *glyphTexture = m_textureResource.value(ctx);
+
+ int oldWidth = glyphTexture->m_width;
+ int oldHeight = glyphTexture->m_height;
+
+ // Make the lower glyph texture size 16 x 16.
+ if (width < 16)
+ width = 16;
+ if (height < 16)
+ height = 16;
+
+ GLuint oldTexture = glyphTexture->m_texture;
+ createTextureData(width, height);
+
+ if (ctx->d_ptr->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
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, glyphTexture->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);
+ glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, tmp_texture, 0);
+
+ 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);
+
+ QGLShaderProgram *blitProgram = 0;
+ if (pex == 0) {
+ if (m_blitProgram == 0) {
+ m_blitProgram = new QGLShaderProgram(ctx);
+
+ {
+ QString source;
+ source.append(QLatin1String(qglslMainWithTexCoordsVertexShader));
+ source.append(QLatin1String(qglslUntransformedPositionVertexShader));
+
+ QGLShader *vertexShader = new QGLShader(QGLShader::Vertex, m_blitProgram);
+ vertexShader->compileSourceCode(source);
+
+ m_blitProgram->addShader(vertexShader);
+ }
+
+ {
+ QString source;
+ source.append(QLatin1String(qglslMainFragmentShader));
+ source.append(QLatin1String(qglslImageSrcFragmentShader));
+
+ QGLShader *fragmentShader = new QGLShader(QGLShader::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();
+ }
+
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_vertexCoordinateArray);
+ 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, glyphTexture->m_texture);
+
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
+
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_RENDERBUFFER_EXT, 0);
+ glDeleteTextures(1, &tmp_texture);
+ glDeleteTextures(1, &oldTexture);
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
+
+ if (pex != 0) {
+ glViewport(0, 0, pex->width, pex->height);
+ pex->updateClipScissorTest();
+ }
+}
+
+void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition)
+{
+ if (ctx == 0) {
+ qWarning("QGLTextureGlyphCache::fillTexture: Called with no context");
+ return;
+ }
+
+ QGLGlyphTexture *glyphTexture = m_textureResource.value(ctx);
+ if (ctx->d_ptr->workaround_brokenFBOReadBack) {
+ QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition);
+
+ glBindTexture(GL_TEXTURE_2D, glyphTexture->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, glyphTexture->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 (!ctx->d_ptr->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)));
+ ctx->d_ptr->workaround_brokenAlphaTexSubImage = versionString.indexOf("NVIDIA") >= 0;
+ ctx->d_ptr->workaround_brokenAlphaTexSubImage_init = true;
+ }
+
+ if (ctx->d_ptr->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 {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_ALPHA, GL_UNSIGNED_BYTE, mask.bits());
+ }
+ }
+}
+
+int QGLTextureGlyphCache::glyphPadding() const
+{
+ return 1;
+}
+
+int QGLTextureGlyphCache::maxTextureWidth() const
+{
+ if (ctx == 0)
+ return QImageTextureGlyphCache::maxTextureWidth();
+ else
+ return ctx->d_ptr->maxTextureSize();
+}
+
+int QGLTextureGlyphCache::maxTextureHeight() const
+{
+ if (ctx == 0)
+ return QImageTextureGlyphCache::maxTextureHeight();
+
+ if (ctx->d_ptr->workaround_brokenTexSubImage)
+ return qMin(1024, ctx->d_ptr->maxTextureSize());
+ else
+ return ctx->d_ptr->maxTextureSize();
+}
+
+void QGLTextureGlyphCache::clear()
+{
+ if (ctx != 0) {
+ m_textureResource.cleanup(ctx);
+
+ m_w = 0;
+ m_h = 0;
+ m_cx = 0;
+ m_cy = 0;
+ m_currentRowHeight = 0;
+ coords.clear();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h
new file mode 100644
index 0000000000..133289e81c
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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/qgl_p.h>
+#include <qglshaderprogram.h>
+
+// #define QT_GL_TEXTURE_GLYPH_CACHE_DEBUG
+
+QT_BEGIN_NAMESPACE
+
+class QGL2PaintEngineExPrivate;
+
+struct QGLGlyphTexture
+{
+ QGLGlyphTexture(const QGLContext *ctx)
+ : m_width(0)
+ , m_height(0)
+ {
+ if (ctx && !ctx->d_ptr->workaround_brokenFBOReadBack)
+ glGenFramebuffers(1, &m_fbo);
+
+#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG
+ qDebug(" -> QGLGlyphTexture() %p for context %p.", this, ctx);
+#endif
+ }
+
+ ~QGLGlyphTexture() {
+ const QGLContext *ctx = QGLContext::currentContext();
+#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG
+ qDebug("~QGLGlyphTexture() %p for context %p.", this, ctx);
+#endif
+ // At this point, the context group is made current, so it's safe to
+ // release resources without a makeCurrent() call
+ if (ctx) {
+ if (!ctx->d_ptr->workaround_brokenFBOReadBack)
+ glDeleteFramebuffers(1, &m_fbo);
+ if (m_width || m_height)
+ glDeleteTextures(1, &m_texture);
+ }
+ }
+
+ GLuint m_texture;
+ GLuint m_fbo;
+ int m_width;
+ int m_height;
+};
+
+class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QImageTextureGlyphCache, public QGLContextGroupResourceBase
+{
+public:
+ QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix);
+ ~QGLTextureGlyphCache();
+
+ 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 {
+ QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this);
+ QGLGlyphTexture *glyphTexture = that->m_textureResource.value(ctx);
+ return glyphTexture ? glyphTexture->m_texture : 0;
+ }
+
+ inline int width() const {
+ QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this);
+ QGLGlyphTexture *glyphTexture = that->m_textureResource.value(ctx);
+ return glyphTexture ? glyphTexture->m_width : 0;
+ }
+ inline int height() const {
+ QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this);
+ QGLGlyphTexture *glyphTexture = that->m_textureResource.value(ctx);
+ return glyphTexture ? glyphTexture->m_height : 0;
+ }
+
+ inline void setPaintEnginePrivate(QGL2PaintEngineExPrivate *p) { pex = p; }
+
+ void setContext(const QGLContext *context);
+ inline const QGLContext *context() const { return ctx; }
+
+ 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();
+
+ void freeResource(void *) { ctx = 0; }
+
+private:
+ QGLContextGroupResource<QGLGlyphTexture> m_textureResource;
+
+ const QGLContext *ctx;
+ QGL2PaintEngineExPrivate *pex;
+ QGLShaderProgram *m_blitProgram;
+ FilterMode m_filterMode;
+
+ GLfloat m_vertexCoordinateArray[8];
+ GLfloat m_textureCoordinateArray[8];
+
+ int m_serialNumber;
+};
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp b/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp
new file mode 100644
index 0000000000..f4e170d928
--- /dev/null
+++ b/src/opengl/gl2paintengineex/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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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/opengl/gl2paintengineex/qtriangulatingstroker_p.h b/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h
new file mode 100644
index 0000000000..adb5d5264d
--- /dev/null
+++ b/src/opengl/gl2paintengineex/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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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/opengl/gl2paintengineex/qtriangulator.cpp b/src/opengl/gl2paintengineex/qtriangulator.cpp
new file mode 100644
index 0000000000..94024f3d74
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qtriangulator.cpp
@@ -0,0 +1,3114 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtriangulator_p.h"
+
+#include <QtGui/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 <QtDebug>
+
+#include <math.h>
+
+#include <private/qgl_p.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;
+}
+
+//============================================================================//
+// QRBTree //
+//============================================================================//
+
+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;
+}
+
+//============================================================================//
+// 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()
+{
+ 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 (QGLExtensions::glExtensions() & QGLExtensions::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 {
+ 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 (QGLExtensions::glExtensions() & QGLExtensions::ElementIndexUint) {
+ QTriangulator<quint32> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint32> vertexSet = triangulator.triangulate();
+ triangleSet.vertices = vertexSet.vertices;
+ triangleSet.indices.setDataUint(vertexSet.indices);
+ } else {
+ 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 (QGLExtensions::glExtensions() & QGLExtensions::ElementIndexUint) {
+ QTriangulator<quint32> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint32> vertexSet = triangulator.triangulate();
+ triangleSet.vertices = vertexSet.vertices;
+ triangleSet.indices.setDataUint(vertexSet.indices);
+ } else {
+ 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 (QGLExtensions::glExtensions() & QGLExtensions::ElementIndexUint) {
+ QTriangulator<quint32> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint32> vertexSet = triangulator.polyline();
+ polyLineSet.vertices = vertexSet.vertices;
+ polyLineSet.indices.setDataUint(vertexSet.indices);
+ } else {
+ QTriangulator<quint16> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint16> vertexSet = triangulator.triangulate();
+ polyLineSet.vertices = vertexSet.vertices;
+ polyLineSet.indices.setDataUshort(vertexSet.indices);
+ }
+ return polyLineSet;
+}
+
+QPolylineSet qPolyline(const QPainterPath &path,
+ const QTransform &matrix, qreal lod)
+{
+ QPolylineSet polyLineSet;
+ if (QGLExtensions::glExtensions() & QGLExtensions::ElementIndexUint) {
+ QTriangulator<quint32> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint32> vertexSet = triangulator.polyline();
+ polyLineSet.vertices = vertexSet.vertices;
+ polyLineSet.indices.setDataUint(vertexSet.indices);
+ } else {
+ QTriangulator<quint16> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint16> vertexSet = triangulator.triangulate();
+ polyLineSet.vertices = vertexSet.vertices;
+ polyLineSet.indices.setDataUshort(vertexSet.indices);
+ }
+ return polyLineSet;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/gl2paintengineex/qtriangulator_p.h b/src/opengl/gl2paintengineex/qtriangulator_p.h
new file mode 100644
index 0000000000..a205b923e9
--- /dev/null
+++ b/src/opengl/gl2paintengineex/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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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 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 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 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;
+};
+
+// 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 qTriangulate(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1);
+QPolylineSet qPolyline(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1);
+QPolylineSet qPolyline(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro
new file mode 100644
index 0000000000..e7c1c446de
--- /dev/null
+++ b/src/opengl/opengl.pro
@@ -0,0 +1,181 @@
+TARGET = QtOpenGL
+QPRO_PWD = $$PWD
+QT = core gui
+DEFINES += QT_BUILD_OPENGL_LIB
+DEFINES += QT_NO_USING_NAMESPACE
+win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x63000000
+solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2
+irix-cc*:QMAKE_CXXFLAGS += -no_prelink -ptused
+
+unix|win32-g++*:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui
+
+include(../qbase.pri)
+
+!win32:!embedded:!mac:!symbian:!qpa:CONFIG += x11
+contains(QT_CONFIG, opengl):CONFIG += opengl
+contains(QT_CONFIG, opengles1):CONFIG += opengles1
+contains(QT_CONFIG, opengles2):CONFIG += opengles2
+contains(QT_CONFIG, egl):CONFIG += egl
+
+HEADERS += qgl.h \
+ qgl_p.h \
+ qglcolormap.h \
+ qglfunctions.h \
+ qglpixelbuffer.h \
+ qglpixelbuffer_p.h \
+ qglframebufferobject.h \
+ qglframebufferobject_p.h \
+ qglextensions_p.h \
+ qglpaintdevice_p.h \
+ qglbuffer.h \
+
+
+SOURCES += qgl.cpp \
+ qglcolormap.cpp \
+ qglfunctions.cpp \
+ qglpixelbuffer.cpp \
+ qglframebufferobject.cpp \
+ qglextensions.cpp \
+ qglpaintdevice.cpp \
+ qglbuffer.cpp \
+
+
+!contains(QT_CONFIG, opengles2) {
+ HEADERS += qpaintengine_opengl_p.h
+ SOURCES += qpaintengine_opengl.cpp
+}
+
+!contains(QT_CONFIG, opengles1) {
+ HEADERS += qglshaderprogram.h \
+ qglpixmapfilter_p.h \
+ qgraphicsshadereffect_p.h \
+ qgraphicssystem_gl_p.h \
+ qwindowsurface_gl_p.h \
+ qpixmapdata_gl_p.h \
+ gl2paintengineex/qglgradientcache_p.h \
+ gl2paintengineex/qglengineshadermanager_p.h \
+ gl2paintengineex/qgl2pexvertexarray_p.h \
+ gl2paintengineex/qpaintengineex_opengl2_p.h \
+ gl2paintengineex/qglengineshadersource_p.h \
+ gl2paintengineex/qglcustomshaderstage_p.h \
+ gl2paintengineex/qtriangulatingstroker_p.h \
+ gl2paintengineex/qtriangulator_p.h \
+ gl2paintengineex/qtextureglyphcache_gl_p.h \
+ gl2paintengineex/qglshadercache_p.h \
+ gl2paintengineex/qglshadercache_meego_p.h
+
+ SOURCES += qglshaderprogram.cpp \
+ qglpixmapfilter.cpp \
+ qgraphicsshadereffect.cpp \
+ qgraphicssystem_gl.cpp \
+ qwindowsurface_gl.cpp \
+ qpixmapdata_gl.cpp \
+ gl2paintengineex/qglgradientcache.cpp \
+ gl2paintengineex/qglengineshadermanager.cpp \
+ gl2paintengineex/qgl2pexvertexarray.cpp \
+ gl2paintengineex/qpaintengineex_opengl2.cpp \
+ gl2paintengineex/qglcustomshaderstage.cpp \
+ gl2paintengineex/qtriangulatingstroker.cpp \
+ gl2paintengineex/qtriangulator.cpp \
+ gl2paintengineex/qtextureglyphcache_gl.cpp
+
+}
+
+qpa {
+ SOURCES += qgl_qpa.cpp \
+ qglpixelbuffer_stub.cpp
+}
+
+x11 {
+ contains(QT_CONFIG, egl) {
+ SOURCES += qgl_x11egl.cpp \
+ qglpixelbuffer_egl.cpp \
+ qgl_egl.cpp \
+ qpixmapdata_x11gl_egl.cpp \
+ qwindowsurface_x11gl.cpp
+
+ HEADERS += qgl_egl_p.h \
+ qpixmapdata_x11gl_p.h \
+ qwindowsurface_x11gl_p.h
+
+ } else {
+ SOURCES += qgl_x11.cpp \
+ qglpixelbuffer_x11.cpp
+ }
+
+ contains(QT_CONFIG, fontconfig) {
+ contains(QT_CONFIG, system-freetype) {
+ embedded:CONFIG += opentype
+ # pull in the proper freetype2 include directory
+ include($$QT_SOURCE_TREE/config.tests/unix/freetype/freetype.pri)
+ LIBS_PRIVATE += -lfreetype
+ } else {
+ ### Note: how does this compile with a non-system freetype?
+ # This probably does not compile
+ }
+ } else {
+ DEFINES *= QT_NO_FREETYPE
+ }
+
+ LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD
+}
+
+mac:!qpa {
+ OBJECTIVE_SOURCES += qgl_mac.mm \
+ qglpixelbuffer_mac.mm
+ LIBS_PRIVATE += -framework AppKit -framework Carbon
+}
+win32:!wince*: {
+ DEFINES += QT_NO_EGL
+ SOURCES += qgl_win.cpp \
+ qglpixelbuffer_win.cpp
+}
+wince*: {
+ SOURCES += qgl_wince.cpp \
+ qglpixelbuffer_egl.cpp \
+ qgl_egl.cpp
+
+ HEADERS += qgl_egl_p.h
+}
+
+embedded {
+ SOURCES += qgl_qws.cpp \
+ qglpixelbuffer_egl.cpp \
+ qglscreen_qws.cpp \
+ qglwindowsurface_qws.cpp \
+ qgl_egl.cpp
+
+ HEADERS += qglscreen_qws.h \
+ qglwindowsurface_qws_p.h \
+ qgl_egl_p.h
+
+ contains(QT_CONFIG, fontconfig) {
+ include($$QT_SOURCE_TREE/config.tests/unix/freetype/freetype.pri)
+ } else {
+ DEFINES *= QT_NO_FREETYPE
+ }
+}
+
+symbian {
+ DEFINES += QGL_USE_TEXTURE_POOL QGL_NO_PRESERVED_SWAP
+ SOURCES -= qpixmapdata_gl.cpp
+ SOURCES += qgl_symbian.cpp \
+ qpixmapdata_poolgl.cpp \
+ qglpixelbuffer_egl.cpp \
+ qgl_egl.cpp \
+ qgltexturepool.cpp
+
+ HEADERS += qgl_egl_p.h \
+ qgltexturepool_p.h
+
+ contains(QT_CONFIG, freetype) {
+ DEFINES += QT_NO_FONTCONFIG
+ INCLUDEPATH += \
+ ../3rdparty/freetype/src \
+ ../3rdparty/freetype/include
+ }
+
+ symbian:TARGET.UID3 = 0x2002131A
+}
+
+INCLUDEPATH += ../3rdparty/harfbuzz/src
diff --git a/src/opengl/opengl.pro.user.2.1pre1 b/src/opengl/opengl.pro.user.2.1pre1
new file mode 100644
index 0000000000..0c38724b0b
--- /dev/null
+++ b/src/opengl/opengl.pro.user.2.1pre1
@@ -0,0 +1,83 @@
+<!DOCTYPE QtCreatorProject>
+<qtcreator>
+ <data>
+ <variable>ProjectExplorer.Project.ActiveTarget</variable>
+ <value type="int">0</value>
+ </data>
+ <data>
+ <variable>ProjectExplorer.Project.EditorSettings</variable>
+ <valuemap type="QVariantMap">
+ <value key="EditorConfiguration.Codec" type="QByteArray">UTF-8</value>
+ </valuemap>
+ </data>
+ <data>
+ <variable>ProjectExplorer.Project.Target.0</variable>
+ <valuemap type="QVariantMap">
+ <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Desktop</value>
+ <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Target.DesktopTarget</value>
+ <value key="ProjectExplorer.Target.ActiveBuildConfiguration" type="int">0</value>
+ <value key="ProjectExplorer.Target.ActiveRunConfiguration" type="int">0</value>
+ <valuemap key="ProjectExplorer.Target.BuildConfiguration.0" type="QVariantMap">
+ <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.0" type="QVariantMap">
+ <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">qmake</value>
+ <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">QtProjectManager.QMakeBuildStep</value>
+ <valuelist key="QtProjectManager.QMakeBuildStep.QMakeArguments" type="QVariantList">
+ <value type="QString">QMAKE_ABSOLUTE_SOURCE_PATH=/home/jlind/dev/qt/lighthouse-master/src/opengl</value>
+ </valuelist>
+ </valuemap>
+ <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.1" type="QVariantMap">
+ <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value>
+ <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value>
+ <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">false</value>
+ <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList">
+ <value type="QString">-j9</value>
+ </valuelist>
+ <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value>
+ </valuemap>
+ <value key="ProjectExplorer.BuildConfiguration.BuildStepsCount" type="int">2</value>
+ <valuemap key="ProjectExplorer.BuildConfiguration.CleanStep.0" type="QVariantMap">
+ <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value>
+ <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value>
+ <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">true</value>
+ <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList">
+ <value type="QString">clean</value>
+ </valuelist>
+ <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value>
+ </valuemap>
+ <value key="ProjectExplorer.BuildConfiguration.CleanStepsCount" type="int">1</value>
+ <value key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment" type="bool">false</value>
+ <valuelist key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges" type="QVariantList"/>
+ <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Debug</value>
+ <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Qt4BuildConfiguration</value>
+ <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration" type="int">2</value>
+ <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory" type="QString">/home/jlind/builds/master-lighthouse/src/opengl</value>
+ <value key="Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId" type="int">23</value>
+ <value key="Qt4ProjectManager.Qt4BuildConfiguration.ToolChain" type="int">0</value>
+ <value key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild" type="bool">true</value>
+ </valuemap>
+ <value key="ProjectExplorer.Target.BuildConfigurationCount" type="int">1</value>
+ <valuemap key="ProjectExplorer.Target.RunConfiguration.0" type="QVariantMap">
+ <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">headers</value>
+ <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Qt4RunConfiguration</value>
+ <value key="Qt4ProjectManager.Qt4RunConfiguration.BaseEnvironmentBase" type="int">2</value>
+ <valuelist key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments" type="QVariantList"/>
+ <value key="Qt4ProjectManager.Qt4RunConfiguration.ProFile" type="QString">../../../../../builds/master-lighthouse/include/QtOpenGL/headers.pri</value>
+ <value key="Qt4ProjectManager.Qt4RunConfiguration.UseDyldImageSuffix" type="bool">false</value>
+ <value key="Qt4ProjectManager.Qt4RunConfiguration.UseTerminal" type="bool">false</value>
+ <valuelist key="Qt4ProjectManager.Qt4RunConfiguration.UserEnvironmentChanges" type="QVariantList"/>
+ <value key="Qt4ProjectManager.Qt4RunConfiguration.UserSetName" type="bool">false</value>
+ <value key="Qt4ProjectManager.Qt4RunConfiguration.UserSetWorkingDirectory" type="bool">false</value>
+ <value key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory" type="QString"></value>
+ </valuemap>
+ <value key="ProjectExplorer.Target.RunConfigurationCount" type="int">1</value>
+ </valuemap>
+ </data>
+ <data>
+ <variable>ProjectExplorer.Project.TargetCount</variable>
+ <value type="int">1</value>
+ </data>
+ <data>
+ <variable>ProjectExplorer.Project.Updater.FileVersion</variable>
+ <value type="int">4</value>
+ </data>
+</qtcreator>
diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp
new file mode 100644
index 0000000000..057fb55e2c
--- /dev/null
+++ b/src/opengl/qgl.cpp
@@ -0,0 +1,6057 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#include "qplatformdefs.h"
+#include "qgl.h"
+#include <qdebug.h>
+
+#if defined(Q_WS_X11)
+#include "private/qt_x11_p.h"
+#include "private/qpixmap_x11_p.h"
+#define INT32 dummy_INT32
+#define INT8 dummy_INT8
+#ifdef QT_NO_EGL
+# include <GL/glx.h>
+#endif
+#undef INT32
+#undef INT8
+#include "qx11info_x11.h"
+#elif defined(Q_WS_MAC)
+# include <private/qt_mac_p.h>
+#endif
+
+#include <qdatetime.h>
+
+#include <stdlib.h> // malloc
+
+#include "qpixmap.h"
+#include "qimage.h"
+#include "qgl_p.h"
+
+#if !defined(QT_OPENGL_ES_1)
+#include "gl2paintengineex/qpaintengineex_opengl2_p.h"
+#include <private/qwindowsurface_gl_p.h>
+#endif
+
+#ifndef QT_OPENGL_ES_2
+#include <private/qpaintengine_opengl_p.h>
+#endif
+
+#ifdef Q_WS_QWS
+#include <private/qglwindowsurface_qws_p.h>
+#endif
+
+#ifdef Q_WS_QPA
+#include <QtGui/QPlatformGLContext>
+#endif
+
+#include <qglpixelbuffer.h>
+#include <qglframebufferobject.h>
+
+#include <private/qimage_p.h>
+#include <private/qpixmapdata_p.h>
+#include <private/qpixmapdata_gl_p.h>
+#include <private/qglpixelbuffer_p.h>
+#include <private/qimagepixmapcleanuphooks_p.h>
+#include "qcolormap.h"
+#include "qfile.h"
+#include "qlibrary.h"
+#include <qmutex.h>
+
+#if defined(QT_OPENGL_ES) && !defined(QT_NO_EGL)
+#include <EGL/egl.h>
+#endif
+#ifdef QGL_USE_TEXTURE_POOL
+#include <private/qgltexturepool_p.h>
+#endif
+
+// #define QT_GL_CONTEXT_RESOURCE_DEBUG
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_SYMBIAN)
+QGLExtensionFuncs QGLContextPrivate::qt_extensionFuncs;
+#endif
+
+#ifdef Q_WS_X11
+extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
+#endif
+
+struct QGLThreadContext {
+ ~QGLThreadContext() {
+ if (context)
+ context->doneCurrent();
+ }
+ QGLContext *context;
+};
+
+#ifndef Q_WS_QPA
+static QThreadStorage<QGLThreadContext *> qgl_context_storage;
+#endif
+
+Q_GLOBAL_STATIC(QGLFormat, qgl_default_format)
+
+class QGLDefaultOverlayFormat: public QGLFormat
+{
+public:
+ inline QGLDefaultOverlayFormat()
+ {
+ setOption(QGL::FormatOption(0xffff << 16)); // turn off all options
+ setOption(QGL::DirectRendering);
+ setPlane(1);
+ }
+};
+Q_GLOBAL_STATIC(QGLDefaultOverlayFormat, defaultOverlayFormatInstance)
+
+Q_GLOBAL_STATIC(QGLSignalProxy, theSignalProxy)
+QGLSignalProxy *QGLSignalProxy::instance()
+{
+ QGLSignalProxy *proxy = theSignalProxy();
+ if (proxy && proxy->thread() != qApp->thread()) {
+ if (proxy->thread() == QThread::currentThread())
+ proxy->moveToThread(qApp->thread());
+ }
+ return proxy;
+}
+
+
+class QGLEngineSelector
+{
+public:
+ QGLEngineSelector() : engineType(QPaintEngine::MaxUser)
+ {
+ }
+
+ void setPreferredPaintEngine(QPaintEngine::Type type) {
+ if (type == QPaintEngine::OpenGL || type == QPaintEngine::OpenGL2)
+ engineType = type;
+ }
+
+ QPaintEngine::Type preferredPaintEngine() {
+#ifdef Q_WS_MAC
+ // The ATI X1600 driver for Mac OS X does not support return
+ // values from functions in GLSL. Since working around this in
+ // the GL2 engine would require a big, ugly rewrite, we're
+ // falling back to the GL 1 engine..
+ static bool mac_x1600_check_done = false;
+ if (!mac_x1600_check_done) {
+ QGLTemporaryContext *tmp = 0;
+ if (!QGLContext::currentContext())
+ tmp = new QGLTemporaryContext();
+ if (strstr((char *) glGetString(GL_RENDERER), "X1600"))
+ engineType = QPaintEngine::OpenGL;
+ if (tmp)
+ delete tmp;
+ mac_x1600_check_done = true;
+ }
+#endif
+ if (engineType == QPaintEngine::MaxUser) {
+ // No user-set engine - use the defaults
+#if defined(QT_OPENGL_ES_2)
+ engineType = QPaintEngine::OpenGL2;
+#else
+ // We can't do this in the constructor for this object because it
+ // needs to be called *before* the QApplication constructor.
+ // Also check for the FragmentShader extension in conjunction with
+ // the 2.0 version flag, to cover the case where we export the display
+ // from an old GL 1.1 server to a GL 2.x client. In that case we can't
+ // use GL 2.0.
+ if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0)
+ && (QGLExtensions::glExtensions() & QGLExtensions::FragmentShader)
+ && qgetenv("QT_GL_USE_OPENGL1ENGINE").isEmpty())
+ engineType = QPaintEngine::OpenGL2;
+ else
+ engineType = QPaintEngine::OpenGL;
+#endif
+ }
+ return engineType;
+ }
+
+private:
+ QPaintEngine::Type engineType;
+};
+
+Q_GLOBAL_STATIC(QGLEngineSelector, qgl_engine_selector)
+
+
+bool qt_gl_preferGL2Engine()
+{
+ return qgl_engine_selector()->preferredPaintEngine() == QPaintEngine::OpenGL2;
+}
+
+
+/*!
+ \namespace QGL
+ \inmodule QtOpenGL
+
+ \brief The QGL namespace specifies miscellaneous identifiers used
+ in the Qt OpenGL module.
+
+ \ingroup painting-3D
+*/
+
+/*!
+ \enum QGL::FormatOption
+
+ This enum specifies the format options that can be used to configure an OpenGL
+ context. These are set using QGLFormat::setOption().
+
+ \value DoubleBuffer Specifies the use of double buffering.
+ \value DepthBuffer Enables the use of a depth buffer.
+ \value Rgba Specifies that the context should use RGBA as its pixel format.
+ \value AlphaChannel Enables the use of an alpha channel.
+ \value AccumBuffer Enables the use of an accumulation buffer.
+ \value StencilBuffer Enables the use of a stencil buffer.
+ \value StereoBuffers Enables the use of a stereo buffers for use with visualization hardware.
+ \value DirectRendering Specifies that the context is used for direct rendering to a display.
+ \value HasOverlay Enables the use of an overlay.
+ \value SampleBuffers Enables the use of sample buffers.
+ \value DeprecatedFunctions Enables the use of deprecated functionality for OpenGL 3.x
+ contexts. A context with deprecated functionality enabled is
+ called a full context in the OpenGL specification.
+ \value SingleBuffer Specifies the use of a single buffer, as opposed to double buffers.
+ \value NoDepthBuffer Disables the use of a depth buffer.
+ \value ColorIndex Specifies that the context should use a color index as its pixel format.
+ \value NoAlphaChannel Disables the use of an alpha channel.
+ \value NoAccumBuffer Disables the use of an accumulation buffer.
+ \value NoStencilBuffer Disables the use of a stencil buffer.
+ \value NoStereoBuffers Disables the use of stereo buffers.
+ \value IndirectRendering Specifies that the context is used for indirect rendering to a buffer.
+ \value NoOverlay Disables the use of an overlay.
+ \value NoSampleBuffers Disables the use of sample buffers.
+ \value NoDeprecatedFunctions Disables the use of deprecated functionality for OpenGL 3.x
+ contexts. A context with deprecated functionality disabled is
+ called a forward compatible context in the OpenGL specification.
+
+ \sa {Sample Buffers Example}
+*/
+
+/*!
+ \fn void QGL::setPreferredPaintEngine(QPaintEngine::Type engineType)
+
+ \since 4.6
+
+ Sets the preferred OpenGL paint engine that is used to draw onto
+ QGLWidget, QGLPixelBuffer and QGLFramebufferObject targets with QPainter
+ in Qt.
+
+ The \a engineType parameter specifies which of the GL engines to
+ use. Only \c QPaintEngine::OpenGL and \c QPaintEngine::OpenGL2 are
+ valid parameters to this function. All other values are ignored.
+
+ By default, the \c QPaintEngine::OpenGL2 engine is used if GL/GLES
+ version 2.0 is available, otherwise \c QPaintEngine::OpenGL is
+ used.
+
+ \warning This function must be called before the QApplication
+ constructor is called.
+*/
+void QGL::setPreferredPaintEngine(QPaintEngine::Type engineType)
+{
+ qgl_engine_selector()->setPreferredPaintEngine(engineType);
+}
+
+
+/*****************************************************************************
+ QGLFormat implementation
+ *****************************************************************************/
+
+
+/*!
+ \class QGLFormat
+ \brief The QGLFormat class specifies the display format of an OpenGL
+ rendering context.
+
+ \ingroup painting-3D
+
+ A display format has several characteristics:
+ \list
+ \i \link setDoubleBuffer() Double or single buffering.\endlink
+ \i \link setDepth() Depth buffer.\endlink
+ \i \link setRgba() RGBA or color index mode.\endlink
+ \i \link setAlpha() Alpha channel.\endlink
+ \i \link setAccum() Accumulation buffer.\endlink
+ \i \link setStencil() Stencil buffer.\endlink
+ \i \link setStereo() Stereo buffers.\endlink
+ \i \link setDirectRendering() Direct rendering.\endlink
+ \i \link setOverlay() Presence of an overlay.\endlink
+ \i \link setPlane() Plane of an overlay.\endlink
+ \i \link setSampleBuffers() Multisample buffers.\endlink
+ \endlist
+
+ You can also specify preferred bit depths for the color buffer,
+ depth buffer, alpha buffer, accumulation buffer and the stencil
+ buffer with the functions: setRedBufferSize(), setGreenBufferSize(),
+ setBlueBufferSize(), setDepthBufferSize(), setAlphaBufferSize(),
+ setAccumBufferSize() and setStencilBufferSize().
+
+ Note that even if you specify that you prefer a 32 bit depth
+ buffer (e.g. with setDepthBufferSize(32)), the format that is
+ chosen may not have a 32 bit depth buffer, even if there is a
+ format available with a 32 bit depth buffer. The main reason for
+ this is how the system dependant picking algorithms work on the
+ different platforms, and some format options may have higher
+ precedence than others.
+
+ You create and tell a QGLFormat object what rendering options you
+ want from an OpenGL rendering context.
+
+ OpenGL drivers or accelerated hardware may or may not support
+ advanced features such as alpha channel or stereographic viewing.
+ If you request some features that the driver/hardware does not
+ provide when you create a QGLWidget, you will get a rendering
+ context with the nearest subset of features.
+
+ There are different ways to define the display characteristics of
+ a rendering context. One is to create a QGLFormat and make it the
+ default for the entire application:
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 0
+
+ Or you can specify the desired format when creating an object of
+ your QGLWidget subclass:
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 1
+
+ After the widget has been created, you can find out which of the
+ requested features the system was able to provide:
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 2
+
+ \legalese
+ OpenGL is a trademark of Silicon Graphics, Inc. in the
+ United States and other countries.
+ \endlegalese
+
+ \sa QGLContext, QGLWidget
+*/
+
+#ifndef QT_OPENGL_ES
+
+static inline void transform_point(GLdouble out[4], const GLdouble m[16], const GLdouble in[4])
+{
+#define M(row,col) m[col*4+row]
+ out[0] =
+ M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];
+ out[1] =
+ M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];
+ out[2] =
+ M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];
+ out[3] =
+ M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];
+#undef M
+}
+
+static inline GLint qgluProject(GLdouble objx, GLdouble objy, GLdouble objz,
+ const GLdouble model[16], const GLdouble proj[16],
+ const GLint viewport[4],
+ GLdouble * winx, GLdouble * winy, GLdouble * winz)
+{
+ GLdouble in[4], out[4];
+
+ in[0] = objx;
+ in[1] = objy;
+ in[2] = objz;
+ in[3] = 1.0;
+ transform_point(out, model, in);
+ transform_point(in, proj, out);
+
+ if (in[3] == 0.0)
+ return GL_FALSE;
+
+ in[0] /= in[3];
+ in[1] /= in[3];
+ in[2] /= in[3];
+
+ *winx = viewport[0] + (1 + in[0]) * viewport[2] / 2;
+ *winy = viewport[1] + (1 + in[1]) * viewport[3] / 2;
+
+ *winz = (1 + in[2]) / 2;
+ return GL_TRUE;
+}
+
+#endif // !QT_OPENGL_ES
+
+/*!
+ Constructs a QGLFormat object with the following default settings:
+ \list
+ \i \link setDoubleBuffer() Double buffer:\endlink Enabled.
+ \i \link setDepth() Depth buffer:\endlink Enabled.
+ \i \link setRgba() RGBA:\endlink Enabled (i.e., color index disabled).
+ \i \link setAlpha() Alpha channel:\endlink Disabled.
+ \i \link setAccum() Accumulator buffer:\endlink Disabled.
+ \i \link setStencil() Stencil buffer:\endlink Enabled.
+ \i \link setStereo() Stereo:\endlink Disabled.
+ \i \link setDirectRendering() Direct rendering:\endlink Enabled.
+ \i \link setOverlay() Overlay:\endlink Disabled.
+ \i \link setPlane() Plane:\endlink 0 (i.e., normal plane).
+ \i \link setSampleBuffers() Multisample buffers:\endlink Disabled.
+ \endlist
+*/
+
+QGLFormat::QGLFormat()
+{
+ d = new QGLFormatPrivate;
+}
+
+
+/*!
+ Creates a QGLFormat object that is a copy of the current
+ defaultFormat().
+
+ If \a options is not 0, the default format is modified by the
+ specified format options. The \a options parameter should be
+ QGL::FormatOption values OR'ed together.
+
+ This constructor makes it easy to specify a certain desired format
+ in classes derived from QGLWidget, for example:
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 3
+
+ Note that there are QGL::FormatOption values to turn format settings
+ both on and off, e.g. QGL::DepthBuffer and QGL::NoDepthBuffer,
+ QGL::DirectRendering and QGL::IndirectRendering, etc.
+
+ The \a plane parameter defaults to 0 and is the plane which this
+ format should be associated with. Not all OpenGL implementations
+ supports overlay/underlay rendering planes.
+
+ \sa defaultFormat(), setOption(), setPlane()
+*/
+
+QGLFormat::QGLFormat(QGL::FormatOptions options, int plane)
+{
+ d = new QGLFormatPrivate;
+ QGL::FormatOptions newOpts = options;
+ d->opts = defaultFormat().d->opts;
+ d->opts |= (newOpts & 0xffff);
+ d->opts &= ~(newOpts >> 16);
+ d->pln = plane;
+}
+
+/*!
+ \internal
+*/
+void QGLFormat::detach()
+{
+ if (d->ref != 1) {
+ QGLFormatPrivate *newd = new QGLFormatPrivate(d);
+ if (!d->ref.deref())
+ delete d;
+ d = newd;
+ }
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QGLFormat::QGLFormat(const QGLFormat &other)
+{
+ d = other.d;
+ d->ref.ref();
+}
+
+/*!
+ Assigns \a other to this object.
+*/
+
+QGLFormat &QGLFormat::operator=(const QGLFormat &other)
+{
+ if (d != other.d) {
+ other.d->ref.ref();
+ if (!d->ref.deref())
+ delete d;
+ d = other.d;
+ }
+ return *this;
+}
+
+/*!
+ Destroys the QGLFormat.
+*/
+QGLFormat::~QGLFormat()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ \fn bool QGLFormat::doubleBuffer() const
+
+ Returns true if double buffering is enabled; otherwise returns
+ false. Double buffering is enabled by default.
+
+ \sa setDoubleBuffer()
+*/
+
+/*!
+ If \a enable is true sets double buffering; otherwise sets single
+ buffering.
+
+ Double buffering is enabled by default.
+
+ Double buffering is a technique where graphics are rendered on an
+ off-screen buffer and not directly to the screen. When the drawing
+ has been completed, the program calls a swapBuffers() function to
+ exchange the screen contents with the buffer. The result is
+ flicker-free drawing and often better performance.
+
+ Note that single buffered contexts are currently not supported
+ with EGL.
+
+ \sa doubleBuffer(), QGLContext::swapBuffers(),
+ QGLWidget::swapBuffers()
+*/
+
+void QGLFormat::setDoubleBuffer(bool enable)
+{
+ setOption(enable ? QGL::DoubleBuffer : QGL::SingleBuffer);
+}
+
+
+/*!
+ \fn bool QGLFormat::depth() const
+
+ Returns true if the depth buffer is enabled; otherwise returns
+ false. The depth buffer is enabled by default.
+
+ \sa setDepth(), setDepthBufferSize()
+*/
+
+/*!
+ If \a enable is true enables the depth buffer; otherwise disables
+ the depth buffer.
+
+ The depth buffer is enabled by default.
+
+ The purpose of a depth buffer (or Z-buffering) is to remove hidden
+ surfaces. Pixels are assigned Z values based on the distance to
+ the viewer. A pixel with a high Z value is closer to the viewer
+ than a pixel with a low Z value. This information is used to
+ decide whether to draw a pixel or not.
+
+ \sa depth(), setDepthBufferSize()
+*/
+
+void QGLFormat::setDepth(bool enable)
+{
+ setOption(enable ? QGL::DepthBuffer : QGL::NoDepthBuffer);
+}
+
+
+/*!
+ \fn bool QGLFormat::rgba() const
+
+ Returns true if RGBA color mode is set. Returns false if color
+ index mode is set. The default color mode is RGBA.
+
+ \sa setRgba()
+*/
+
+/*!
+ If \a enable is true sets RGBA mode. If \a enable is false sets
+ color index mode.
+
+ The default color mode is RGBA.
+
+ RGBA is the preferred mode for most OpenGL applications. In RGBA
+ color mode you specify colors as red + green + blue + alpha
+ quadruplets.
+
+ In color index mode you specify an index into a color lookup
+ table.
+
+ \sa rgba()
+*/
+
+void QGLFormat::setRgba(bool enable)
+{
+ setOption(enable ? QGL::Rgba : QGL::ColorIndex);
+}
+
+
+/*!
+ \fn bool QGLFormat::alpha() const
+
+ Returns true if the alpha buffer in the framebuffer is enabled;
+ otherwise returns false. The alpha buffer is disabled by default.
+
+ \sa setAlpha(), setAlphaBufferSize()
+*/
+
+/*!
+ If \a enable is true enables the alpha buffer; otherwise disables
+ the alpha buffer.
+
+ The alpha buffer is disabled by default.
+
+ The alpha buffer is typically used for implementing transparency
+ or translucency. The A in RGBA specifies the transparency of a
+ pixel.
+
+ \sa alpha(), setAlphaBufferSize()
+*/
+
+void QGLFormat::setAlpha(bool enable)
+{
+ setOption(enable ? QGL::AlphaChannel : QGL::NoAlphaChannel);
+}
+
+
+/*!
+ \fn bool QGLFormat::accum() const
+
+ Returns true if the accumulation buffer is enabled; otherwise
+ returns false. The accumulation buffer is disabled by default.
+
+ \sa setAccum(), setAccumBufferSize()
+*/
+
+/*!
+ If \a enable is true enables the accumulation buffer; otherwise
+ disables the accumulation buffer.
+
+ The accumulation buffer is disabled by default.
+
+ The accumulation buffer is used to create blur effects and
+ multiple exposures.
+
+ \sa accum(), setAccumBufferSize()
+*/
+
+void QGLFormat::setAccum(bool enable)
+{
+ setOption(enable ? QGL::AccumBuffer : QGL::NoAccumBuffer);
+}
+
+
+/*!
+ \fn bool QGLFormat::stencil() const
+
+ Returns true if the stencil buffer is enabled; otherwise returns
+ false. The stencil buffer is enabled by default.
+
+ \sa setStencil(), setStencilBufferSize()
+*/
+
+/*!
+ If \a enable is true enables the stencil buffer; otherwise
+ disables the stencil buffer.
+
+ The stencil buffer is enabled by default.
+
+ The stencil buffer masks certain parts of the drawing area so that
+ masked parts are not drawn on.
+
+ \sa stencil(), setStencilBufferSize()
+*/
+
+void QGLFormat::setStencil(bool enable)
+{
+ setOption(enable ? QGL::StencilBuffer: QGL::NoStencilBuffer);
+}
+
+
+/*!
+ \fn bool QGLFormat::stereo() const
+
+ Returns true if stereo buffering is enabled; otherwise returns
+ false. Stereo buffering is disabled by default.
+
+ \sa setStereo()
+*/
+
+/*!
+ If \a enable is true enables stereo buffering; otherwise disables
+ stereo buffering.
+
+ Stereo buffering is disabled by default.
+
+ Stereo buffering provides extra color buffers to generate left-eye
+ and right-eye images.
+
+ \sa stereo()
+*/
+
+void QGLFormat::setStereo(bool enable)
+{
+ setOption(enable ? QGL::StereoBuffers : QGL::NoStereoBuffers);
+}
+
+
+/*!
+ \fn bool QGLFormat::directRendering() const
+
+ Returns true if direct rendering is enabled; otherwise returns
+ false.
+
+ Direct rendering is enabled by default.
+
+ \sa setDirectRendering()
+*/
+
+/*!
+ If \a enable is true enables direct rendering; otherwise disables
+ direct rendering.
+
+ Direct rendering is enabled by default.
+
+ Enabling this option will make OpenGL bypass the underlying window
+ system and render directly from hardware to the screen, if this is
+ supported by the system.
+
+ \sa directRendering()
+*/
+
+void QGLFormat::setDirectRendering(bool enable)
+{
+ setOption(enable ? QGL::DirectRendering : QGL::IndirectRendering);
+}
+
+/*!
+ \fn bool QGLFormat::sampleBuffers() const
+
+ Returns true if multisample buffer support is enabled; otherwise
+ returns false.
+
+ The multisample buffer is disabled by default.
+
+ \sa setSampleBuffers()
+*/
+
+/*!
+ If \a enable is true, a GL context with multisample buffer support
+ is picked; otherwise ignored.
+
+ \sa sampleBuffers(), setSamples(), samples()
+*/
+void QGLFormat::setSampleBuffers(bool enable)
+{
+ setOption(enable ? QGL::SampleBuffers : QGL::NoSampleBuffers);
+}
+
+/*!
+ Returns the number of samples per pixel when multisampling is
+ enabled. By default, the highest number of samples that is
+ available is used.
+
+ \sa setSampleBuffers(), sampleBuffers(), setSamples()
+*/
+int QGLFormat::samples() const
+{
+ return d->numSamples;
+}
+
+/*!
+ Set the preferred number of samples per pixel when multisampling
+ is enabled to \a numSamples. By default, the highest number of
+ samples available is used.
+
+ \sa setSampleBuffers(), sampleBuffers(), samples()
+*/
+void QGLFormat::setSamples(int numSamples)
+{
+ detach();
+ if (numSamples < 0) {
+ qWarning("QGLFormat::setSamples: Cannot have negative number of samples per pixel %d", numSamples);
+ return;
+ }
+ d->numSamples = numSamples;
+ setSampleBuffers(numSamples > 0);
+}
+
+/*!
+ \since 4.2
+
+ Set the preferred swap interval. This can be used to sync the GL
+ drawing into a system window to the vertical refresh of the screen.
+ Setting an \a interval value of 0 will turn the vertical refresh syncing
+ off, any value higher than 0 will turn the vertical syncing on.
+
+ Under Windows and under X11, where the \c{WGL_EXT_swap_control}
+ and \c{GLX_SGI_video_sync} extensions are used, the \a interval
+ parameter can be used to set the minimum number of video frames
+ that are displayed before a buffer swap will occur. In effect,
+ setting the \a interval to 10, means there will be 10 vertical
+ retraces between every buffer swap.
+
+ Under Windows the \c{WGL_EXT_swap_control} extension has to be present,
+ and under X11 the \c{GLX_SGI_video_sync} extension has to be present.
+*/
+void QGLFormat::setSwapInterval(int interval)
+{
+ detach();
+ d->swapInterval = interval;
+}
+
+/*!
+ \since 4.2
+
+ Returns the currently set swap interval. -1 is returned if setting
+ the swap interval isn't supported in the system GL implementation.
+*/
+int QGLFormat::swapInterval() const
+{
+ return d->swapInterval;
+}
+
+/*!
+ \fn bool QGLFormat::hasOverlay() const
+
+ Returns true if overlay plane is enabled; otherwise returns false.
+
+ Overlay is disabled by default.
+
+ \sa setOverlay()
+*/
+
+/*!
+ If \a enable is true enables an overlay plane; otherwise disables
+ the overlay plane.
+
+ Enabling the overlay plane will cause QGLWidget to create an
+ additional context in an overlay plane. See the QGLWidget
+ documentation for further information.
+
+ \sa hasOverlay()
+*/
+
+void QGLFormat::setOverlay(bool enable)
+{
+ setOption(enable ? QGL::HasOverlay : QGL::NoOverlay);
+}
+
+/*!
+ Returns the plane of this format. The default for normal formats
+ is 0, which means the normal plane. The default for overlay
+ formats is 1, which is the first overlay plane.
+
+ \sa setPlane(), defaultOverlayFormat()
+*/
+int QGLFormat::plane() const
+{
+ return d->pln;
+}
+
+/*!
+ Sets the requested plane to \a plane. 0 is the normal plane, 1 is
+ the first overlay plane, 2 is the second overlay plane, etc.; -1,
+ -2, etc. are underlay planes.
+
+ Note that in contrast to other format specifications, the plane
+ specifications will be matched exactly. This means that if you
+ specify a plane that the underlying OpenGL system cannot provide,
+ an \link QGLWidget::isValid() invalid\endlink QGLWidget will be
+ created.
+
+ \sa plane()
+*/
+void QGLFormat::setPlane(int plane)
+{
+ detach();
+ d->pln = plane;
+}
+
+/*!
+ Sets the format option to \a opt.
+
+ \sa testOption()
+*/
+
+void QGLFormat::setOption(QGL::FormatOptions opt)
+{
+ detach();
+ if (opt & 0xffff)
+ d->opts |= opt;
+ else
+ d->opts &= ~(opt >> 16);
+}
+
+
+
+/*!
+ Returns true if format option \a opt is set; otherwise returns false.
+
+ \sa setOption()
+*/
+
+bool QGLFormat::testOption(QGL::FormatOptions opt) const
+{
+ if (opt & 0xffff)
+ return (d->opts & opt) != 0;
+ else
+ return (d->opts & (opt >> 16)) == 0;
+}
+
+/*!
+ Set the minimum depth buffer size to \a size.
+
+ \sa depthBufferSize(), setDepth(), depth()
+*/
+void QGLFormat::setDepthBufferSize(int size)
+{
+ detach();
+ if (size < 0) {
+ qWarning("QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size %d", size);
+ return;
+ }
+ d->depthSize = size;
+ setDepth(size > 0);
+}
+
+/*!
+ Returns the depth buffer size.
+
+ \sa depth(), setDepth(), setDepthBufferSize()
+*/
+int QGLFormat::depthBufferSize() const
+{
+ return d->depthSize;
+}
+
+/*!
+ \since 4.2
+
+ Set the preferred red buffer size to \a size.
+
+ \sa setGreenBufferSize(), setBlueBufferSize(), setAlphaBufferSize()
+*/
+void QGLFormat::setRedBufferSize(int size)
+{
+ detach();
+ if (size < 0) {
+ qWarning("QGLFormat::setRedBufferSize: Cannot set negative red buffer size %d", size);
+ return;
+ }
+ d->redSize = size;
+}
+
+/*!
+ \since 4.2
+
+ Returns the red buffer size.
+
+ \sa setRedBufferSize()
+*/
+int QGLFormat::redBufferSize() const
+{
+ return d->redSize;
+}
+
+/*!
+ \since 4.2
+
+ Set the preferred green buffer size to \a size.
+
+ \sa setRedBufferSize(), setBlueBufferSize(), setAlphaBufferSize()
+*/
+void QGLFormat::setGreenBufferSize(int size)
+{
+ detach();
+ if (size < 0) {
+ qWarning("QGLFormat::setGreenBufferSize: Cannot set negative green buffer size %d", size);
+ return;
+ }
+ d->greenSize = size;
+}
+
+/*!
+ \since 4.2
+
+ Returns the green buffer size.
+
+ \sa setGreenBufferSize()
+*/
+int QGLFormat::greenBufferSize() const
+{
+ return d->greenSize;
+}
+
+/*!
+ \since 4.2
+
+ Set the preferred blue buffer size to \a size.
+
+ \sa setRedBufferSize(), setGreenBufferSize(), setAlphaBufferSize()
+*/
+void QGLFormat::setBlueBufferSize(int size)
+{
+ detach();
+ if (size < 0) {
+ qWarning("QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size %d", size);
+ return;
+ }
+ d->blueSize = size;
+}
+
+/*!
+ \since 4.2
+
+ Returns the blue buffer size.
+
+ \sa setBlueBufferSize()
+*/
+int QGLFormat::blueBufferSize() const
+{
+ return d->blueSize;
+}
+
+/*!
+ Set the preferred alpha buffer size to \a size.
+ This function implicitly enables the alpha channel.
+
+ \sa setRedBufferSize(), setGreenBufferSize(), alphaBufferSize()
+*/
+void QGLFormat::setAlphaBufferSize(int size)
+{
+ detach();
+ if (size < 0) {
+ qWarning("QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size %d", size);
+ return;
+ }
+ d->alphaSize = size;
+ setAlpha(size > 0);
+}
+
+/*!
+ Returns the alpha buffer size.
+
+ \sa alpha(), setAlpha(), setAlphaBufferSize()
+*/
+int QGLFormat::alphaBufferSize() const
+{
+ return d->alphaSize;
+}
+
+/*!
+ Set the preferred accumulation buffer size, where \a size is the
+ bit depth for each RGBA component.
+
+ \sa accum(), setAccum(), accumBufferSize()
+*/
+void QGLFormat::setAccumBufferSize(int size)
+{
+ detach();
+ if (size < 0) {
+ qWarning("QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size %d", size);
+ return;
+ }
+ d->accumSize = size;
+ setAccum(size > 0);
+}
+
+/*!
+ Returns the accumulation buffer size.
+
+ \sa setAccumBufferSize(), accum(), setAccum()
+*/
+int QGLFormat::accumBufferSize() const
+{
+ return d->accumSize;
+}
+
+/*!
+ Set the preferred stencil buffer size to \a size.
+
+ \sa stencilBufferSize(), setStencil(), stencil()
+*/
+void QGLFormat::setStencilBufferSize(int size)
+{
+ detach();
+ if (size < 0) {
+ qWarning("QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size %d", size);
+ return;
+ }
+ d->stencilSize = size;
+ setStencil(size > 0);
+}
+
+/*!
+ Returns the stencil buffer size.
+
+ \sa stencil(), setStencil(), setStencilBufferSize()
+*/
+int QGLFormat::stencilBufferSize() const
+{
+ return d->stencilSize;
+}
+
+/*!
+ \since 4.7
+
+ Set the OpenGL version to the \a major and \a minor numbers. If a
+ context compatible with the requested OpenGL version cannot be
+ created, a context compatible with version 1.x is created instead.
+
+ \sa majorVersion(), minorVersion()
+*/
+void QGLFormat::setVersion(int major, int minor)
+{
+ if (major < 1 || minor < 0) {
+ qWarning("QGLFormat::setVersion: Cannot set zero or negative version number %d.%d", major, minor);
+ return;
+ }
+ detach();
+ d->majorVersion = major;
+ d->minorVersion = minor;
+}
+
+/*!
+ \since 4.7
+
+ Returns the OpenGL major version.
+
+ \sa setVersion(), minorVersion()
+*/
+int QGLFormat::majorVersion() const
+{
+ return d->majorVersion;
+}
+
+/*!
+ \since 4.7
+
+ Returns the OpenGL minor version.
+
+ \sa setVersion(), majorVersion()
+*/
+int QGLFormat::minorVersion() const
+{
+ return d->minorVersion;
+}
+
+/*!
+ \enum QGLFormat::OpenGLContextProfile
+ \since 4.7
+
+ This enum describes the OpenGL context profiles that can be
+ specified for contexts implementing OpenGL version 3.2 or
+ higher. These profiles are different from OpenGL ES profiles.
+
+ \value NoProfile OpenGL version is lower than 3.2.
+ \value CoreProfile Functionality deprecated in OpenGL version 3.0 is not available.
+ \value CompatibilityProfile Functionality from earlier OpenGL versions is available.
+*/
+
+/*!
+ \since 4.7
+
+ Set the OpenGL context profile to \a profile. The \a profile is
+ ignored if the requested OpenGL version is less than 3.2.
+
+ \sa profile()
+*/
+void QGLFormat::setProfile(OpenGLContextProfile profile)
+{
+ detach();
+ d->profile = profile;
+}
+
+/*!
+ \since 4.7
+
+ Returns the OpenGL context profile.
+
+ \sa setProfile()
+*/
+QGLFormat::OpenGLContextProfile QGLFormat::profile() const
+{
+ return d->profile;
+}
+
+
+/*!
+ \fn bool QGLFormat::hasOpenGL()
+
+ Returns true if the window system has any OpenGL support;
+ otherwise returns false.
+
+ \warning This function must not be called until the QApplication
+ object has been created.
+*/
+
+
+
+/*!
+ \fn bool QGLFormat::hasOpenGLOverlays()
+
+ Returns true if the window system supports OpenGL overlays;
+ otherwise returns false.
+
+ \warning This function must not be called until the QApplication
+ object has been created.
+*/
+
+QGLFormat::OpenGLVersionFlags Q_AUTOTEST_EXPORT qOpenGLVersionFlagsFromString(const QString &versionString)
+{
+ QGLFormat::OpenGLVersionFlags versionFlags = QGLFormat::OpenGL_Version_None;
+
+ if (versionString.startsWith(QLatin1String("OpenGL ES"))) {
+ QStringList parts = versionString.split(QLatin1Char(' '));
+ if (parts.size() >= 3) {
+ if (parts[2].startsWith(QLatin1String("1."))) {
+ if (parts[1].endsWith(QLatin1String("-CM"))) {
+ versionFlags |= QGLFormat::OpenGL_ES_Common_Version_1_0 |
+ QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
+ if (parts[2].startsWith(QLatin1String("1.1")))
+ versionFlags |= QGLFormat::OpenGL_ES_Common_Version_1_1 |
+ QGLFormat::OpenGL_ES_CommonLite_Version_1_1;
+ } else {
+ // Not -CM, must be CL, CommonLite
+ versionFlags |= QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
+ if (parts[2].startsWith(QLatin1String("1.1")))
+ versionFlags |= QGLFormat::OpenGL_ES_CommonLite_Version_1_1;
+ }
+ } else {
+ // OpenGL ES version 2.0 or higher
+ versionFlags |= QGLFormat::OpenGL_ES_Version_2_0;
+ }
+ } else {
+ // if < 3 parts to the name, it is an unrecognised OpenGL ES
+ qWarning("Unrecognised OpenGL ES version");
+ }
+ } else {
+ // not ES, regular OpenGL, the version numbers are first in the string
+ if (versionString.startsWith(QLatin1String("1."))) {
+ switch (versionString[2].toAscii()) {
+ case '5':
+ versionFlags |= QGLFormat::OpenGL_Version_1_5;
+ case '4':
+ versionFlags |= QGLFormat::OpenGL_Version_1_4;
+ case '3':
+ versionFlags |= QGLFormat::OpenGL_Version_1_3;
+ case '2':
+ versionFlags |= QGLFormat::OpenGL_Version_1_2;
+ case '1':
+ versionFlags |= QGLFormat::OpenGL_Version_1_1;
+ default:
+ break;
+ }
+ } else if (versionString.startsWith(QLatin1String("2."))) {
+ versionFlags |= QGLFormat::OpenGL_Version_1_1 |
+ QGLFormat::OpenGL_Version_1_2 |
+ QGLFormat::OpenGL_Version_1_3 |
+ QGLFormat::OpenGL_Version_1_4 |
+ QGLFormat::OpenGL_Version_1_5 |
+ QGLFormat::OpenGL_Version_2_0;
+ if (versionString[2].toAscii() == '1')
+ versionFlags |= QGLFormat::OpenGL_Version_2_1;
+ } else if (versionString.startsWith(QLatin1String("3."))) {
+ versionFlags |= QGLFormat::OpenGL_Version_1_1 |
+ QGLFormat::OpenGL_Version_1_2 |
+ QGLFormat::OpenGL_Version_1_3 |
+ QGLFormat::OpenGL_Version_1_4 |
+ QGLFormat::OpenGL_Version_1_5 |
+ QGLFormat::OpenGL_Version_2_0 |
+ QGLFormat::OpenGL_Version_2_1 |
+ QGLFormat::OpenGL_Version_3_0;
+ switch (versionString[2].toAscii()) {
+ case '3':
+ versionFlags |= QGLFormat::OpenGL_Version_3_3;
+ case '2':
+ versionFlags |= QGLFormat::OpenGL_Version_3_2;
+ case '1':
+ versionFlags |= QGLFormat::OpenGL_Version_3_1;
+ case '0':
+ break;
+ default:
+ versionFlags |= QGLFormat::OpenGL_Version_3_1 |
+ QGLFormat::OpenGL_Version_3_2 |
+ QGLFormat::OpenGL_Version_3_3;
+ break;
+ }
+ } else if (versionString.startsWith(QLatin1String("4."))) {
+ versionFlags |= QGLFormat::OpenGL_Version_1_1 |
+ QGLFormat::OpenGL_Version_1_2 |
+ QGLFormat::OpenGL_Version_1_3 |
+ QGLFormat::OpenGL_Version_1_4 |
+ QGLFormat::OpenGL_Version_1_5 |
+ QGLFormat::OpenGL_Version_2_0 |
+ QGLFormat::OpenGL_Version_2_1 |
+ QGLFormat::OpenGL_Version_3_0 |
+ QGLFormat::OpenGL_Version_3_1 |
+ QGLFormat::OpenGL_Version_3_2 |
+ QGLFormat::OpenGL_Version_3_3 |
+ QGLFormat::OpenGL_Version_4_0;
+ } else {
+ versionFlags |= QGLFormat::OpenGL_Version_1_1 |
+ QGLFormat::OpenGL_Version_1_2 |
+ QGLFormat::OpenGL_Version_1_3 |
+ QGLFormat::OpenGL_Version_1_4 |
+ QGLFormat::OpenGL_Version_1_5 |
+ QGLFormat::OpenGL_Version_2_0 |
+ QGLFormat::OpenGL_Version_2_1 |
+ QGLFormat::OpenGL_Version_3_0 |
+ QGLFormat::OpenGL_Version_3_1 |
+ QGLFormat::OpenGL_Version_3_2 |
+ QGLFormat::OpenGL_Version_3_3 |
+ QGLFormat::OpenGL_Version_4_0;
+ }
+ }
+ return versionFlags;
+}
+
+/*!
+ \enum QGLFormat::OpenGLVersionFlag
+ \since 4.2
+
+ This enum describes the various OpenGL versions that are
+ recognized by Qt. Use the QGLFormat::openGLVersionFlags() function
+ to identify which versions that are supported at runtime.
+
+ \value OpenGL_Version_None If no OpenGL is present or if no OpenGL context is current.
+
+ \value OpenGL_Version_1_1 OpenGL version 1.1 or higher is present.
+
+ \value OpenGL_Version_1_2 OpenGL version 1.2 or higher is present.
+
+ \value OpenGL_Version_1_3 OpenGL version 1.3 or higher is present.
+
+ \value OpenGL_Version_1_4 OpenGL version 1.4 or higher is present.
+
+ \value OpenGL_Version_1_5 OpenGL version 1.5 or higher is present.
+
+ \value OpenGL_Version_2_0 OpenGL version 2.0 or higher is present.
+ Note that version 2.0 supports all the functionality of version 1.5.
+
+ \value OpenGL_Version_2_1 OpenGL version 2.1 or higher is present.
+
+ \value OpenGL_Version_3_0 OpenGL version 3.0 or higher is present.
+
+ \value OpenGL_Version_3_1 OpenGL version 3.1 or higher is present.
+ Note that OpenGL version 3.1 or higher does not necessarily support all the features of
+ version 3.0 and lower.
+
+ \value OpenGL_Version_3_2 OpenGL version 3.2 or higher is present.
+
+ \value OpenGL_Version_3_3 OpenGL version 3.3 or higher is present.
+
+ \value OpenGL_Version_4_0 OpenGL version 4.0 or higher is present.
+
+ \value OpenGL_ES_CommonLite_Version_1_0 OpenGL ES version 1.0 Common Lite or higher is present.
+
+ \value OpenGL_ES_Common_Version_1_0 OpenGL ES version 1.0 Common or higher is present.
+ The Common profile supports all the features of Common Lite.
+
+ \value OpenGL_ES_CommonLite_Version_1_1 OpenGL ES version 1.1 Common Lite or higher is present.
+
+ \value OpenGL_ES_Common_Version_1_1 OpenGL ES version 1.1 Common or higher is present.
+ The Common profile supports all the features of Common Lite.
+
+ \value OpenGL_ES_Version_2_0 OpenGL ES version 2.0 or higher is present.
+ Note that OpenGL ES version 2.0 does not support all the features of OpenGL ES 1.x.
+ So if OpenGL_ES_Version_2_0 is returned, none of the ES 1.x flags are returned.
+
+ See also \l{http://www.opengl.org} for more information about the different
+ revisions of OpenGL.
+
+ \sa openGLVersionFlags()
+*/
+
+/*!
+ \since 4.2
+
+ Identifies, at runtime, which OpenGL versions that are supported
+ by the current platform.
+
+ Note that if OpenGL version 1.5 is supported, its predecessors
+ (i.e., version 1.4 and lower) are also supported. To identify the
+ support of a particular feature, like multi texturing, test for
+ the version in which the feature was first introduced (i.e.,
+ version 1.3 in the case of multi texturing) to adapt to the largest
+ possible group of runtime platforms.
+
+ This function needs a valid current OpenGL context to work;
+ otherwise it will return OpenGL_Version_None.
+
+ \sa hasOpenGL(), hasOpenGLOverlays()
+*/
+QGLFormat::OpenGLVersionFlags QGLFormat::openGLVersionFlags()
+{
+ static bool cachedDefault = false;
+ static OpenGLVersionFlags defaultVersionFlags = OpenGL_Version_None;
+ QGLContext *currentCtx = const_cast<QGLContext *>(QGLContext::currentContext());
+ QGLTemporaryContext *tmpContext = 0;
+
+ if (currentCtx && currentCtx->d_func()->version_flags_cached)
+ return currentCtx->d_func()->version_flags;
+
+ if (!currentCtx) {
+ if (cachedDefault) {
+ return defaultVersionFlags;
+ } else {
+ if (!hasOpenGL())
+ return defaultVersionFlags;
+ tmpContext = new QGLTemporaryContext;
+ cachedDefault = true;
+ }
+ }
+
+ QString versionString(QLatin1String(reinterpret_cast<const char*>(glGetString(GL_VERSION))));
+ OpenGLVersionFlags versionFlags = qOpenGLVersionFlagsFromString(versionString);
+ if (currentCtx) {
+ currentCtx->d_func()->version_flags_cached = true;
+ currentCtx->d_func()->version_flags = versionFlags;
+ }
+ if (tmpContext) {
+ defaultVersionFlags = versionFlags;
+ delete tmpContext;
+ }
+
+ return versionFlags;
+}
+
+
+/*!
+ Returns the default QGLFormat for the application. All QGLWidget
+ objects that are created use this format unless another format is
+ specified, e.g. when they are constructed.
+
+ If no special default format has been set using
+ setDefaultFormat(), the default format is the same as that created
+ with QGLFormat().
+
+ \sa setDefaultFormat()
+*/
+
+QGLFormat QGLFormat::defaultFormat()
+{
+ return *qgl_default_format();
+}
+
+/*!
+ Sets a new default QGLFormat for the application to \a f. For
+ example, to set single buffering as the default instead of double
+ buffering, your main() might contain code like this:
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 4
+
+ \sa defaultFormat()
+*/
+
+void QGLFormat::setDefaultFormat(const QGLFormat &f)
+{
+ *qgl_default_format() = f;
+}
+
+
+/*!
+ Returns the default QGLFormat for overlay contexts.
+
+ The default overlay format is:
+ \list
+ \i \link setDoubleBuffer() Double buffer:\endlink Disabled.
+ \i \link setDepth() Depth buffer:\endlink Disabled.
+ \i \link setRgba() RGBA:\endlink Disabled (i.e., color index enabled).
+ \i \link setAlpha() Alpha channel:\endlink Disabled.
+ \i \link setAccum() Accumulator buffer:\endlink Disabled.
+ \i \link setStencil() Stencil buffer:\endlink Disabled.
+ \i \link setStereo() Stereo:\endlink Disabled.
+ \i \link setDirectRendering() Direct rendering:\endlink Enabled.
+ \i \link setOverlay() Overlay:\endlink Disabled.
+ \i \link setSampleBuffers() Multisample buffers:\endlink Disabled.
+ \i \link setPlane() Plane:\endlink 1 (i.e., first overlay plane).
+ \endlist
+
+ \sa setDefaultFormat()
+*/
+
+QGLFormat QGLFormat::defaultOverlayFormat()
+{
+ return *defaultOverlayFormatInstance();
+}
+
+/*!
+ Sets a new default QGLFormat for overlay contexts to \a f. This
+ format is used whenever a QGLWidget is created with a format that
+ hasOverlay() enabled.
+
+ For example, to get a double buffered overlay context (if
+ available), use code like this:
+
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 5
+
+ As usual, you can find out after widget creation whether the
+ underlying OpenGL system was able to provide the requested
+ specification:
+
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 6
+
+ \sa defaultOverlayFormat()
+*/
+
+void QGLFormat::setDefaultOverlayFormat(const QGLFormat &f)
+{
+ QGLFormat *defaultFormat = defaultOverlayFormatInstance();
+ *defaultFormat = f;
+ // Make sure the user doesn't request that the overlays themselves
+ // have overlays, since it is unlikely that the system supports
+ // infinitely many planes...
+ defaultFormat->setOverlay(false);
+}
+
+
+/*!
+ Returns true if all the options of the two QGLFormat objects
+ \a a and \a b are equal; otherwise returns false.
+
+ \relates QGLFormat
+*/
+
+bool operator==(const QGLFormat& a, const QGLFormat& b)
+{
+ return (a.d == b.d) || ((int) a.d->opts == (int) b.d->opts
+ && a.d->pln == b.d->pln
+ && a.d->alphaSize == b.d->alphaSize
+ && a.d->accumSize == b.d->accumSize
+ && a.d->stencilSize == b.d->stencilSize
+ && a.d->depthSize == b.d->depthSize
+ && a.d->redSize == b.d->redSize
+ && a.d->greenSize == b.d->greenSize
+ && a.d->blueSize == b.d->blueSize
+ && a.d->numSamples == b.d->numSamples
+ && a.d->swapInterval == b.d->swapInterval
+ && a.d->majorVersion == b.d->majorVersion
+ && a.d->minorVersion == b.d->minorVersion
+ && a.d->profile == b.d->profile);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QGLFormat &f)
+{
+ const QGLFormatPrivate * const d = f.d;
+
+ dbg.nospace() << "QGLFormat("
+ << "options " << d->opts
+ << ", plane " << d->pln
+ << ", depthBufferSize " << d->depthSize
+ << ", accumBufferSize " << d->accumSize
+ << ", stencilBufferSize " << d->stencilSize
+ << ", redBufferSize " << d->redSize
+ << ", greenBufferSize " << d->greenSize
+ << ", blueBufferSize " << d->blueSize
+ << ", alphaBufferSize " << d->alphaSize
+ << ", samples " << d->numSamples
+ << ", swapInterval " << d->swapInterval
+ << ", majorVersion " << d->majorVersion
+ << ", minorVersion " << d->minorVersion
+ << ", profile " << d->profile
+ << ')';
+
+ return dbg.space();
+}
+#endif
+
+
+/*!
+ Returns false if all the options of the two QGLFormat objects
+ \a a and \a b are equal; otherwise returns true.
+
+ \relates QGLFormat
+*/
+
+bool operator!=(const QGLFormat& a, const QGLFormat& b)
+{
+ return !(a == b);
+}
+
+struct QGLContextGroupList {
+ void append(QGLContextGroup *group) {
+ QMutexLocker locker(&m_mutex);
+ m_list.append(group);
+ }
+
+ void remove(QGLContextGroup *group) {
+ QMutexLocker locker(&m_mutex);
+ m_list.removeOne(group);
+ }
+
+ QList<QGLContextGroup *> m_list;
+ QMutex m_mutex;
+};
+
+Q_GLOBAL_STATIC(QGLContextGroupList, qt_context_groups)
+
+/*****************************************************************************
+ QGLContext implementation
+ *****************************************************************************/
+
+QGLContextGroup::QGLContextGroup(const QGLContext *context)
+ : m_context(context), m_guards(0), m_refs(1)
+{
+ qt_context_groups()->append(this);
+}
+
+QGLContextGroup::~QGLContextGroup()
+{
+ // Clear any remaining QGLSharedResourceGuard objects on the group.
+ QGLSharedResourceGuard *guard = m_guards;
+ while (guard != 0) {
+ guard->m_group = 0;
+ guard->m_id = 0;
+ guard = guard->m_next;
+ }
+ qt_context_groups()->remove(this);
+}
+
+void QGLContextGroup::addGuard(QGLSharedResourceGuard *guard)
+{
+ if (m_guards)
+ m_guards->m_prev = guard;
+ guard->m_next = m_guards;
+ guard->m_prev = 0;
+ m_guards = guard;
+}
+
+void QGLContextGroup::removeGuard(QGLSharedResourceGuard *guard)
+{
+ if (guard->m_next)
+ guard->m_next->m_prev = guard->m_prev;
+ if (guard->m_prev)
+ guard->m_prev->m_next = guard->m_next;
+ else
+ m_guards = guard->m_next;
+}
+
+const QGLContext *qt_gl_transfer_context(const QGLContext *ctx)
+{
+ if (!ctx)
+ return 0;
+ QList<const QGLContext *> shares
+ (QGLContextPrivate::contextGroup(ctx)->shares());
+ if (shares.size() >= 2)
+ return (ctx == shares.at(0)) ? shares.at(1) : shares.at(0);
+ else
+ return 0;
+}
+
+QGLContextPrivate::QGLContextPrivate(QGLContext *context)
+ : internal_context(false)
+ , q_ptr(context)
+{
+ group = new QGLContextGroup(context);
+ texture_destroyer = new QGLTextureDestroyer;
+ texture_destroyer->moveToThread(qApp->thread());
+}
+
+QGLContextPrivate::~QGLContextPrivate()
+{
+ if (!group->m_refs.deref()) {
+ Q_ASSERT(group->context() == q_ptr);
+ delete group;
+ }
+
+ delete texture_destroyer;
+}
+
+void QGLContextPrivate::init(QPaintDevice *dev, const QGLFormat &format)
+{
+ Q_Q(QGLContext);
+ glFormat = reqFormat = format;
+ valid = false;
+ q->setDevice(dev);
+#if defined(Q_WS_X11)
+ pbuf = 0;
+ gpm = 0;
+ vi = 0;
+ screen = QX11Info::appScreen();
+#endif
+#if defined(Q_WS_WIN)
+ dc = 0;
+ win = 0;
+ threadId = 0;
+ pixelFormatId = 0;
+ cmap = 0;
+ hbitmap = 0;
+ hbitmap_hdc = 0;
+#endif
+#if defined(Q_WS_MAC)
+# ifndef QT_MAC_USE_COCOA
+ update = false;
+# endif
+ vi = 0;
+#endif
+#if defined(Q_WS_QPA)
+ platformContext = 0;
+#endif
+#if !defined(QT_NO_EGL)
+ ownsEglContext = false;
+ eglContext = 0;
+ eglSurface = EGL_NO_SURFACE;
+#endif
+ fbo = 0;
+ crWin = false;
+ initDone = false;
+ sharing = false;
+ max_texture_size = -1;
+ version_flags_cached = false;
+ version_flags = QGLFormat::OpenGL_Version_None;
+ extension_flags_cached = false;
+ extension_flags = 0;
+ current_fbo = 0;
+ default_fbo = 0;
+ active_engine = 0;
+ workaround_needsFullClearOnEveryFrame = false;
+ workaround_brokenFBOReadBack = false;
+ workaround_brokenTexSubImage = false;
+ workaroundsCached = false;
+
+ workaround_brokenTextureFromPixmap = false;
+ workaround_brokenTextureFromPixmap_init = false;
+
+ workaround_brokenAlphaTexSubImage = false;
+ workaround_brokenAlphaTexSubImage_init = false;
+
+ for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i)
+ vertexAttributeArraysEnabledState[i] = false;
+}
+
+QGLContext* QGLContext::currentCtx = 0;
+
+/*
+ Read back the contents of the currently bound framebuffer, used in
+ QGLWidget::grabFrameBuffer(), QGLPixelbuffer::toImage() and
+ QGLFramebufferObject::toImage()
+*/
+
+static 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();
+}
+
+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;
+}
+
+QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha)
+{
+ QImage img(size, alpha_format ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
+ int w = size.width();
+ int h = size.height();
+#if !defined(QT_OPENGL_ES_2) && !defined(QT_OPENGL_ES_1)
+ //### glGetTexImage not in GL ES 2.0, need to do something else here!
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+#endif
+ convertFromGLImage(img, w, h, alpha_format, include_alpha);
+ return img;
+}
+
+// returns the highest number closest to v, which is a power of 2
+// NB! assumes 32 bit ints
+int qt_next_power_of_two(int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ ++v;
+ return v;
+}
+
+typedef void (*_qt_pixmap_cleanup_hook_64)(qint64);
+typedef void (*_qt_image_cleanup_hook_64)(qint64);
+
+extern Q_GUI_EXPORT _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64;
+extern Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64;
+
+
+Q_GLOBAL_STATIC(QGLTextureCache, qt_gl_texture_cache)
+
+QGLTextureCache::QGLTextureCache()
+ : m_cache(64*1024) // cache ~64 MB worth of textures - this is not accurate though
+{
+ QImagePixmapCleanupHooks::instance()->addPixmapDataModificationHook(cleanupTexturesForPixampData);
+ QImagePixmapCleanupHooks::instance()->addPixmapDataDestructionHook(cleanupBeforePixmapDestruction);
+ QImagePixmapCleanupHooks::instance()->addImageHook(cleanupTexturesForCacheKey);
+}
+
+QGLTextureCache::~QGLTextureCache()
+{
+ QImagePixmapCleanupHooks::instance()->removePixmapDataModificationHook(cleanupTexturesForPixampData);
+ QImagePixmapCleanupHooks::instance()->removePixmapDataDestructionHook(cleanupBeforePixmapDestruction);
+ QImagePixmapCleanupHooks::instance()->removeImageHook(cleanupTexturesForCacheKey);
+}
+
+void QGLTextureCache::insert(QGLContext* ctx, qint64 key, QGLTexture* texture, int cost)
+{
+ QWriteLocker locker(&m_lock);
+ const QGLTextureCacheKey cacheKey = {key, QGLContextPrivate::contextGroup(ctx)};
+ m_cache.insert(cacheKey, texture, cost);
+}
+
+void QGLTextureCache::remove(qint64 key)
+{
+ QWriteLocker locker(&m_lock);
+ QMutexLocker groupLocker(&qt_context_groups()->m_mutex);
+ QList<QGLContextGroup *>::const_iterator it = qt_context_groups()->m_list.constBegin();
+ while (it != qt_context_groups()->m_list.constEnd()) {
+ const QGLTextureCacheKey cacheKey = {key, *it};
+ m_cache.remove(cacheKey);
+ ++it;
+ }
+}
+
+bool QGLTextureCache::remove(QGLContext* ctx, GLuint textureId)
+{
+ QWriteLocker locker(&m_lock);
+ QList<QGLTextureCacheKey> keys = m_cache.keys();
+ for (int i = 0; i < keys.size(); ++i) {
+ QGLTexture *tex = m_cache.object(keys.at(i));
+ if (tex->id == textureId && tex->context == ctx) {
+ tex->options |= QGLContext::MemoryManagedBindOption; // forces a glDeleteTextures() call
+ m_cache.remove(keys.at(i));
+ return true;
+ }
+ }
+ return false;
+}
+
+void QGLTextureCache::removeContextTextures(QGLContext* ctx)
+{
+ QWriteLocker locker(&m_lock);
+ QList<QGLTextureCacheKey> keys = m_cache.keys();
+ for (int i = 0; i < keys.size(); ++i) {
+ const QGLTextureCacheKey &key = keys.at(i);
+ if (m_cache.object(key)->context == ctx)
+ m_cache.remove(key);
+ }
+}
+
+/*
+ a hook that removes textures from the cache when a pixmap/image
+ is deref'ed
+*/
+void QGLTextureCache::cleanupTexturesForCacheKey(qint64 cacheKey)
+{
+ qt_gl_texture_cache()->remove(cacheKey);
+}
+
+
+void QGLTextureCache::cleanupTexturesForPixampData(QPixmapData* pmd)
+{
+ cleanupTexturesForCacheKey(pmd->cacheKey());
+}
+
+void QGLTextureCache::cleanupBeforePixmapDestruction(QPixmapData* pmd)
+{
+ // Remove any bound textures first:
+ cleanupTexturesForPixampData(pmd);
+
+#if defined(Q_WS_X11)
+ if (pmd->classId() == QPixmapData::X11Class) {
+ Q_ASSERT(pmd->ref == 0); // Make sure reference counting isn't broken
+ QGLContextPrivate::destroyGlSurfaceForPixmap(pmd);
+ }
+#endif
+}
+
+QGLTextureCache *QGLTextureCache::instance()
+{
+ return qt_gl_texture_cache();
+}
+
+// DDS format structure
+struct DDSFormat {
+ quint32 dwSize;
+ quint32 dwFlags;
+ quint32 dwHeight;
+ quint32 dwWidth;
+ quint32 dwLinearSize;
+ quint32 dummy1;
+ quint32 dwMipMapCount;
+ quint32 dummy2[11];
+ struct {
+ quint32 dummy3[2];
+ quint32 dwFourCC;
+ quint32 dummy4[5];
+ } ddsPixelFormat;
+};
+
+// compressed texture pixel formats
+#define FOURCC_DXT1 0x31545844
+#define FOURCC_DXT2 0x32545844
+#define FOURCC_DXT3 0x33545844
+#define FOURCC_DXT4 0x34545844
+#define FOURCC_DXT5 0x35545844
+
+#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT
+#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
+#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#endif
+
+#ifndef GL_GENERATE_MIPMAP_SGIS
+#define GL_GENERATE_MIPMAP_SGIS 0x8191
+#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192
+#endif
+
+/*!
+ \class QGLContext
+ \brief The QGLContext class encapsulates an OpenGL rendering context.
+
+ \ingroup painting-3D
+
+ An OpenGL rendering context is a complete set of OpenGL state
+ variables. The rendering context's \l {QGL::FormatOption} {format}
+ is set in the constructor, but it can also be set later with
+ setFormat(). The format options that are actually set are returned
+ by format(); the options you asked for are returned by
+ requestedFormat(). Note that after a QGLContext object has been
+ constructed, the actual OpenGL context must be created by
+ explicitly calling the \link create() create()\endlink
+ function. The makeCurrent() function makes this context the
+ current rendering context. You can make \e no context current
+ using doneCurrent(). The reset() function will reset the context
+ and make it invalid.
+
+ You can examine properties of the context with, e.g. isValid(),
+ isSharing(), initialized(), windowCreated() and
+ overlayTransparentColor().
+
+ If you're using double buffering you can swap the screen contents
+ with the off-screen buffer using swapBuffers().
+
+ Please note that QGLContext is not thread safe.
+*/
+
+/*!
+ \enum QGLContext::BindOption
+ \since 4.6
+
+ A set of options to decide how to bind a texture using bindTexture().
+
+ \value NoBindOption Don't do anything, pass the texture straight
+ through.
+
+ \value InvertedYBindOption Specifies that the texture should be flipped
+ over the X axis so that the texture coordinate 0,0 corresponds to
+ the top left corner. Inverting the texture implies a deep copy
+ prior to upload.
+
+ \value MipmapBindOption Specifies that bindTexture() should try
+ to generate mipmaps. If the GL implementation supports the \c
+ GL_SGIS_generate_mipmap extension, mipmaps will be automatically
+ generated for the texture. Mipmap generation is only supported for
+ the \c GL_TEXTURE_2D target.
+
+ \value PremultipliedAlphaBindOption Specifies that the image should be
+ uploaded with premultiplied alpha and does a conversion accordingly.
+
+ \value LinearFilteringBindOption Specifies that the texture filtering
+ should be set to GL_LINEAR. Default is GL_NEAREST. If mipmap is
+ also enabled, filtering will be set to GL_LINEAR_MIPMAP_LINEAR.
+
+ \value DefaultBindOption In Qt 4.5 and earlier, bindTexture()
+ would mirror the image and automatically generate mipmaps. This
+ option helps preserve this default behavior.
+
+ \omitvalue CanFlipNativePixmapBindOption Used by x11 from pixmap to choose
+ whether or not it can bind the pixmap upside down or not.
+
+ \omitvalue MemoryManagedBindOption Used by paint engines to
+ indicate that the pixmap should be memory managed along side with
+ the pixmap/image that it stems from, e.g. installing destruction
+ hooks in them.
+
+ \omitvalue TemporarilyCachedBindOption Used by paint engines on some
+ platforms to indicate that the pixmap or image texture is possibly
+ cached only temporarily and must be destroyed immediately after the use.
+
+ \omitvalue InternalBindOption
+*/
+
+/*!
+ \obsolete
+
+ Constructs an OpenGL context for the given paint \a device, which
+ can be a widget or a pixmap. The \a format specifies several
+ display options for the context.
+
+ If the underlying OpenGL/Window system cannot satisfy all the
+ features requested in \a format, the nearest subset of features
+ will be used. After creation, the format() method will return the
+ actual format obtained.
+
+ Note that after a QGLContext object has been constructed, \l
+ create() must be called explicitly to create the actual OpenGL
+ context. The context will be \l {isValid()}{invalid} if it was not
+ possible to obtain a GL context at all.
+*/
+
+QGLContext::QGLContext(const QGLFormat &format, QPaintDevice *device)
+ : d_ptr(new QGLContextPrivate(this))
+{
+ Q_D(QGLContext);
+ d->init(device, format);
+}
+
+/*!
+ Constructs an OpenGL context with the given \a format which
+ specifies several display options for the context.
+
+ If the underlying OpenGL/Window system cannot satisfy all the
+ features requested in \a format, the nearest subset of features
+ will be used. After creation, the format() method will return the
+ actual format obtained.
+
+ Note that after a QGLContext object has been constructed, \l
+ create() must be called explicitly to create the actual OpenGL
+ context. The context will be \l {isValid()}{invalid} if it was not
+ possible to obtain a GL context at all.
+
+ \sa format(), isValid()
+*/
+QGLContext::QGLContext(const QGLFormat &format)
+ : d_ptr(new QGLContextPrivate(this))
+{
+ Q_D(QGLContext);
+ d->init(0, format);
+}
+
+/*!
+ Destroys the OpenGL context and frees its resources.
+*/
+
+QGLContext::~QGLContext()
+{
+ // remove any textures cached in this context
+ QGLTextureCache::instance()->removeContextTextures(this);
+
+ // clean up resources specific to this context
+ d_ptr->cleanup();
+ // clean up resources belonging to this context's group
+ d_ptr->group->cleanupResources(this);
+
+ QGLSignalProxy::instance()->emitAboutToDestroyContext(this);
+ reset();
+}
+
+void QGLContextPrivate::cleanup()
+{
+ QHash<QGLContextResourceBase *, void *>::ConstIterator it;
+ for (it = m_resources.begin(); it != m_resources.end(); ++it)
+ it.key()->freeResource(it.value());
+ m_resources.clear();
+}
+
+#define ctx q_ptr
+void QGLContextPrivate::setVertexAttribArrayEnabled(int arrayIndex, bool enabled)
+{
+ Q_ASSERT(arrayIndex < QT_GL_VERTEX_ARRAY_TRACKED_COUNT);
+#ifdef glEnableVertexAttribArray
+ Q_ASSERT(glEnableVertexAttribArray);
+#endif
+
+ if (vertexAttributeArraysEnabledState[arrayIndex] && !enabled)
+ glDisableVertexAttribArray(arrayIndex);
+
+ if (!vertexAttributeArraysEnabledState[arrayIndex] && enabled)
+ glEnableVertexAttribArray(arrayIndex);
+
+ vertexAttributeArraysEnabledState[arrayIndex] = enabled;
+}
+
+void QGLContextPrivate::syncGlState()
+{
+#ifdef glEnableVertexAttribArray
+ Q_ASSERT(glEnableVertexAttribArray);
+#endif
+ for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i) {
+ if (vertexAttributeArraysEnabledState[i])
+ glEnableVertexAttribArray(i);
+ else
+ glDisableVertexAttribArray(i);
+ }
+
+}
+#undef ctx
+
+#ifdef QT_NO_EGL
+void QGLContextPrivate::swapRegion(const QRegion &)
+{
+ Q_Q(QGLContext);
+ q->swapBuffers();
+}
+#endif
+
+/*!
+ \overload
+
+ Reads the compressed texture file \a fileName and generates a 2D GL
+ texture from it.
+
+ This function can load DirectDrawSurface (DDS) textures in the
+ DXT1, DXT3 and DXT5 DDS formats if the \c GL_ARB_texture_compression
+ and \c GL_EXT_texture_compression_s3tc extensions are supported.
+
+ Since 4.6.1, textures in the ETC1 format can be loaded if the
+ \c GL_OES_compressed_ETC1_RGB8_texture extension is supported
+ and the ETC1 texture has been encapsulated in the PVR container format.
+ Also, textures in the PVRTC2 and PVRTC4 formats can be loaded
+ if the \c GL_IMG_texture_compression_pvrtc extension is supported.
+
+ \sa deleteTexture()
+*/
+
+GLuint QGLContext::bindTexture(const QString &fileName)
+{
+ QGLTexture texture(this);
+ QSize size = texture.bindCompressedTexture(fileName);
+ if (!size.isValid())
+ return 0;
+ return texture.id;
+}
+
+static inline QRgb qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format)
+{
+ if (texture_format == GL_BGRA) {
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ return ((src_pixel << 24) & 0xff000000)
+ | ((src_pixel >> 24) & 0x000000ff)
+ | ((src_pixel << 8) & 0x00ff0000)
+ | ((src_pixel >> 8) & 0x0000ff00);
+ } else {
+ return src_pixel;
+ }
+ } else { // GL_RGBA
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ return (src_pixel << 8) | ((src_pixel >> 24) & 0xff);
+ } else {
+ return ((src_pixel << 16) & 0xff0000)
+ | ((src_pixel >> 16) & 0xff)
+ | (src_pixel & 0xff00ff00);
+ }
+ }
+}
+
+QRgb qt_gl_convertToGLFormat(QRgb src_pixel, GLenum texture_format)
+{
+ return qt_gl_convertToGLFormatHelper(src_pixel, texture_format);
+}
+
+static void convertToGLFormatHelper(QImage &dst, const QImage &img, GLenum texture_format)
+{
+ Q_ASSERT(dst.depth() == 32);
+ Q_ASSERT(img.depth() == 32);
+
+ if (dst.size() != img.size()) {
+ int target_width = dst.width();
+ int target_height = dst.height();
+ qreal sx = target_width / qreal(img.width());
+ qreal sy = target_height / qreal(img.height());
+
+ quint32 *dest = (quint32 *) dst.scanLine(0); // NB! avoid detach here
+ uchar *srcPixels = (uchar *) img.scanLine(img.height() - 1);
+ int sbpl = img.bytesPerLine();
+ int dbpl = dst.bytesPerLine();
+
+ int ix = int(0x00010000 / sx);
+ int iy = int(0x00010000 / sy);
+
+ quint32 basex = int(0.5 * ix);
+ quint32 srcy = int(0.5 * iy);
+
+ // scale, swizzle and mirror in one loop
+ while (target_height--) {
+ const uint *src = (const quint32 *) (srcPixels - (srcy >> 16) * sbpl);
+ int srcx = basex;
+ for (int x=0; x<target_width; ++x) {
+ dest[x] = qt_gl_convertToGLFormatHelper(src[srcx >> 16], texture_format);
+ srcx += ix;
+ }
+ dest = (quint32 *)(((uchar *) dest) + dbpl);
+ srcy += iy;
+ }
+ } else {
+ const int width = img.width();
+ const int height = img.height();
+ const uint *p = (const uint*) img.scanLine(img.height() - 1);
+ uint *q = (uint*) dst.scanLine(0);
+
+ if (texture_format == GL_BGRA) {
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ // mirror + swizzle
+ for (int i=0; i < height; ++i) {
+ const uint *end = p + width;
+ while (p < end) {
+ *q = ((*p << 24) & 0xff000000)
+ | ((*p >> 24) & 0x000000ff)
+ | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00);
+ p++;
+ q++;
+ }
+ p -= 2 * width;
+ }
+ } else {
+ const uint bytesPerLine = img.bytesPerLine();
+ for (int i=0; i < height; ++i) {
+ memcpy(q, p, bytesPerLine);
+ q += width;
+ p -= width;
+ }
+ }
+ } else {
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ for (int i=0; i < height; ++i) {
+ const uint *end = p + width;
+ while (p < end) {
+ *q = (*p << 8) | ((*p >> 24) & 0xff);
+ p++;
+ q++;
+ }
+ p -= 2 * width;
+ }
+ } else {
+ for (int i=0; i < height; ++i) {
+ const uint *end = p + width;
+ while (p < end) {
+ *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00);
+ p++;
+ q++;
+ }
+ p -= 2 * width;
+ }
+ }
+ }
+ }
+}
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_SYMBIAN)
+QGLExtensionFuncs& QGLContextPrivate::extensionFuncs(const QGLContext *)
+{
+ return qt_extensionFuncs;
+}
+#endif
+
+QImage QGLContextPrivate::convertToGLFormat(const QImage &image, bool force_premul,
+ GLenum texture_format)
+{
+ QImage::Format target_format = image.format();
+ if (force_premul || image.format() != QImage::Format_ARGB32)
+ target_format = QImage::Format_ARGB32_Premultiplied;
+
+ QImage result(image.width(), image.height(), target_format);
+ convertToGLFormatHelper(result, image.convertToFormat(target_format), texture_format);
+ return result;
+}
+
+/*! \internal */
+QGLTexture *QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format,
+ QGLContext::BindOptions options)
+{
+ Q_Q(QGLContext);
+
+ const qint64 key = image.cacheKey();
+ QGLTexture *texture = textureCacheLookup(key, target);
+ if (texture) {
+ if (image.paintingActive()) {
+ // A QPainter is active on the image - take the safe route and replace the texture.
+ q->deleteTexture(texture->id);
+ texture = 0;
+ } else {
+ glBindTexture(target, texture->id);
+ return texture;
+ }
+ }
+
+ if (!texture)
+ texture = bindTexture(image, target, format, key, options);
+ // NOTE: bindTexture(const QImage&, GLenum, GLint, const qint64, bool) should never return null
+ Q_ASSERT(texture);
+
+ // Enable the cleanup hooks for this image so that the texture cache entry is removed when the
+ // image gets deleted:
+ QImagePixmapCleanupHooks::enableCleanupHooks(image);
+
+ return texture;
+}
+
+// #define QGL_BIND_TEXTURE_DEBUG
+
+// map from Qt's ARGB endianness-dependent format to GL's big-endian RGBA layout
+static inline void qgl_byteSwapImage(QImage &img, GLenum pixel_type)
+{
+ const int width = img.width();
+ const int height = img.height();
+
+ if (pixel_type == GL_UNSIGNED_INT_8_8_8_8_REV
+ || (pixel_type == GL_UNSIGNED_BYTE && QSysInfo::ByteOrder == QSysInfo::LittleEndian))
+ {
+ for (int i = 0; i < height; ++i) {
+ uint *p = (uint *) img.scanLine(i);
+ for (int x = 0; x < width; ++x)
+ p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
+ }
+ } else {
+ for (int i = 0; i < height; ++i) {
+ uint *p = (uint *) img.scanLine(i);
+ for (int x = 0; x < width; ++x)
+ p[x] = (p[x] << 8) | ((p[x] >> 24) & 0xff);
+ }
+ }
+}
+
+QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint internalFormat,
+ const qint64 key, QGLContext::BindOptions options)
+{
+ Q_Q(QGLContext);
+
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf("QGLContextPrivate::bindTexture(), imageSize=(%d,%d), internalFormat =0x%x, options=%x, key=%llx\n",
+ image.width(), image.height(), internalFormat, int(options), key);
+ QTime time;
+ time.start();
+#endif
+
+#ifndef QT_NO_DEBUG
+ // Reset the gl error stack...git
+ while (glGetError() != GL_NO_ERROR) ;
+#endif
+
+ // Scale the pixmap if needed. GL textures needs to have the
+ // dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL
+ // 2.0 or use the GL_TEXTURE_RECTANGLE texture target
+ int tx_w = qt_next_power_of_two(image.width());
+ int tx_h = qt_next_power_of_two(image.height());
+
+ QImage img = image;
+
+ if (!(QGLExtensions::glExtensions() & QGLExtensions::NPOTTextures)
+ && !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0)
+ && (target == GL_TEXTURE_2D && (tx_w != image.width() || tx_h != image.height())))
+ {
+ img = img.scaled(tx_w, tx_h);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - upscaled to %dx%d (%d ms)\n", tx_w, tx_h, time.elapsed());
+
+#endif
+ }
+
+ GLuint filtering = options & QGLContext::LinearFilteringBindOption ? GL_LINEAR : GL_NEAREST;
+
+ GLuint tx_id;
+ glGenTextures(1, &tx_id);
+ glBindTexture(target, tx_id);
+ glTexParameterf(target, GL_TEXTURE_MAG_FILTER, filtering);
+
+#if defined(QT_OPENGL_ES_2)
+ bool genMipmap = false;
+#endif
+ if (glFormat.directRendering()
+ && (QGLExtensions::glExtensions() & QGLExtensions::GenerateMipmap)
+ && target == GL_TEXTURE_2D
+ && (options & QGLContext::MipmapBindOption))
+ {
+#if !defined(QT_OPENGL_ES_2)
+ glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
+#ifndef QT_OPENGL_ES
+ glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
+#else
+ glTexParameterf(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
+#endif
+#else
+ glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
+ genMipmap = true;
+#endif
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, options & QGLContext::LinearFilteringBindOption
+ ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - generating mipmaps (%d ms)\n", time.elapsed());
+#endif
+ } else {
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, filtering);
+ }
+
+ QImage::Format target_format = img.format();
+ bool premul = options & QGLContext::PremultipliedAlphaBindOption;
+ GLenum externalFormat;
+ GLuint pixel_type;
+ if (QGLExtensions::glExtensions() & QGLExtensions::BGRATextureFormat) {
+ externalFormat = GL_BGRA;
+ if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2)
+ pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ else
+ pixel_type = GL_UNSIGNED_BYTE;
+ } else {
+ externalFormat = GL_RGBA;
+ pixel_type = GL_UNSIGNED_BYTE;
+ }
+
+ switch (target_format) {
+ case QImage::Format_ARGB32:
+ if (premul) {
+ img = img.convertToFormat(target_format = QImage::Format_ARGB32_Premultiplied);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - converted ARGB32 -> ARGB32_Premultiplied (%d ms) \n", time.elapsed());
+#endif
+ }
+ break;
+ case QImage::Format_ARGB32_Premultiplied:
+ if (!premul) {
+ img = img.convertToFormat(target_format = QImage::Format_ARGB32);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - converted ARGB32_Premultiplied -> ARGB32 (%d ms)\n", time.elapsed());
+#endif
+ }
+ break;
+ case QImage::Format_RGB16:
+ pixel_type = GL_UNSIGNED_SHORT_5_6_5;
+ externalFormat = GL_RGB;
+ internalFormat = GL_RGB;
+ break;
+ case QImage::Format_RGB32:
+ break;
+ default:
+ if (img.hasAlphaChannel()) {
+ img = img.convertToFormat(premul
+ ? QImage::Format_ARGB32_Premultiplied
+ : QImage::Format_ARGB32);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - converted to 32-bit alpha format (%d ms)\n", time.elapsed());
+#endif
+ } else {
+ img = img.convertToFormat(QImage::Format_RGB32);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - converted to 32-bit (%d ms)\n", time.elapsed());
+#endif
+ }
+ }
+
+ if (options & QGLContext::InvertedYBindOption) {
+ if (img.isDetached()) {
+ int ipl = img.bytesPerLine() / 4;
+ int h = img.height();
+ for (int y=0; y<h/2; ++y) {
+ int *a = (int *) img.scanLine(y);
+ int *b = (int *) img.scanLine(h - y - 1);
+ for (int x=0; x<ipl; ++x)
+ qSwap(a[x], b[x]);
+ }
+ } else {
+ // Create a new image and copy across. If we use the
+ // above in-place code then a full copy of the image is
+ // made before the lines are swapped, which processes the
+ // data twice. This version should only do it once.
+ img = img.mirrored();
+ }
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - flipped bits over y (%d ms)\n", time.elapsed());
+#endif
+ }
+
+ if (externalFormat == GL_RGBA) {
+ // The only case where we end up with a depth different from
+ // 32 in the switch above is for the RGB16 case, where we set
+ // the format to GL_RGB
+ Q_ASSERT(img.depth() == 32);
+ qgl_byteSwapImage(img, pixel_type);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - did byte swapping (%d ms)\n", time.elapsed());
+#endif
+ }
+#ifdef QT_OPENGL_ES
+ // OpenGL/ES requires that the internal and external formats be
+ // identical.
+ internalFormat = externalFormat;
+#endif
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - uploading, image.format=%d, externalFormat=0x%x, internalFormat=0x%x, pixel_type=0x%x\n",
+ img.format(), externalFormat, internalFormat, pixel_type);
+#endif
+
+ const QImage &constRef = img; // to avoid detach in bits()...
+#ifdef QGL_USE_TEXTURE_POOL
+ QGLTexturePool::instance()->createPermanentTexture(tx_id,
+ target,
+ 0, internalFormat,
+ img.width(), img.height(),
+ externalFormat,
+ pixel_type,
+ constRef.bits());
+#else
+ glTexImage2D(target, 0, internalFormat, img.width(), img.height(), 0, externalFormat,
+ pixel_type, constRef.bits());
+#endif
+#if defined(QT_OPENGL_ES_2)
+ if (genMipmap)
+ glGenerateMipmap(target);
+#endif
+#ifndef QT_NO_DEBUG
+ GLenum error = glGetError();
+ if (error != GL_NO_ERROR) {
+ qWarning(" - texture upload failed, error code 0x%x, enum: %d (%x)\n", error, target, target);
+ }
+#endif
+
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ static int totalUploadTime = 0;
+ totalUploadTime += time.elapsed();
+ printf(" - upload done in %d ms, (accumulated: %d ms)\n", time.elapsed(), totalUploadTime);
+#endif
+
+
+ // this assumes the size of a texture is always smaller than the max cache size
+ int cost = img.width()*img.height()*4/1024;
+ QGLTexture *texture = new QGLTexture(q, tx_id, target, options);
+ QGLTextureCache::instance()->insert(q, key, texture, cost);
+
+ return texture;
+}
+
+QGLTexture *QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum target)
+{
+ Q_Q(QGLContext);
+ QGLTexture *texture = QGLTextureCache::instance()->getTexture(q, key);
+ if (texture && texture->target == target
+ && (texture->context == q || QGLContext::areSharing(q, texture->context)))
+ {
+ return texture;
+ }
+ return 0;
+}
+
+/*! \internal */
+QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, QGLContext::BindOptions options)
+{
+ Q_Q(QGLContext);
+ QPixmapData *pd = pixmap.pixmapData();
+#if !defined(QT_OPENGL_ES_1)
+ if (target == GL_TEXTURE_2D && pd->classId() == QPixmapData::OpenGLClass) {
+ const QGLPixmapData *data = static_cast<const QGLPixmapData *>(pd);
+
+ if (data->isValidContext(q)) {
+ data->bind();
+ return data->texture();
+ }
+ }
+#else
+ Q_UNUSED(pd);
+#endif
+
+ const qint64 key = pixmap.cacheKey();
+ QGLTexture *texture = textureCacheLookup(key, target);
+ if (texture) {
+ if (pixmap.paintingActive()) {
+ // A QPainter is active on the pixmap - take the safe route and replace the texture.
+ q->deleteTexture(texture->id);
+ texture = 0;
+ } else {
+ glBindTexture(target, texture->id);
+ return texture;
+ }
+ }
+
+#if defined(Q_WS_X11)
+ // Try to use texture_from_pixmap
+ const QX11Info *xinfo = qt_x11Info(paintDevice);
+ if (pd->classId() == QPixmapData::X11Class && pd->pixelType() == QPixmapData::PixmapType
+ && xinfo && xinfo->screen() == pixmap.x11Info().screen()
+ && target == GL_TEXTURE_2D
+ && QApplication::instance()->thread() == QThread::currentThread())
+ {
+ if (!workaround_brokenTextureFromPixmap_init) {
+ workaround_brokenTextureFromPixmap_init = true;
+
+ const QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
+ const int pos = versionString.indexOf("NVIDIA ");
+
+ if (pos >= 0) {
+ const QByteArray nvidiaVersionString = versionString.mid(pos + strlen("NVIDIA "));
+
+ if (nvidiaVersionString.startsWith("195") || nvidiaVersionString.startsWith("256"))
+ workaround_brokenTextureFromPixmap = true;
+ }
+ }
+
+ if (!workaround_brokenTextureFromPixmap) {
+ texture = bindTextureFromNativePixmap(const_cast<QPixmap*>(&pixmap), key, options);
+ if (texture) {
+ texture->options |= QGLContext::MemoryManagedBindOption;
+ texture->boundPixmap = pd;
+ boundPixmaps.insert(pd, QPixmap(pixmap));
+ }
+ }
+ }
+#endif
+
+ if (!texture) {
+ QImage image = pixmap.toImage();
+ // If the system depth is 16 and the pixmap doesn't have an alpha channel
+ // then we convert it to RGB16 in the hope that it gets uploaded as a 16
+ // bit texture which is much faster to access than a 32-bit one.
+ if (pixmap.depth() == 16 && !image.hasAlphaChannel() )
+ image = image.convertToFormat(QImage::Format_RGB16);
+ texture = bindTexture(image, target, format, key, options);
+ }
+ // NOTE: bindTexture(const QImage&, GLenum, GLint, const qint64, bool) should never return null
+ Q_ASSERT(texture);
+
+ if (texture->id > 0)
+ QImagePixmapCleanupHooks::enableCleanupHooks(pixmap);
+
+ return texture;
+}
+
+/*! \internal */
+int QGLContextPrivate::maxTextureSize()
+{
+ if (max_texture_size != -1)
+ return max_texture_size;
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
+
+#if defined(QT_OPENGL_ES)
+ return max_texture_size;
+#else
+ GLenum proxy = GL_PROXY_TEXTURE_2D;
+
+ GLint size;
+ GLint next = 64;
+ glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &size);
+ if (size == 0) {
+ return max_texture_size;
+ }
+ do {
+ size = next;
+ next = size * 2;
+
+ if (next > max_texture_size)
+ break;
+ glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &next);
+ } while (next > size);
+
+ max_texture_size = size;
+ return max_texture_size;
+#endif
+}
+
+/*!
+ Generates and binds a 2D GL texture to the current context, based
+ on \a image. The generated texture id is returned and can be used in
+ later \c glBindTexture() calls.
+
+ \overload
+*/
+GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format)
+{
+ if (image.isNull())
+ return 0;
+
+ Q_D(QGLContext);
+ QGLTexture *texture = d->bindTexture(image, target, format, DefaultBindOption);
+ return texture->id;
+}
+
+/*!
+ \since 4.6
+
+ Generates and binds a 2D GL texture to the current context, based
+ on \a image. The generated texture id is returned and can be used
+ in later \c glBindTexture() calls.
+
+ The \a target parameter specifies the texture target. The default
+ target is \c GL_TEXTURE_2D.
+
+ The \a format parameter sets the internal format for the
+ texture. The default format is \c GL_RGBA.
+
+ The binding \a options are a set of options used to decide how to
+ bind the texture to the context.
+
+ The texture that is generated is cached, so multiple calls to
+ bindTexture() with the same QImage will return the same texture
+ id.
+
+ Note that we assume default values for the glPixelStore() and
+ glPixelTransfer() parameters.
+
+ \sa deleteTexture()
+*/
+GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format, BindOptions options)
+{
+ if (image.isNull())
+ return 0;
+
+ Q_D(QGLContext);
+ QGLTexture *texture = d->bindTexture(image, target, format, options);
+ return texture->id;
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format)
+{
+ if (image.isNull())
+ return 0;
+
+ Q_D(QGLContext);
+ QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), DefaultBindOption);
+ return texture->id;
+}
+
+/*! \internal */
+GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format,
+ BindOptions options)
+{
+ if (image.isNull())
+ return 0;
+
+ Q_D(QGLContext);
+ QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), options);
+ return texture->id;
+}
+#endif
+
+/*! \overload
+
+ Generates and binds a 2D GL texture based on \a pixmap.
+*/
+GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format)
+{
+ if (pixmap.isNull())
+ return 0;
+
+ Q_D(QGLContext);
+ QGLTexture *texture = d->bindTexture(pixmap, target, format, DefaultBindOption);
+ return texture->id;
+}
+
+/*!
+ \overload
+ \since 4.6
+
+ Generates and binds a 2D GL texture to the current context, based
+ on \a pixmap.
+*/
+GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, BindOptions options)
+{
+ if (pixmap.isNull())
+ return 0;
+
+ Q_D(QGLContext);
+ QGLTexture *texture = d->bindTexture(pixmap, target, format, options);
+ return texture->id;
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format)
+{
+ if (pixmap.isNull())
+ return 0;
+
+ Q_D(QGLContext);
+ QGLTexture *texture = d->bindTexture(pixmap, GLenum(target), GLint(format), DefaultBindOption);
+ return texture->id;
+}
+/*! \internal */
+GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format,
+ BindOptions options)
+{
+ if (pixmap.isNull())
+ return 0;
+
+ Q_D(QGLContext);
+ QGLTexture *texture = d->bindTexture(pixmap, GLenum(target), GLint(format), options);
+ return texture->id;
+}
+#endif
+
+/*!
+ Removes the texture identified by \a id from the texture cache,
+ and calls glDeleteTextures() to delete the texture from the
+ context.
+
+ \sa bindTexture()
+*/
+void QGLContext::deleteTexture(GLuint id)
+{
+ if (QGLTextureCache::instance()->remove(this, id))
+ return;
+ glDeleteTextures(1, &id);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLContext::deleteTexture(QMacCompatGLuint id)
+{
+ return deleteTexture(GLuint(id));
+}
+#endif
+
+void qt_add_rect_to_array(const QRectF &r, GLfloat *array)
+{
+ qreal left = r.left();
+ qreal right = r.right();
+ qreal top = r.top();
+ qreal bottom = r.bottom();
+
+ array[0] = left;
+ array[1] = top;
+ array[2] = right;
+ array[3] = top;
+ array[4] = right;
+ array[5] = bottom;
+ array[6] = left;
+ array[7] = bottom;
+}
+
+void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, GLfloat *array)
+{
+ array[0] = x1;
+ array[1] = y1;
+ array[2] = x2;
+ array[3] = y1;
+ array[4] = x2;
+ array[5] = y2;
+ array[6] = x1;
+ array[7] = y2;
+}
+
+#if !defined(QT_OPENGL_ES_2)
+
+static void qDrawTextureRect(const QRectF &target, GLint textureWidth, GLint textureHeight, GLenum textureTarget)
+{
+ GLfloat tx = 1.0f;
+ GLfloat ty = 1.0f;
+
+#ifdef QT_OPENGL_ES
+ Q_UNUSED(textureWidth);
+ Q_UNUSED(textureHeight);
+ Q_UNUSED(textureTarget);
+#else
+ if (textureTarget != GL_TEXTURE_2D) {
+ if (textureWidth == -1 || textureHeight == -1) {
+ glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_WIDTH, &textureWidth);
+ glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_HEIGHT, &textureHeight);
+ }
+
+ tx = GLfloat(textureWidth);
+ ty = GLfloat(textureHeight);
+ }
+#endif
+
+ GLfloat texCoordArray[4*2] = {
+ 0, ty, tx, ty, tx, 0, 0, 0
+ };
+
+ GLfloat vertexArray[4*2];
+ qt_add_rect_to_array(target, vertexArray);
+
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+#endif // !QT_OPENGL_ES_2
+
+/*!
+ \since 4.4
+
+ This function supports the following use cases:
+
+ \list
+ \i On OpenGL and OpenGL ES 1.x it draws the given texture, \a textureId,
+ to the given target rectangle, \a target, in OpenGL model space. The
+ \a textureTarget should be a 2D texture target.
+ \i On OpenGL and OpenGL ES 2.x, if a painter is active, not inside a
+ beginNativePainting / endNativePainting block, and uses the
+ engine with type QPaintEngine::OpenGL2, the function will draw the given
+ texture, \a textureId, to the given target rectangle, \a target,
+ respecting the current painter state. This will let you draw a texture
+ with the clip, transform, render hints, and composition mode set by the
+ painter. Note that the texture target needs to be GL_TEXTURE_2D for this
+ use case, and that this is the only supported use case under OpenGL ES 2.x.
+ \endlist
+
+*/
+void QGLContext::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
+{
+#if !defined(QT_OPENGL_ES) || defined(QT_OPENGL_ES_2)
+ if (d_ptr->active_engine &&
+ d_ptr->active_engine->type() == QPaintEngine::OpenGL2) {
+ QGL2PaintEngineEx *eng = static_cast<QGL2PaintEngineEx*>(d_ptr->active_engine);
+ if (!eng->isNativePaintingActive()) {
+ QRectF src(0, 0, target.width(), target.height());
+ QSize size(target.width(), target.height());
+ if (eng->drawTexture(target, textureId, size, src))
+ return;
+ }
+ }
+#endif
+
+#ifndef QT_OPENGL_ES_2
+#ifdef QT_OPENGL_ES
+ if (textureTarget != GL_TEXTURE_2D) {
+ qWarning("QGLContext::drawTexture(): texture target must be GL_TEXTURE_2D on OpenGL ES");
+ return;
+ }
+#else
+ const bool wasEnabled = glIsEnabled(GL_TEXTURE_2D);
+ GLint oldTexture;
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTexture);
+#endif
+
+ glEnable(textureTarget);
+ glBindTexture(textureTarget, textureId);
+
+ qDrawTextureRect(target, -1, -1, textureTarget);
+
+#ifdef QT_OPENGL_ES
+ glDisable(textureTarget);
+#else
+ if (!wasEnabled)
+ glDisable(textureTarget);
+ glBindTexture(textureTarget, oldTexture);
+#endif
+#else
+ Q_UNUSED(target);
+ Q_UNUSED(textureId);
+ Q_UNUSED(textureTarget);
+ qWarning("drawTexture() with OpenGL ES 2.0 requires an active OpenGL2 paint engine");
+#endif
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLContext::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ drawTexture(target, GLuint(textureId), GLenum(textureTarget));
+}
+#endif
+
+/*!
+ \since 4.4
+
+ This function supports the following use cases:
+
+ \list
+ \i By default it draws the given texture, \a textureId,
+ at the given \a point in OpenGL model space. The
+ \a textureTarget should be a 2D texture target.
+ \i If a painter is active, not inside a
+ beginNativePainting / endNativePainting block, and uses the
+ engine with type QPaintEngine::OpenGL2, the function will draw the given
+ texture, \a textureId, at the given \a point,
+ respecting the current painter state. This will let you draw a texture
+ with the clip, transform, render hints, and composition mode set by the
+ painter. Note that the texture target needs to be GL_TEXTURE_2D for this
+ use case.
+ \endlist
+
+ \note This function is not supported under any version of OpenGL ES.
+*/
+void QGLContext::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
+{
+#ifdef QT_OPENGL_ES
+ Q_UNUSED(point);
+ Q_UNUSED(textureId);
+ Q_UNUSED(textureTarget);
+ qWarning("drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget) not supported with OpenGL ES, use rect version instead");
+#else
+
+ const bool wasEnabled = glIsEnabled(GL_TEXTURE_2D);
+ GLint oldTexture;
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTexture);
+
+ glEnable(textureTarget);
+ glBindTexture(textureTarget, textureId);
+
+ GLint textureWidth;
+ GLint textureHeight;
+
+ glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_WIDTH, &textureWidth);
+ glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_HEIGHT, &textureHeight);
+
+ if (d_ptr->active_engine &&
+ d_ptr->active_engine->type() == QPaintEngine::OpenGL2) {
+ QGL2PaintEngineEx *eng = static_cast<QGL2PaintEngineEx*>(d_ptr->active_engine);
+ if (!eng->isNativePaintingActive()) {
+ QRectF dest(point, QSizeF(textureWidth, textureHeight));
+ QRectF src(0, 0, textureWidth, textureHeight);
+ QSize size(textureWidth, textureHeight);
+ if (eng->drawTexture(dest, textureId, size, src))
+ return;
+ }
+ }
+
+ qDrawTextureRect(QRectF(point, QSizeF(textureWidth, textureHeight)), textureWidth, textureHeight, textureTarget);
+
+ if (!wasEnabled)
+ glDisable(textureTarget);
+ glBindTexture(textureTarget, oldTexture);
+#endif
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLContext::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ drawTexture(point, GLuint(textureId), GLenum(textureTarget));
+}
+#endif
+
+
+/*!
+ This function sets the limit for the texture cache to \a size,
+ expressed in kilobytes.
+
+ By default, the cache limit is approximately 64 MB.
+
+ \sa textureCacheLimit()
+*/
+void QGLContext::setTextureCacheLimit(int size)
+{
+ QGLTextureCache::instance()->setMaxCost(size);
+}
+
+/*!
+ Returns the current texture cache limit in kilobytes.
+
+ \sa setTextureCacheLimit()
+*/
+int QGLContext::textureCacheLimit()
+{
+ return QGLTextureCache::instance()->maxCost();
+}
+
+
+/*!
+ \fn QGLFormat QGLContext::format() const
+
+ Returns the frame buffer format that was obtained (this may be a
+ subset of what was requested).
+
+ \sa requestedFormat()
+*/
+
+/*!
+ \fn QGLFormat QGLContext::requestedFormat() const
+
+ Returns the frame buffer format that was originally requested in
+ the constructor or setFormat().
+
+ \sa format()
+*/
+
+/*!
+ Sets a \a format for this context. The context is \link reset()
+ reset\endlink.
+
+ Call create() to create a new GL context that tries to match the
+ new format.
+
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 7
+
+ \sa format(), reset(), create()
+*/
+
+void QGLContext::setFormat(const QGLFormat &format)
+{
+ Q_D(QGLContext);
+ reset();
+ d->glFormat = d->reqFormat = format;
+}
+
+/*!
+ \internal
+*/
+void QGLContext::setDevice(QPaintDevice *pDev)
+{
+ Q_D(QGLContext);
+ if (isValid())
+ reset();
+ d->paintDevice = pDev;
+ if (d->paintDevice && (d->paintDevice->devType() != QInternal::Widget
+ && d->paintDevice->devType() != QInternal::Pixmap
+ && d->paintDevice->devType() != QInternal::Pbuffer)) {
+ qWarning("QGLContext: Unsupported paint device type");
+ }
+}
+
+/*!
+ \fn bool QGLContext::isValid() const
+
+ Returns true if a GL rendering context has been successfully
+ created; otherwise returns false.
+*/
+
+/*!
+ \fn void QGLContext::setValid(bool valid)
+ \internal
+
+ Forces the GL rendering context to be valid.
+*/
+
+/*!
+ \fn bool QGLContext::isSharing() const
+
+ Returns true if this context is sharing its GL context with
+ another QGLContext, otherwise false is returned. Note that context
+ sharing might not be supported between contexts with different
+ formats.
+*/
+
+/*!
+ Returns true if \a context1 and \a context2 are sharing their
+ GL resources such as textures, shader programs, etc;
+ otherwise returns false.
+
+ \since 4.6
+*/
+bool QGLContext::areSharing(const QGLContext *context1, const QGLContext *context2)
+{
+ if (!context1 || !context2)
+ return false;
+ return context1->d_ptr->group == context2->d_ptr->group;
+}
+
+/*!
+ \fn bool QGLContext::deviceIsPixmap() const
+
+ Returns true if the paint device of this context is a pixmap;
+ otherwise returns false.
+*/
+
+/*!
+ \fn bool QGLContext::windowCreated() const
+
+ Returns true if a window has been created for this context;
+ otherwise returns false.
+
+ \sa setWindowCreated()
+*/
+
+/*!
+ \fn void QGLContext::setWindowCreated(bool on)
+
+ If \a on is true the context has had a window created for it. If
+ \a on is false no window has been created for the context.
+
+ \sa windowCreated()
+*/
+
+/*!
+ \fn uint QGLContext::colorIndex(const QColor& c) const
+
+ \internal
+
+ Returns a colormap index for the color c, in ColorIndex mode. Used
+ by qglColor() and qglClearColor().
+*/
+
+
+/*!
+ \fn bool QGLContext::initialized() const
+
+ Returns true if this context has been initialized, i.e. if
+ QGLWidget::initializeGL() has been performed on it; otherwise
+ returns false.
+
+ \sa setInitialized()
+*/
+
+/*!
+ \fn void QGLContext::setInitialized(bool on)
+
+ If \a on is true the context has been initialized, i.e.
+ QGLContext::setInitialized() has been called on it. If \a on is
+ false the context has not been initialized.
+
+ \sa initialized()
+*/
+
+/*!
+ \fn const QGLContext* QGLContext::currentContext()
+
+ Returns the current context, i.e. the context to which any OpenGL
+ commands will currently be directed. Returns 0 if no context is
+ current.
+
+ \sa makeCurrent()
+*/
+
+/*!
+ \fn QColor QGLContext::overlayTransparentColor() const
+
+ If this context is a valid context in an overlay plane, returns
+ the plane's transparent color. Otherwise returns an \link
+ QColor::isValid() invalid \endlink color.
+
+ The returned color's \link QColor::pixel() pixel \endlink value is
+ the index of the transparent color in the colormap of the overlay
+ plane. (Naturally, the color's RGB values are meaningless.)
+
+ The returned QColor object will generally work as expected only
+ when passed as the argument to QGLWidget::qglColor() or
+ QGLWidget::qglClearColor(). Under certain circumstances it can
+ also be used to draw transparent graphics with a QPainter. See the
+ examples/opengl/overlay_x11 example for details.
+*/
+
+
+/*!
+ Creates the GL context. Returns true if it was successful in
+ creating a valid GL rendering context on the paint device
+ specified in the constructor; otherwise returns false (i.e. the
+ context is invalid).
+
+ After successful creation, format() returns the set of features of
+ the created GL rendering context.
+
+ If \a shareContext points to a valid QGLContext, this method will
+ try to establish OpenGL display list and texture object sharing
+ between this context and the \a shareContext. Note that this may
+ fail if the two contexts have different \l {format()} {formats}.
+ Use isSharing() to see if sharing is in effect.
+
+ \warning Implementation note: initialization of C++ class
+ members usually takes place in the class constructor. QGLContext
+ is an exception because it must be simple to customize. The
+ virtual functions chooseContext() (and chooseVisual() for X11) can
+ be reimplemented in a subclass to select a particular context. The
+ problem is that virtual functions are not properly called during
+ construction (even though this is correct C++) because C++
+ constructs class hierarchies from the bottom up. For this reason
+ we need a create() function.
+
+ \sa chooseContext(), format(), isValid()
+*/
+
+bool QGLContext::create(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+#ifdef Q_WS_QPA
+ if (!d->paintDevice && !d->platformContext)
+#else
+ if (!d->paintDevice)
+#endif
+ return false;
+
+ reset();
+ d->valid = chooseContext(shareContext);
+ if (d->valid && d->paintDevice && d->paintDevice->devType() == QInternal::Widget) {
+ QWidgetPrivate *wd = qt_widget_private(static_cast<QWidget *>(d->paintDevice));
+ wd->usesDoubleBufferedGLContext = d->glFormat.doubleBuffer();
+ }
+#ifndef Q_WS_QPA //We do this in choose context->setupSharing()
+ if (d->sharing) // ok, we managed to share
+ QGLContextGroup::addShare(this, shareContext);
+#endif
+ return d->valid;
+}
+
+bool QGLContext::isValid() const
+{
+ Q_D(const QGLContext);
+ return d->valid;
+}
+
+void QGLContext::setValid(bool valid)
+{
+ Q_D(QGLContext);
+ d->valid = valid;
+}
+
+bool QGLContext::isSharing() const
+{
+ Q_D(const QGLContext);
+ return d->group->isSharing();
+}
+
+QGLFormat QGLContext::format() const
+{
+ Q_D(const QGLContext);
+ return d->glFormat;
+}
+
+QGLFormat QGLContext::requestedFormat() const
+{
+ Q_D(const QGLContext);
+ return d->reqFormat;
+}
+
+ QPaintDevice* QGLContext::device() const
+{
+ Q_D(const QGLContext);
+ return d->paintDevice;
+}
+
+bool QGLContext::deviceIsPixmap() const
+{
+ Q_D(const QGLContext);
+ return d->paintDevice->devType() == QInternal::Pixmap;
+}
+
+
+bool QGLContext::windowCreated() const
+{
+ Q_D(const QGLContext);
+ return d->crWin;
+}
+
+
+void QGLContext::setWindowCreated(bool on)
+{
+ Q_D(QGLContext);
+ d->crWin = on;
+}
+
+bool QGLContext::initialized() const
+{
+ Q_D(const QGLContext);
+ return d->initDone;
+}
+
+void QGLContext::setInitialized(bool on)
+{
+ Q_D(QGLContext);
+ d->initDone = on;
+}
+
+const QGLContext* QGLContext::currentContext()
+{
+#ifdef Q_WS_QPA
+ if (const QPlatformGLContext *threadContext = QPlatformGLContext::currentContext()) {
+ return QGLContext::fromPlatformGLContext(const_cast<QPlatformGLContext *>(threadContext));
+ }
+ return 0;
+#else
+ QGLThreadContext *threadContext = qgl_context_storage.localData();
+ if (threadContext)
+ return threadContext->context;
+ return 0;
+#endif //Q_WS_QPA
+}
+
+void QGLContextPrivate::setCurrentContext(QGLContext *context)
+{
+#ifdef Q_WS_QPA
+ Q_UNUSED(context);
+#else
+ QGLThreadContext *threadContext = qgl_context_storage.localData();
+ if (!threadContext) {
+ if (!QThread::currentThread()) {
+ // We don't have a current QThread, so just set the static.
+ QGLContext::currentCtx = context;
+ return;
+ }
+ threadContext = new QGLThreadContext;
+ qgl_context_storage.setLocalData(threadContext);
+ }
+ threadContext->context = context;
+ QGLContext::currentCtx = context; // XXX: backwards-compat, not thread-safe
+#endif
+}
+
+/*!
+ \fn bool QGLContext::chooseContext(const QGLContext* shareContext = 0)
+
+ This semi-internal function is called by create(). It creates a
+ system-dependent OpenGL handle that matches the format() of \a
+ shareContext as closely as possible, returning true if successful
+ or false if a suitable handle could not be found.
+
+ On Windows, it calls the virtual function choosePixelFormat(),
+ which finds a matching pixel format identifier. On X11, it calls
+ the virtual function chooseVisual() which finds an appropriate X
+ visual. On other platforms it may work differently.
+*/
+
+/*! \fn int QGLContext::choosePixelFormat(void* dummyPfd, HDC pdc)
+
+ \bold{Win32 only:} This virtual function chooses a pixel format
+ that matches the OpenGL \link setFormat() format\endlink.
+ Reimplement this function in a subclass if you need a custom
+ context.
+
+ \warning The \a dummyPfd pointer and \a pdc are used as a \c
+ PIXELFORMATDESCRIPTOR*. We use \c void to avoid using
+ Windows-specific types in our header files.
+
+ \sa chooseContext()
+*/
+
+/*! \fn void *QGLContext::chooseVisual()
+
+ \bold{X11 only:} This virtual function tries to find a visual that
+ matches the format, reducing the demands if the original request
+ cannot be met.
+
+ The algorithm for reducing the demands of the format is quite
+ simple-minded, so override this method in your subclass if your
+ application has spcific requirements on visual selection.
+
+ \sa chooseContext()
+*/
+
+/*! \fn void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth)
+ \internal
+
+ \bold{X11 only:} This virtual function chooses a visual that matches
+ the OpenGL \link format() format\endlink. Reimplement this function
+ in a subclass if you need a custom visual.
+
+ \sa chooseContext()
+*/
+
+/*!
+ \fn void QGLContext::reset()
+
+ Resets the context and makes it invalid.
+
+ \sa create(), isValid()
+*/
+
+
+/*!
+ \fn void QGLContext::makeCurrent()
+
+ Makes this context the current OpenGL rendering context. All GL
+ functions you call operate on this context until another context
+ is made current.
+
+ In some very rare cases the underlying call may fail. If this
+ occurs an error message is output to stderr.
+*/
+
+
+/*!
+ \fn void QGLContext::swapBuffers() const
+
+ Swaps the screen contents with an off-screen buffer. Only works if
+ the context is in double buffer mode.
+
+ \sa QGLFormat::setDoubleBuffer()
+*/
+
+
+/*!
+ \fn void QGLContext::doneCurrent()
+
+ Makes no GL context the current context. Normally, you do not need
+ to call this function; QGLContext calls it as necessary.
+*/
+
+
+/*!
+ \fn QPaintDevice* QGLContext::device() const
+
+ Returns the paint device set for this context.
+
+ \sa QGLContext::QGLContext()
+*/
+
+/*!
+ \obsolete
+ \fn void QGLContext::generateFontDisplayLists(const QFont& font, int listBase)
+
+ Generates a set of 256 display lists for the 256 first characters
+ in the font \a font. The first list will start at index \a listBase.
+
+ \sa QGLWidget::renderText()
+*/
+
+
+
+/*****************************************************************************
+ QGLWidget implementation
+ *****************************************************************************/
+
+
+/*!
+ \class QGLWidget
+ \brief The QGLWidget class is a widget for rendering OpenGL graphics.
+
+ \ingroup painting-3D
+
+
+ QGLWidget provides functionality for displaying OpenGL graphics
+ integrated into a Qt application. It is very simple to use. You
+ inherit from it and use the subclass like any other QWidget,
+ except that you have the choice between using QPainter and
+ standard OpenGL rendering commands.
+
+ QGLWidget provides three convenient virtual functions that you can
+ reimplement in your subclass to perform the typical OpenGL tasks:
+
+ \list
+ \i paintGL() - Renders the OpenGL scene. Gets called whenever the widget
+ needs to be updated.
+ \i resizeGL() - Sets up the OpenGL viewport, projection, etc. Gets
+ called whenever the widget has been resized (and also when it
+ is shown for the first time because all newly created widgets get a
+ resize event automatically).
+ \i initializeGL() - Sets up the OpenGL rendering context, defines display
+ lists, etc. Gets called once before the first time resizeGL() or
+ paintGL() is called.
+ \endlist
+
+ Here is a rough outline of how a QGLWidget subclass might look:
+
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 8
+
+ If you need to trigger a repaint from places other than paintGL()
+ (a typical example is when using \link QTimer timers\endlink to
+ animate scenes), you should call the widget's updateGL() function.
+
+ Your widget's OpenGL rendering context is made current when
+ paintGL(), resizeGL(), or initializeGL() is called. If you need to
+ call the standard OpenGL API functions from other places (e.g. in
+ your widget's constructor or in your own paint functions), you
+ must call makeCurrent() first.
+
+ QGLWidget provides functions for requesting a new display \link
+ QGLFormat format\endlink and you can also create widgets with
+ customized rendering \link QGLContext contexts\endlink.
+
+ You can also share OpenGL display lists between QGLWidget objects (see
+ the documentation of the QGLWidget constructors for details).
+
+ Note that under Windows, the QGLContext belonging to a QGLWidget
+ has to be recreated when the QGLWidget is reparented. This is
+ necessary due to limitations on the Windows platform. This will
+ most likely cause problems for users that have subclassed and
+ installed their own QGLContext on a QGLWidget. It is possible to
+ work around this issue by putting the QGLWidget inside a dummy
+ widget and then reparenting the dummy widget, instead of the
+ QGLWidget. This will side-step the issue altogether, and is what
+ we recommend for users that need this kind of functionality.
+
+ On Mac OS X, when Qt is built with Cocoa support, a QGLWidget
+ can't have any sibling widgets placed ontop of itself. This is due
+ to limitations in the Cocoa API and is not supported by Apple.
+
+ \section1 Overlays
+
+ The QGLWidget creates a GL overlay context in addition to the
+ normal context if overlays are supported by the underlying system.
+
+ If you want to use overlays, you specify it in the \link QGLFormat
+ format\endlink. (Note: Overlay must be requested in the format
+ passed to the QGLWidget constructor.) Your GL widget should also
+ implement some or all of these virtual methods:
+
+ \list
+ \i paintOverlayGL()
+ \i resizeOverlayGL()
+ \i initializeOverlayGL()
+ \endlist
+
+ These methods work in the same way as the normal paintGL() etc.
+ functions, except that they will be called when the overlay
+ context is made current. You can explicitly make the overlay
+ context current by using makeOverlayCurrent(), and you can access
+ the overlay context directly (e.g. to ask for its transparent
+ color) by calling overlayContext().
+
+ On X servers in which the default visual is in an overlay plane,
+ non-GL Qt windows can also be used for overlays.
+
+ \section1 Painting Techniques
+
+ As described above, subclass QGLWidget to render pure 3D content in the
+ following way:
+
+ \list
+ \o Reimplement the QGLWidget::initializeGL() and QGLWidget::resizeGL() to
+ set up the OpenGL state and provide a perspective transformation.
+ \o Reimplement QGLWidget::paintGL() to paint the 3D scene, calling only
+ OpenGL functions to draw on the widget.
+ \endlist
+
+ It is also possible to draw 2D graphics onto a QGLWidget subclass, it is necessary
+ to reimplement QGLWidget::paintEvent() and do the following:
+
+ \list
+ \o Construct a QPainter object.
+ \o Initialize it for use on the widget with the QPainter::begin() function.
+ \o Draw primitives using QPainter's member functions.
+ \o Call QPainter::end() to finish painting.
+ \endlist
+
+ Overpainting 2D content on top of 3D content takes a little more effort.
+ One approach to doing this is shown in the
+ \l{Overpainting Example}{Overpainting} example.
+
+ \section1 Threading
+
+ As of Qt version 4.8, support for doing threaded GL rendering has
+ been improved. There are three scenarios that we currently support:
+ \list
+ \o 1. Buffer swapping in a thread.
+
+ Swapping buffers in a double buffered context may be a
+ synchronous, locking call that may be a costly operation in some
+ GL implementations. Especially so on embedded devices. It's not
+ optimal to have the CPU idling while the GPU is doing a buffer
+ swap. In those cases it is possible to do the rendering in the
+ main thread and do the actual buffer swap in a separate
+ thread. This can be done with the following steps:
+
+ 1. Call doneCurrent() in the main thread when the rendering is
+ finished.
+
+ 2. Notify the swapping thread that it can grab the context.
+
+ 3. Make the rendering context current in the swapping thread with
+ makeCurrent() and then call swapBuffers().
+
+ 4. Call doneCurrent() in the swapping thread and notify the main
+ thread that swapping is done.
+
+ Doing this will free up the main thread so that it can continue
+ with, for example, handling UI events or network requests. Even if
+ there is a context swap involved, it may be preferable compared to
+ having the main thread wait while the GPU finishes the swap
+ operation. Note that this is highly implementation dependent.
+
+ \o 2. Texture uploading in a thread.
+
+ Doing texture uploads in a thread may be very useful for
+ applications handling large amounts of images that needs to be
+ displayed, like for instance a photo gallery application. This is
+ supported in Qt through the existing bindTexture() API. A simple
+ way of doing this is to create two sharing QGLWidgets. One is made
+ current in the main GUI thread, while the other is made current in
+ the texture upload thread. The widget in the uploading thread is
+ never shown, it is only used for sharing textures with the main
+ thread. For each texture that is bound via bindTexture(), notify
+ the main thread so that it can start using the texture.
+
+ \o 3. Using QPainter to draw into a QGLWidget in a thread.
+
+ In Qt 4.8, it is possible to draw into a QGLWidget using a
+ QPainter in a separate thread. Note that this is also possible for
+ QGLPixelBuffers and QGLFramebufferObjects. Since this is only
+ supported in the GL 2 paint engine, OpenGL 2.0 or OpenGL ES 2.0 is
+ required.
+
+ QGLWidgets can only be created in the main GUI thread. This means
+ a call to doneCurrent() is necessary to release the GL context
+ from the main thread, before the widget can be drawn into by
+ another thread. Also, the main GUI thread will dispatch resize and
+ paint events to a QGLWidget when the widget is resized, or parts
+ of it becomes exposed or needs redrawing. It is therefore
+ necessary to handle those events because the default
+ implementations inside QGLWidget will try to make the QGLWidget's
+ context current, which again will interfere with any threads
+ rendering into the widget. Reimplement QGLWidget::paintEvent() and
+ QGLWidget::resizeEvent() to notify the rendering thread that a
+ resize or update is necessary, and be careful not to call the base
+ class implementation. If you are rendering an animation, it might
+ not be necessary to handle the paint event at all since the
+ rendering thread is doing regular updates. Then it would be enough
+ to reimplement QGLWidget::paintEvent() to do nothing.
+
+ \endlist
+
+ As a general rule when doing threaded rendering: be aware that
+ binding and releasing contexts in different threads have to be
+ synchronized by the user. A GL rendering context can only be
+ current in one thread at any time. If you try to open a QPainter
+ on a QGLWidget and the widget's rendering context is current in
+ another thread, it will fail.
+
+ Note that under X11 it is necessary to set the
+ Qt::AA_X11InitThreads application attribute to make the X11
+ library and GLX calls thread safe, otherwise the above scenarios
+ will fail.
+
+ In addition to this, rendering using raw GL calls in a separate
+ thread is supported.
+
+ \e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other
+ countries.}
+
+ \sa QGLPixelBuffer, {Hello GL Example}, {2D Painting Example}, {Overpainting Example},
+ {Grabber Example}
+*/
+
+/*!
+ Constructs an OpenGL widget with a \a parent widget.
+
+ The \link QGLFormat::defaultFormat() default format\endlink is
+ used. The widget will be \link isValid() invalid\endlink if the
+ system has no \link QGLFormat::hasOpenGL() OpenGL support\endlink.
+
+ The \a parent and widget flag, \a f, arguments are passed
+ to the QWidget constructor.
+
+ If \a shareWidget is a valid QGLWidget, this widget will share
+ OpenGL display lists and texture objects with \a shareWidget. But
+ if \a shareWidget and this widget have different \l {format()}
+ {formats}, sharing might not be possible. You can check whether
+ sharing is in effect by calling isSharing().
+
+ The initialization of OpenGL rendering state, etc. should be done
+ by overriding the initializeGL() function, rather than in the
+ constructor of your QGLWidget subclass.
+
+ \sa QGLFormat::defaultFormat(), {Textures Example}
+*/
+
+QGLWidget::QGLWidget(QWidget *parent, const QGLWidget* shareWidget, Qt::WindowFlags f)
+ : QWidget(*(new QGLWidgetPrivate), parent, f | Qt::MSWindowsOwnDC)
+{
+ Q_D(QGLWidget);
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAutoFillBackground(true); // for compatibility
+ d->init(new QGLContext(QGLFormat::defaultFormat(), this), shareWidget);
+}
+
+
+/*!
+ Constructs an OpenGL widget with parent \a parent.
+
+ The \a format argument specifies the desired \link QGLFormat
+ rendering options \endlink. If the underlying OpenGL/Window system
+ cannot satisfy all the features requested in \a format, the
+ nearest subset of features will be used. After creation, the
+ format() method will return the actual format obtained.
+
+ The widget will be \link isValid() invalid\endlink if the system
+ has no \link QGLFormat::hasOpenGL() OpenGL support\endlink.
+
+ The \a parent and widget flag, \a f, arguments are passed
+ to the QWidget constructor.
+
+ If \a shareWidget is a valid QGLWidget, this widget will share
+ OpenGL display lists and texture objects with \a shareWidget. But
+ if \a shareWidget and this widget have different \l {format()}
+ {formats}, sharing might not be possible. You can check whether
+ sharing is in effect by calling isSharing().
+
+ The initialization of OpenGL rendering state, etc. should be done
+ by overriding the initializeGL() function, rather than in the
+ constructor of your QGLWidget subclass.
+
+ \sa QGLFormat::defaultFormat(), isValid()
+*/
+
+QGLWidget::QGLWidget(const QGLFormat &format, QWidget *parent, const QGLWidget* shareWidget,
+ Qt::WindowFlags f)
+ : QWidget(*(new QGLWidgetPrivate), parent, f | Qt::MSWindowsOwnDC)
+{
+ Q_D(QGLWidget);
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAutoFillBackground(true); // for compatibility
+ d->init(new QGLContext(format, this), shareWidget);
+}
+
+/*!
+ Constructs an OpenGL widget with parent \a parent.
+
+ The \a context argument is a pointer to the QGLContext that
+ you wish to be bound to this widget. This allows you to pass in
+ your own QGLContext sub-classes.
+
+ The widget will be \link isValid() invalid\endlink if the system
+ has no \link QGLFormat::hasOpenGL() OpenGL support\endlink.
+
+ The \a parent and widget flag, \a f, arguments are passed
+ to the QWidget constructor.
+
+ If \a shareWidget is a valid QGLWidget, this widget will share
+ OpenGL display lists and texture objects with \a shareWidget. But
+ if \a shareWidget and this widget have different \l {format()}
+ {formats}, sharing might not be possible. You can check whether
+ sharing is in effect by calling isSharing().
+
+ The initialization of OpenGL rendering state, etc. should be done
+ by overriding the initializeGL() function, rather than in the
+ constructor of your QGLWidget subclass.
+
+ \sa QGLFormat::defaultFormat(), isValid()
+*/
+QGLWidget::QGLWidget(QGLContext *context, QWidget *parent, const QGLWidget *shareWidget,
+ Qt::WindowFlags f)
+ : QWidget(*(new QGLWidgetPrivate), parent, f | Qt::MSWindowsOwnDC)
+{
+ Q_D(QGLWidget);
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAutoFillBackground(true); // for compatibility
+ d->init(context, shareWidget);
+}
+
+/*!
+ Destroys the widget.
+*/
+
+QGLWidget::~QGLWidget()
+{
+ Q_D(QGLWidget);
+#if defined(GLX_MESA_release_buffers) && defined(QGL_USE_MESA_EXT)
+ bool doRelease = (glcx && glcx->windowCreated());
+#endif
+ delete d->glcx;
+ d->glcx = 0;
+#if defined(Q_WS_WIN)
+ delete d->olcx;
+ d->olcx = 0;
+#endif
+#if defined(GLX_MESA_release_buffers) && defined(QGL_USE_MESA_EXT)
+ if (doRelease)
+ glXReleaseBuffersMESA(x11Display(), winId());
+#endif
+ d->cleanupColormaps();
+
+#ifdef Q_WS_MAC
+ QWidget *current = parentWidget();
+ while (current) {
+ qt_widget_private(current)->glWidgets.removeAll(QWidgetPrivate::GlWidgetInfo(this));
+ if (current->isWindow())
+ break;
+ current = current->parentWidget();
+ };
+#endif
+}
+
+/*!
+ \fn QGLFormat QGLWidget::format() const
+
+ Returns the format of the contained GL rendering context.
+*/
+
+/*!
+ \fn bool QGLWidget::doubleBuffer() const
+
+ Returns true if the contained GL rendering context has double
+ buffering; otherwise returns false.
+
+ \sa QGLFormat::doubleBuffer()
+*/
+
+/*!
+ \fn void QGLWidget::setAutoBufferSwap(bool on)
+
+ If \a on is true automatic GL buffer swapping is switched on;
+ otherwise it is switched off.
+
+ If \a on is true and the widget is using a double-buffered format,
+ the background and foreground GL buffers will automatically be
+ swapped after each paintGL() call.
+
+ The buffer auto-swapping is on by default.
+
+ \sa autoBufferSwap(), doubleBuffer(), swapBuffers()
+*/
+
+/*!
+ \fn bool QGLWidget::autoBufferSwap() const
+
+ Returns true if the widget is doing automatic GL buffer swapping;
+ otherwise returns false.
+
+ \sa setAutoBufferSwap()
+*/
+
+/*!
+ \fn void *QGLContext::getProcAddress(const QString &proc) const
+
+ Returns a function pointer to the GL extension function passed in
+ \a proc. 0 is returned if a pointer to the function could not be
+ obtained.
+*/
+
+/*!
+ \fn bool QGLWidget::isValid() const
+
+ Returns true if the widget has a valid GL rendering context;
+ otherwise returns false. A widget will be invalid if the system
+ has no \link QGLFormat::hasOpenGL() OpenGL support\endlink.
+*/
+
+bool QGLWidget::isValid() const
+{
+ Q_D(const QGLWidget);
+ return d->glcx && d->glcx->isValid();
+}
+
+/*!
+ \fn bool QGLWidget::isSharing() const
+
+ Returns true if this widget's GL context is shared with another GL
+ context, otherwise false is returned. Context sharing might not be
+ possible if the widgets use different formats.
+
+ \sa format()
+*/
+
+bool QGLWidget::isSharing() const
+{
+ Q_D(const QGLWidget);
+ return d->glcx->isSharing();
+}
+
+/*!
+ \fn void QGLWidget::makeCurrent()
+
+ Makes this widget the current widget for OpenGL operations, i.e.
+ makes the widget's rendering context the current OpenGL rendering
+ context.
+*/
+
+void QGLWidget::makeCurrent()
+{
+ Q_D(QGLWidget);
+ d->glcx->makeCurrent();
+}
+
+/*!
+ \fn void QGLWidget::doneCurrent()
+
+ Makes no GL context the current context. Normally, you do not need
+ to call this function; QGLContext calls it as necessary. However,
+ it may be useful in multithreaded environments.
+*/
+
+void QGLWidget::doneCurrent()
+{
+ Q_D(QGLWidget);
+ d->glcx->doneCurrent();
+}
+
+/*!
+ \fn void QGLWidget::swapBuffers()
+
+ Swaps the screen contents with an off-screen buffer. This only
+ works if the widget's format specifies double buffer mode.
+
+ Normally, there is no need to explicitly call this function
+ because it is done automatically after each widget repaint, i.e.
+ each time after paintGL() has been executed.
+
+ \sa doubleBuffer(), setAutoBufferSwap(), QGLFormat::setDoubleBuffer()
+*/
+
+void QGLWidget::swapBuffers()
+{
+ Q_D(QGLWidget);
+ d->glcx->swapBuffers();
+}
+
+
+/*!
+ \fn const QGLContext* QGLWidget::overlayContext() const
+
+ Returns the overlay context of this widget, or 0 if this widget
+ has no overlay.
+
+ \sa context()
+*/
+
+
+
+/*!
+ \fn void QGLWidget::makeOverlayCurrent()
+
+ Makes the overlay context of this widget current. Use this if you
+ need to issue OpenGL commands to the overlay context outside of
+ initializeOverlayGL(), resizeOverlayGL(), and paintOverlayGL().
+
+ Does nothing if this widget has no overlay.
+
+ \sa makeCurrent()
+*/
+
+
+/*!
+ \obsolete
+
+ Sets a new format for this widget.
+
+ If the underlying OpenGL/Window system cannot satisfy all the
+ features requested in \a format, the nearest subset of features will
+ be used. After creation, the format() method will return the actual
+ rendering context format obtained.
+
+ The widget will be assigned a new QGLContext, and the initializeGL()
+ function will be executed for this new context before the first
+ resizeGL() or paintGL().
+
+ This method will try to keep display list and texture object sharing
+ in effect with other QGLWidget objects, but changing the format might make
+ sharing impossible. Use isSharing() to see if sharing is still in
+ effect.
+
+ \sa format(), isSharing(), isValid()
+*/
+
+void QGLWidget::setFormat(const QGLFormat &format)
+{
+ setContext(new QGLContext(format,this));
+}
+
+
+
+
+/*!
+ \fn const QGLContext *QGLWidget::context() const
+
+ Returns the context of this widget.
+
+ It is possible that the context is not valid (see isValid()), for
+ example, if the underlying hardware does not support the format
+ attributes that were requested.
+*/
+
+/*
+ \fn void QGLWidget::setContext(QGLContext *context,
+ const QGLContext* shareContext,
+ bool deleteOldContext)
+ \obsolete
+
+ Sets a new context for this widget. The QGLContext \a context must
+ be created using \e new. QGLWidget will delete \a context when
+ another context is set or when the widget is destroyed.
+
+ If \a context is invalid, QGLContext::create() is performed on
+ it. The initializeGL() function will then be executed for the new
+ context before the first resizeGL() or paintGL().
+
+ If \a context is invalid, this method will try to keep display list
+ and texture object sharing in effect, or (if \a shareContext points
+ to a valid context) start display list and texture object sharing
+ with that context, but sharing might be impossible if the two
+ contexts have different \l {format()} {formats}. Use isSharing() to
+ see whether sharing is in effect.
+
+ If \a deleteOldContext is true (the default), the existing context
+ will be deleted. You may use false here if you have kept a pointer
+ to the old context (as returned by context()), and want to restore
+ that context later.
+
+ \sa context(), isSharing()
+*/
+
+
+
+/*!
+ \fn void QGLWidget::updateGL()
+
+ Updates the widget by calling glDraw().
+*/
+
+void QGLWidget::updateGL()
+{
+ if (updatesEnabled())
+ glDraw();
+}
+
+
+/*!
+ \fn void QGLWidget::updateOverlayGL()
+
+ Updates the widget's overlay (if any). Will cause the virtual
+ function paintOverlayGL() to be executed.
+
+ The widget's rendering context will become the current context and
+ initializeGL() will be called if it hasn't already been called.
+*/
+
+
+/*!
+ This virtual function is called once before the first call to
+ paintGL() or resizeGL(), and then once whenever the widget has
+ been assigned a new QGLContext. Reimplement it in a subclass.
+
+ This function should set up any required OpenGL context rendering
+ flags, defining display lists, etc.
+
+ There is no need to call makeCurrent() because this has already
+ been done when this function is called.
+*/
+
+void QGLWidget::initializeGL()
+{
+}
+
+
+/*!
+ This virtual function is called whenever the widget needs to be
+ painted. Reimplement it in a subclass.
+
+ There is no need to call makeCurrent() because this has already
+ been done when this function is called.
+*/
+
+void QGLWidget::paintGL()
+{
+}
+
+
+/*!
+ \fn void QGLWidget::resizeGL(int width , int height)
+
+ This virtual function is called whenever the widget has been
+ resized. The new size is passed in \a width and \a height.
+ Reimplement it in a subclass.
+
+ There is no need to call makeCurrent() because this has already
+ been done when this function is called.
+*/
+
+void QGLWidget::resizeGL(int, int)
+{
+}
+
+
+
+/*!
+ This virtual function is used in the same manner as initializeGL()
+ except that it operates on the widget's overlay context instead of
+ the widget's main context. This means that initializeOverlayGL()
+ is called once before the first call to paintOverlayGL() or
+ resizeOverlayGL(). Reimplement it in a subclass.
+
+ This function should set up any required OpenGL context rendering
+ flags, defining display lists, etc. for the overlay context.
+
+ There is no need to call makeOverlayCurrent() because this has
+ already been done when this function is called.
+*/
+
+void QGLWidget::initializeOverlayGL()
+{
+}
+
+
+/*!
+ This virtual function is used in the same manner as paintGL()
+ except that it operates on the widget's overlay context instead of
+ the widget's main context. This means that paintOverlayGL() is
+ called whenever the widget's overlay needs to be painted.
+ Reimplement it in a subclass.
+
+ There is no need to call makeOverlayCurrent() because this has
+ already been done when this function is called.
+*/
+
+void QGLWidget::paintOverlayGL()
+{
+}
+
+
+/*!
+ \fn void QGLWidget::resizeOverlayGL(int width , int height)
+
+ This virtual function is used in the same manner as paintGL()
+ except that it operates on the widget's overlay context instead of
+ the widget's main context. This means that resizeOverlayGL() is
+ called whenever the widget has been resized. The new size is
+ passed in \a width and \a height. Reimplement it in a subclass.
+
+ There is no need to call makeOverlayCurrent() because this has
+ already been done when this function is called.
+*/
+
+void QGLWidget::resizeOverlayGL(int, int)
+{
+}
+
+/*! \fn bool QGLWidget::event(QEvent *e)
+ \reimp
+*/
+#if !defined(Q_OS_WINCE) && !defined(Q_WS_QWS) && !defined(Q_WS_QPA)
+bool QGLWidget::event(QEvent *e)
+{
+ Q_D(QGLWidget);
+
+ if (e->type() == QEvent::Paint) {
+ QPoint offset;
+ QPaintDevice *redirectedDevice = d->redirected(&offset);
+ if (redirectedDevice && redirectedDevice->devType() == QInternal::Pixmap) {
+ d->restoreRedirected();
+ QPixmap pixmap = renderPixmap();
+ d->setRedirected(redirectedDevice, offset);
+ QPainter p(redirectedDevice);
+ p.drawPixmap(-offset, pixmap);
+ return true;
+ }
+ }
+
+#if defined(Q_WS_X11)
+ if (e->type() == QEvent::ParentChange) {
+ // if we've reparented a window that has the current context
+ // bound, we need to rebind that context to the new window id
+ if (d->glcx == QGLContext::currentContext())
+ makeCurrent();
+
+ if (d->glcx->d_func()->screen != d->xinfo.screen() || testAttribute(Qt::WA_TranslucentBackground)) {
+ setContext(new QGLContext(d->glcx->requestedFormat(), this));
+ // ### recreating the overlay isn't supported atm
+ }
+ }
+
+#ifndef QT_NO_EGL
+ // A re-parent is likely to destroy the X11 window and re-create it. It is important
+ // that we free the EGL surface _before_ the winID changes - otherwise we can leak.
+ if (e->type() == QEvent::ParentAboutToChange)
+ d->glcx->d_func()->destroyEglSurfaceForDevice();
+
+ if ((e->type() == QEvent::ParentChange) || (e->type() == QEvent::WindowStateChange)) {
+ // The window may have been re-created during re-parent or state change - if so, the EGL
+ // surface will need to be re-created.
+ d->recreateEglSurface();
+ }
+#endif
+#elif defined(Q_WS_WIN)
+ if (e->type() == QEvent::ParentChange) {
+ QGLContext *newContext = new QGLContext(d->glcx->requestedFormat(), this);
+ setContext(newContext, d->glcx);
+
+ // the overlay needs to be recreated as well
+ delete d->olcx;
+ if (isValid() && context()->format().hasOverlay()) {
+ d->olcx = new QGLContext(QGLFormat::defaultOverlayFormat(), this);
+ if (!d->olcx->create(isSharing() ? d->glcx : 0)) {
+ delete d->olcx;
+ d->olcx = 0;
+ d->glcx->d_func()->glFormat.setOverlay(false);
+ }
+ } else {
+ d->olcx = 0;
+ }
+ } else if (e->type() == QEvent::Show) {
+ if (!format().rgba())
+ d->updateColormap();
+ }
+#elif defined(Q_WS_MAC)
+ if (e->type() == QEvent::MacGLWindowChange
+#if 0 //(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ && ((QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5 && isWindow())
+ || QSysInfo::MacintoshVersion <= QSysInfo::MV_10_4)
+#endif
+ ) {
+ if (d->needWindowChange) {
+ d->needWindowChange = false;
+ d->glcx->updatePaintDevice();
+ update();
+ }
+ return true;
+# if defined(QT_MAC_USE_COCOA)
+ } else if (e->type() == QEvent::MacGLClearDrawable) {
+ d->glcx->d_ptr->clearDrawable();
+# endif
+ }
+#elif defined(Q_OS_SYMBIAN)
+ // prevents errors on some systems, where we get a flush to a
+ // hidden widget
+ if (e->type() == QEvent::Hide) {
+ makeCurrent();
+ glFinish();
+ doneCurrent();
+ } else if (e->type() == QEvent::ParentChange) {
+ // if we've reparented a window that has the current context
+ // bound, we need to rebind that context to the new window id
+ if (d->glcx == QGLContext::currentContext())
+ makeCurrent();
+
+ if (testAttribute(Qt::WA_TranslucentBackground))
+ setContext(new QGLContext(d->glcx->requestedFormat(), this));
+ }
+
+ // A re-parent is likely to destroy the Symbian window and re-create it. It is important
+ // that we free the EGL surface _before_ the winID changes - otherwise we can leak.
+ if (e->type() == QEvent::ParentAboutToChange)
+ d->glcx->d_func()->destroyEglSurfaceForDevice();
+
+ if ((e->type() == QEvent::ParentChange) || (e->type() == QEvent::WindowStateChange)) {
+ // The window may have been re-created during re-parent or state change - if so, the EGL
+ // surface will need to be re-created.
+ d->recreateEglSurface();
+ }
+
+#endif
+
+ return QWidget::event(e);
+}
+#endif
+
+/*!
+ \fn void QGLWidget::paintEvent(QPaintEvent *event)
+
+ Handles paint events passed in the \a event parameter. Will cause
+ the virtual paintGL() function to be called.
+
+ The widget's rendering context will become the current context and
+ initializeGL() will be called if it hasn't already been called.
+*/
+
+void QGLWidget::paintEvent(QPaintEvent *)
+{
+ if (updatesEnabled()) {
+ glDraw();
+ updateOverlayGL();
+ }
+}
+
+
+/*!
+ \fn void QGLWidget::resizeEvent(QResizeEvent *event)
+
+ Handles resize events that are passed in the \a event parameter.
+ Calls the virtual function resizeGL().
+*/
+
+
+/*!
+ \fn void QGLWidget::setMouseTracking(bool enable)
+
+ If \a enable is true then mouse tracking is enabled; otherwise it
+ is disabled.
+*/
+
+
+/*!
+ Renders the current scene on a pixmap and returns the pixmap.
+
+ You can use this method on both visible and invisible QGLWidget objects.
+
+ This method will create a pixmap and a temporary QGLContext to
+ render on the pixmap. It will then call initializeGL(),
+ resizeGL(), and paintGL() on this context. Finally, the widget's
+ original GL context is restored.
+
+ The size of the pixmap will be \a w pixels wide and \a h pixels
+ high unless one of these parameters is 0 (the default), in which
+ case the pixmap will have the same size as the widget.
+
+ If \a useContext is true, this method will try to be more
+ efficient by using the existing GL context to render the pixmap.
+ The default is false. Only use true if you understand the risks.
+ Note that under Windows a temporary context has to be created
+ and usage of the \e useContext parameter is not supported.
+
+ Overlays are not rendered onto the pixmap.
+
+ If the GL rendering context and the desktop have different bit
+ depths, the result will most likely look surprising.
+
+ Note that the creation of display lists, modifications of the view
+ frustum etc. should be done from within initializeGL(). If this is
+ not done, the temporary QGLContext will not be initialized
+ properly, and the rendered pixmap may be incomplete/corrupted.
+*/
+
+QPixmap QGLWidget::renderPixmap(int w, int h, bool useContext)
+{
+ Q_D(QGLWidget);
+ QSize sz = size();
+ if ((w > 0) && (h > 0))
+ sz = QSize(w, h);
+
+#if defined(Q_WS_X11)
+ extern int qt_x11_preferred_pixmap_depth;
+ int old_depth = qt_x11_preferred_pixmap_depth;
+ qt_x11_preferred_pixmap_depth = x11Info().depth();
+
+ QPixmapData *data = new QX11PixmapData(QPixmapData::PixmapType);
+ data->resize(sz.width(), sz.height());
+ QPixmap pm(data);
+ qt_x11_preferred_pixmap_depth = old_depth;
+ QX11Info xinfo = x11Info();
+
+ // make sure we use a pixmap with the same depth/visual as the widget
+ if (xinfo.visual() != QX11Info::appVisual()) {
+ QX11InfoData* xd = pm.x11Info().getX11Data(true);
+ xd->depth = xinfo.depth();
+ xd->visual = static_cast<Visual *>(xinfo.visual());
+ const_cast<QX11Info &>(pm.x11Info()).setX11Data(xd);
+ }
+
+#else
+ QPixmap pm(sz);
+#endif
+
+ d->glcx->doneCurrent();
+
+ bool success = true;
+
+ if (useContext && isValid() && d->renderCxPm(&pm))
+ return pm;
+
+ QGLFormat fmt = d->glcx->requestedFormat();
+ fmt.setDirectRendering(false); // Direct is unlikely to work
+ fmt.setDoubleBuffer(false); // We don't need dbl buf
+#ifdef Q_WS_MAC // crash prevention on the Mac - it's unlikely to work anyway
+ fmt.setSampleBuffers(false);
+#endif
+
+ QGLContext* ocx = d->glcx;
+ ocx->doneCurrent();
+ d->glcx = new QGLContext(fmt, &pm);
+ d->glcx->create();
+
+ if (d->glcx->isValid())
+ updateGL();
+ else
+ success = false;
+
+ delete d->glcx;
+ d->glcx = ocx;
+
+ ocx->makeCurrent();
+
+ if (success) {
+#if defined(Q_WS_X11)
+ if (xinfo.visual() != QX11Info::appVisual()) {
+ QImage image = pm.toImage();
+ QPixmap p = QPixmap::fromImage(image);
+ return p;
+ }
+#endif
+ return pm;
+ }
+ return QPixmap();
+}
+
+/*!
+ Returns an image of the frame buffer. If \a withAlpha is true the
+ alpha channel is included.
+
+ Depending on your hardware, you can explicitly select which color
+ buffer to grab with a glReadBuffer() call before calling this
+ function.
+*/
+QImage QGLWidget::grabFrameBuffer(bool withAlpha)
+{
+ makeCurrent();
+ QImage res;
+ int w = width();
+ int h = height();
+ if (format().rgba()) {
+ res = qt_gl_read_framebuffer(QSize(w, h), format().alpha(), withAlpha);
+ } else {
+#if defined (Q_WS_WIN) && !defined(QT_OPENGL_ES)
+ res = QImage(w, h, QImage::Format_Indexed8);
+ glReadPixels(0, 0, w, h, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, res.bits());
+ const QVector<QColor> pal = QColormap::instance().colormap();
+ if (pal.size()) {
+ res.setColorCount(pal.size());
+ for (int i = 0; i < pal.size(); i++)
+ res.setColor(i, pal.at(i).rgb());
+ }
+ res = res.mirrored();
+#endif
+ }
+
+ return res;
+}
+
+
+
+/*!
+ Initializes OpenGL for this widget's context. Calls the virtual
+ function initializeGL().
+*/
+
+void QGLWidget::glInit()
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ initializeGL();
+ d->glcx->setInitialized(true);
+}
+
+
+/*!
+ Executes the virtual function paintGL().
+
+ The widget's rendering context will become the current context and
+ initializeGL() will be called if it hasn't already been called.
+*/
+
+void QGLWidget::glDraw()
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+#ifdef Q_OS_SYMBIAN
+ // Crashes on Symbian if trying to render to invisible surfaces
+ if (!isVisible() && d->glcx->device()->devType() == QInternal::Widget)
+ return;
+#endif
+ makeCurrent();
+#ifndef QT_OPENGL_ES
+ if (d->glcx->deviceIsPixmap())
+ glDrawBuffer(GL_FRONT);
+#endif
+ if (!d->glcx->initialized()) {
+ glInit();
+ resizeGL(d->glcx->device()->width(), d->glcx->device()->height()); // New context needs this "resize"
+ }
+ paintGL();
+ if (doubleBuffer()) {
+ if (d->autoSwap)
+ swapBuffers();
+ } else {
+ glFlush();
+ }
+}
+
+/*!
+ Convenience function for specifying a drawing color to OpenGL.
+ Calls glColor4 (in RGBA mode) or glIndex (in color-index mode)
+ with the color \a c. Applies to this widgets GL context.
+
+ \note This function is not supported on OpenGL/ES 2.0 systems.
+
+ \sa qglClearColor(), QGLContext::currentContext(), QColor
+*/
+
+void QGLWidget::qglColor(const QColor& c) const
+{
+#if !defined(QT_OPENGL_ES_2)
+#ifdef QT_OPENGL_ES
+ glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF());
+#else
+ Q_D(const QGLWidget);
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (ctx) {
+ if (ctx->format().rgba())
+ glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF());
+ else if (!d->cmap.isEmpty()) { // QGLColormap in use?
+ int i = d->cmap.find(c.rgb());
+ if (i < 0)
+ i = d->cmap.findNearest(c.rgb());
+ glIndexi(i);
+ } else
+ glIndexi(ctx->colorIndex(c));
+ }
+#endif //QT_OPENGL_ES
+#else
+ Q_UNUSED(c);
+#endif //QT_OPENGL_ES_2
+}
+
+/*!
+ Convenience function for specifying the clearing color to OpenGL.
+ Calls glClearColor (in RGBA mode) or glClearIndex (in color-index
+ mode) with the color \a c. Applies to this widgets GL context.
+
+ \sa qglColor(), QGLContext::currentContext(), QColor
+*/
+
+void QGLWidget::qglClearColor(const QColor& c) const
+{
+#ifdef QT_OPENGL_ES
+ glClearColor(c.redF(), c.greenF(), c.blueF(), c.alphaF());
+#else
+ Q_D(const QGLWidget);
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (ctx) {
+ if (ctx->format().rgba())
+ glClearColor(c.redF(), c.greenF(), c.blueF(), c.alphaF());
+ else if (!d->cmap.isEmpty()) { // QGLColormap in use?
+ int i = d->cmap.find(c.rgb());
+ if (i < 0)
+ i = d->cmap.findNearest(c.rgb());
+ glClearIndex(i);
+ } else
+ glClearIndex(ctx->colorIndex(c));
+ }
+#endif
+}
+
+
+/*!
+ Converts the image \a img into the unnamed format expected by
+ OpenGL functions such as glTexImage2D(). The returned image is not
+ usable as a QImage, but QImage::width(), QImage::height() and
+ QImage::bits() may be used with OpenGL. The GL format used is
+ \c GL_RGBA.
+
+ \omit ###
+
+ \l opengl/texture example
+ The following few lines are from the texture example. Most of the
+ code is irrelevant, so we just quote the relevant bits:
+
+ \quotefromfile opengl/texture/gltexobj.cpp
+ \skipto tex1
+ \printline tex1
+ \printline gllogo.bmp
+
+ We create \e tex1 (and another variable) for OpenGL, and load a real
+ image into \e buf.
+
+ \skipto convertToGLFormat
+ \printline convertToGLFormat
+
+ A few lines later, we convert \e buf into OpenGL format and store it
+ in \e tex1.
+
+ \skipto glTexImage2D
+ \printline glTexImage2D
+ \printline tex1.bits
+
+ Note the dimension restrictions for texture images as described in
+ the glTexImage2D() documentation. The width must be 2^m + 2*border
+ and the height 2^n + 2*border where m and n are integers and
+ border is either 0 or 1.
+
+ Another function in the same example uses \e tex1 with OpenGL.
+
+ \endomit
+*/
+
+QImage QGLWidget::convertToGLFormat(const QImage& img)
+{
+ QImage res(img.size(), QImage::Format_ARGB32);
+ convertToGLFormatHelper(res, img.convertToFormat(QImage::Format_ARGB32), GL_RGBA);
+ return res;
+}
+
+
+/*!
+ \fn QGLColormap & QGLWidget::colormap() const
+
+ Returns the colormap for this widget.
+
+ Usually it is only top-level widgets that can have different
+ colormaps installed. Asking for the colormap of a child widget
+ will return the colormap for the child's top-level widget.
+
+ If no colormap has been set for this widget, the QGLColormap
+ returned will be empty.
+
+ \sa setColormap(), QGLColormap::isEmpty()
+*/
+
+/*!
+ \fn void QGLWidget::setColormap(const QGLColormap & cmap)
+
+ Set the colormap for this widget to \a cmap. Usually it is only
+ top-level widgets that can have colormaps installed.
+
+ \sa colormap()
+*/
+
+
+/*!
+ \obsolete
+
+ Returns the value of the first display list that is generated for
+ the characters in the given \a font. \a listBase indicates the base
+ value used when generating the display lists for the font. The
+ default value is 2000.
+
+ \note This function is not supported on OpenGL/ES systems.
+*/
+int QGLWidget::fontDisplayListBase(const QFont & font, int listBase)
+{
+#ifndef QT_OPENGL_ES
+ Q_D(QGLWidget);
+ int base;
+
+ if (!d->glcx) { // this can't happen unless we run out of mem
+ return 0;
+ }
+
+ // always regenerate font disp. lists for pixmaps - hw accelerated
+ // contexts can't handle this otherwise
+ bool regenerate = d->glcx->deviceIsPixmap();
+#ifndef QT_NO_FONTCONFIG
+ // font color needs to be part of the font cache key when using
+ // antialiased fonts since one set of glyphs needs to be generated
+ // for each font color
+ QString color_key;
+ if (font.styleStrategy() != QFont::NoAntialias) {
+ GLfloat color[4];
+ glGetFloatv(GL_CURRENT_COLOR, color);
+ color_key.sprintf("%f_%f_%f",color[0], color[1], color[2]);
+ }
+ QString key = font.key() + color_key + QString::number((int) regenerate);
+#else
+ QString key = font.key() + QString::number((int) regenerate);
+#endif
+ if (!regenerate && (d->displayListCache.find(key) != d->displayListCache.end())) {
+ base = d->displayListCache[key];
+ } else {
+ int maxBase = listBase - 256;
+ QMap<QString,int>::ConstIterator it;
+ for (it = d->displayListCache.constBegin(); it != d->displayListCache.constEnd(); ++it) {
+ if (maxBase < it.value()) {
+ maxBase = it.value();
+ }
+ }
+ maxBase += 256;
+ d->glcx->generateFontDisplayLists(font, maxBase);
+ d->displayListCache[key] = maxBase;
+ base = maxBase;
+ }
+ return base;
+#else // QT_OPENGL_ES
+ Q_UNUSED(font);
+ Q_UNUSED(listBase);
+ return 0;
+#endif
+}
+
+#ifndef QT_OPENGL_ES
+
+static void qt_save_gl_state()
+{
+ glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
+ glPushAttrib(GL_ALL_ATTRIB_BITS);
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ glShadeModel(GL_FLAT);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_LIGHTING);
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+static void qt_restore_gl_state()
+{
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ glPopAttrib();
+ glPopClientAttrib();
+}
+
+static void qt_gl_draw_text(QPainter *p, int x, int y, const QString &str,
+ const QFont &font)
+{
+ GLfloat color[4];
+ glGetFloatv(GL_CURRENT_COLOR, &color[0]);
+
+ QColor col;
+ col.setRgbF(color[0], color[1], color[2],color[3]);
+ QPen old_pen = p->pen();
+ QFont old_font = p->font();
+
+ p->setPen(col);
+ p->setFont(font);
+ p->drawText(x, y, str);
+
+ p->setPen(old_pen);
+ p->setFont(old_font);
+}
+
+#endif // !QT_OPENGL_ES
+
+/*!
+ Renders the string \a str into the GL context of this widget.
+
+ \a x and \a y are specified in window coordinates, with the origin
+ in the upper left-hand corner of the window. If \a font is not
+ specified, the currently set application font will be used to
+ render the string. To change the color of the rendered text you can
+ use the glColor() call (or the qglColor() convenience function),
+ just before the renderText() call.
+
+ The \a listBase parameter is obsolete and will be removed in a
+ future version of Qt.
+
+ \note This function clears the stencil buffer.
+
+ \note This function is not supported on OpenGL/ES systems.
+
+ \note This function temporarily disables depth-testing when the
+ text is drawn.
+
+ \note This function can only be used inside a
+ QPainter::beginNativePainting()/QPainter::endNativePainting() block
+ if the default OpenGL paint engine is QPaintEngine::OpenGL. To make
+ QPaintEngine::OpenGL the default GL engine, call
+ QGL::setPreferredPaintEngine(QPaintEngine::OpenGL) before the
+ QApplication constructor.
+
+ \l{Overpainting Example}{Overpaint} with QPainter::drawText() instead.
+*/
+
+void QGLWidget::renderText(int x, int y, const QString &str, const QFont &font, int)
+{
+#ifndef QT_OPENGL_ES
+ Q_D(QGLWidget);
+ if (str.isEmpty() || !isValid())
+ return;
+
+ GLint view[4];
+ bool use_scissor_testing = glIsEnabled(GL_SCISSOR_TEST);
+ if (!use_scissor_testing)
+ glGetIntegerv(GL_VIEWPORT, &view[0]);
+ int width = d->glcx->device()->width();
+ int height = d->glcx->device()->height();
+ bool auto_swap = autoBufferSwap();
+
+ QPaintEngine::Type oldEngineType = qgl_engine_selector()->preferredPaintEngine();
+
+ QPaintEngine *engine = paintEngine();
+ if (engine && (oldEngineType == QPaintEngine::OpenGL2) && engine->isActive()) {
+ qWarning("QGLWidget::renderText(): Calling renderText() while a GL 2 paint engine is"
+ " active on the same device is not allowed.");
+ return;
+ }
+
+ // this changes what paintEngine() returns
+ qgl_engine_selector()->setPreferredPaintEngine(QPaintEngine::OpenGL);
+ engine = paintEngine();
+ QPainter *p;
+ bool reuse_painter = false;
+ if (engine->isActive()) {
+ reuse_painter = true;
+ p = engine->painter();
+ qt_save_gl_state();
+
+ glDisable(GL_DEPTH_TEST);
+ glViewport(0, 0, width, height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, width, height, 0, 0, 1);
+ glMatrixMode(GL_MODELVIEW);
+
+ glLoadIdentity();
+ } else {
+ setAutoBufferSwap(false);
+ // disable glClear() as a result of QPainter::begin()
+ d->disable_clear_on_painter_begin = true;
+ p = new QPainter(this);
+ }
+
+ QRect viewport(view[0], view[1], view[2], view[3]);
+ if (!use_scissor_testing && viewport != rect()) {
+ // if the user hasn't set a scissor box, we set one that
+ // covers the current viewport
+ glScissor(view[0], view[1], view[2], view[3]);
+ glEnable(GL_SCISSOR_TEST);
+ } else if (use_scissor_testing) {
+ // use the scissor box set by the user
+ glEnable(GL_SCISSOR_TEST);
+ }
+
+ qt_gl_draw_text(p, x, y, str, font);
+
+ if (reuse_painter) {
+ qt_restore_gl_state();
+ } else {
+ p->end();
+ delete p;
+ setAutoBufferSwap(auto_swap);
+ d->disable_clear_on_painter_begin = false;
+ }
+ qgl_engine_selector()->setPreferredPaintEngine(oldEngineType);
+#else // QT_OPENGL_ES
+ Q_UNUSED(x);
+ Q_UNUSED(y);
+ Q_UNUSED(str);
+ Q_UNUSED(font);
+ qWarning("QGLWidget::renderText is not supported under OpenGL/ES");
+#endif
+}
+
+/*! \overload
+
+ \a x, \a y and \a z are specified in scene or object coordinates
+ relative to the currently set projection and model matrices. This
+ can be useful if you want to annotate models with text labels and
+ have the labels move with the model as it is rotated etc.
+
+ \note This function is not supported on OpenGL/ES systems.
+
+ \note If depth testing is enabled before this function is called,
+ then the drawn text will be depth-tested against the models that
+ have already been drawn in the scene. Use \c{glDisable(GL_DEPTH_TEST)}
+ before calling this function to annotate the models without
+ depth-testing the text.
+
+ \l{Overpainting Example}{Overpaint} with QPainter::drawText() instead.
+*/
+void QGLWidget::renderText(double x, double y, double z, const QString &str, const QFont &font, int)
+{
+#ifndef QT_OPENGL_ES
+ Q_D(QGLWidget);
+ if (str.isEmpty() || !isValid())
+ return;
+
+ bool auto_swap = autoBufferSwap();
+
+ int width = d->glcx->device()->width();
+ int height = d->glcx->device()->height();
+ GLdouble model[4][4], proj[4][4];
+ GLint view[4];
+ glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]);
+ glGetDoublev(GL_PROJECTION_MATRIX, &proj[0][0]);
+ glGetIntegerv(GL_VIEWPORT, &view[0]);
+ GLdouble win_x = 0, win_y = 0, win_z = 0;
+ qgluProject(x, y, z, &model[0][0], &proj[0][0], &view[0],
+ &win_x, &win_y, &win_z);
+ win_y = height - win_y; // y is inverted
+
+ QPaintEngine::Type oldEngineType = qgl_engine_selector()->preferredPaintEngine();
+ QPaintEngine *engine = paintEngine();
+
+ if (engine && (oldEngineType == QPaintEngine::OpenGL2) && engine->isActive()) {
+ qWarning("QGLWidget::renderText(): Calling renderText() while a GL 2 paint engine is"
+ " active on the same device is not allowed.");
+ return;
+ }
+
+ // this changes what paintEngine() returns
+ qgl_engine_selector()->setPreferredPaintEngine(QPaintEngine::OpenGL);
+ engine = paintEngine();
+ QPainter *p;
+ bool reuse_painter = false;
+ bool use_depth_testing = glIsEnabled(GL_DEPTH_TEST);
+ bool use_scissor_testing = glIsEnabled(GL_SCISSOR_TEST);
+
+ if (engine->isActive()) {
+ reuse_painter = true;
+ p = engine->painter();
+ qt_save_gl_state();
+ } else {
+ setAutoBufferSwap(false);
+ // disable glClear() as a result of QPainter::begin()
+ d->disable_clear_on_painter_begin = true;
+ p = new QPainter(this);
+ }
+
+ QRect viewport(view[0], view[1], view[2], view[3]);
+ if (!use_scissor_testing && viewport != rect()) {
+ glScissor(view[0], view[1], view[2], view[3]);
+ glEnable(GL_SCISSOR_TEST);
+ } else if (use_scissor_testing) {
+ glEnable(GL_SCISSOR_TEST);
+ }
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glViewport(0, 0, width, height);
+ glOrtho(0, width, height, 0, 0, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glAlphaFunc(GL_GREATER, 0.0);
+ glEnable(GL_ALPHA_TEST);
+ if (use_depth_testing)
+ glEnable(GL_DEPTH_TEST);
+ glTranslated(0, 0, -win_z);
+ qt_gl_draw_text(p, qRound(win_x), qRound(win_y), str, font);
+
+ if (reuse_painter) {
+ qt_restore_gl_state();
+ } else {
+ p->end();
+ delete p;
+ setAutoBufferSwap(auto_swap);
+ d->disable_clear_on_painter_begin = false;
+ }
+ qgl_engine_selector()->setPreferredPaintEngine(oldEngineType);
+#else // QT_OPENGL_ES
+ Q_UNUSED(x);
+ Q_UNUSED(y);
+ Q_UNUSED(z);
+ Q_UNUSED(str);
+ Q_UNUSED(font);
+ qWarning("QGLWidget::renderText is not supported under OpenGL/ES");
+#endif
+}
+
+QGLFormat QGLWidget::format() const
+{
+ Q_D(const QGLWidget);
+ return d->glcx->format();
+}
+
+const QGLContext *QGLWidget::context() const
+{
+ Q_D(const QGLWidget);
+ return d->glcx;
+}
+
+bool QGLWidget::doubleBuffer() const
+{
+ Q_D(const QGLWidget);
+ return d->glcx->d_ptr->glFormat.testOption(QGL::DoubleBuffer);
+}
+
+void QGLWidget::setAutoBufferSwap(bool on)
+{
+ Q_D(QGLWidget);
+ d->autoSwap = on;
+}
+
+bool QGLWidget::autoBufferSwap() const
+{
+ Q_D(const QGLWidget);
+ return d->autoSwap;
+}
+
+/*!
+ Calls QGLContext:::bindTexture(\a image, \a target, \a format) on the currently
+ set context.
+
+ \sa deleteTexture()
+*/
+GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format)
+{
+ if (image.isNull())
+ return 0;
+
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(image, target, format, QGLContext::DefaultBindOption);
+}
+
+/*!
+ \overload
+ \since 4.6
+
+ The binding \a options are a set of options used to decide how to
+ bind the texture to the context.
+ */
+GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format, QGLContext::BindOptions options)
+{
+ if (image.isNull())
+ return 0;
+
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(image, target, format, options);
+}
+
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+GLuint QGLWidget::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format)
+{
+ if (image.isNull())
+ return 0;
+
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(image, GLenum(target), GLint(format), QGLContext::DefaultBindOption);
+}
+
+GLuint QGLWidget::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format,
+ QGLContext::BindOptions options)
+{
+ if (image.isNull())
+ return 0;
+
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(image, GLenum(target), GLint(format), options);
+}
+#endif
+
+/*!
+ Calls QGLContext:::bindTexture(\a pixmap, \a target, \a format) on the currently
+ set context.
+
+ \sa deleteTexture()
+*/
+GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format)
+{
+ if (pixmap.isNull())
+ return 0;
+
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(pixmap, target, format, QGLContext::DefaultBindOption);
+}
+
+/*!
+ \overload
+ \since 4.6
+
+ Generates and binds a 2D GL texture to the current context, based
+ on \a pixmap. The generated texture id is returned and can be used in
+
+ The binding \a options are a set of options used to decide how to
+ bind the texture to the context.
+ */
+GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format,
+ QGLContext::BindOptions options)
+{
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(pixmap, target, format, options);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+GLuint QGLWidget::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format)
+{
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(pixmap, target, format, QGLContext::DefaultBindOption);
+}
+
+GLuint QGLWidget::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format,
+ QGLContext::BindOptions options)
+{
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(pixmap, target, format, options);
+}
+#endif
+
+
+/*! \overload
+
+ Calls QGLContext::bindTexture(\a fileName) on the currently set context.
+
+ \sa deleteTexture()
+*/
+GLuint QGLWidget::bindTexture(const QString &fileName)
+{
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(fileName);
+}
+
+/*!
+ Calls QGLContext::deleteTexture(\a id) on the currently set
+ context.
+
+ \sa bindTexture()
+*/
+void QGLWidget::deleteTexture(GLuint id)
+{
+ Q_D(QGLWidget);
+ d->glcx->deleteTexture(id);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLWidget::deleteTexture(QMacCompatGLuint id)
+{
+ Q_D(QGLWidget);
+ d->glcx->deleteTexture(GLuint(id));
+}
+#endif
+
+/*!
+ \since 4.4
+
+ Calls the corresponding QGLContext::drawTexture() with
+ \a target, \a textureId, and \a textureTarget for this
+ widget's context.
+*/
+void QGLWidget::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
+{
+ Q_D(QGLWidget);
+ d->glcx->drawTexture(target, textureId, textureTarget);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLWidget::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ Q_D(QGLWidget);
+ d->glcx->drawTexture(target, GLint(textureId), GLenum(textureTarget));
+}
+#endif
+
+/*!
+ \since 4.4
+
+ Calls the corresponding QGLContext::drawTexture() with
+ \a point, \a textureId, and \a textureTarget for this
+ widget's context.
+*/
+void QGLWidget::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
+{
+ Q_D(QGLWidget);
+ d->glcx->drawTexture(point, textureId, textureTarget);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLWidget::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ Q_D(QGLWidget);
+ d->glcx->drawTexture(point, GLuint(textureId), GLenum(textureTarget));
+}
+#endif
+
+#ifndef QT_OPENGL_ES_1
+Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_gl_2_engine)
+#endif
+
+#ifndef QT_OPENGL_ES_2
+Q_GLOBAL_STATIC(QGLEngineThreadStorage<QOpenGLPaintEngine>, qt_gl_engine)
+#endif
+
+Q_OPENGL_EXPORT QPaintEngine* qt_qgl_paint_engine()
+{
+#if defined(QT_OPENGL_ES_1)
+ return qt_gl_engine()->engine();
+#elif defined(QT_OPENGL_ES_2)
+ return qt_gl_2_engine()->engine();
+#else
+ if (qt_gl_preferGL2Engine())
+ return qt_gl_2_engine()->engine();
+ else
+ return qt_gl_engine()->engine();
+#endif
+}
+
+/*!
+ \internal
+
+ Returns the GL widget's paint engine. This is normally a
+ QOpenGLPaintEngine.
+*/
+QPaintEngine *QGLWidget::paintEngine() const
+{
+ return qt_qgl_paint_engine();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \overload
+ \obsolete
+ */
+QGLWidget::QGLWidget(QWidget *parent, const char *name,
+ const QGLWidget* shareWidget, Qt::WindowFlags f)
+ : QWidget(*(new QGLWidgetPrivate), parent, f | Qt::MSWindowsOwnDC)
+{
+ Q_D(QGLWidget);
+ if (name)
+ setObjectName(QString::fromAscii(name));
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAutoFillBackground(true); // for compatibility
+ d->init(new QGLContext(QGLFormat::defaultFormat(), this), shareWidget);
+}
+
+/*!
+ \overload
+ \obsolete
+ */
+QGLWidget::QGLWidget(const QGLFormat &format, QWidget *parent,
+ const char *name, const QGLWidget* shareWidget,
+ Qt::WindowFlags f)
+ : QWidget(*(new QGLWidgetPrivate), parent, f | Qt::MSWindowsOwnDC)
+{
+ Q_D(QGLWidget);
+ if (name)
+ setObjectName(QString::fromAscii(name));
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAutoFillBackground(true); // for compatibility
+ d->init(new QGLContext(format, this), shareWidget);
+}
+
+/*!
+ \overload
+ \obsolete
+ */
+QGLWidget::QGLWidget(QGLContext *context, QWidget *parent,
+ const char *name, const QGLWidget *shareWidget, Qt::WindowFlags f)
+ : QWidget(*(new QGLWidgetPrivate), parent, f | Qt::MSWindowsOwnDC)
+{
+ Q_D(QGLWidget);
+ if (name)
+ setObjectName(QString::fromAscii(name));
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAutoFillBackground(true); // for compatibility
+ d->init(context, shareWidget);
+}
+
+#endif // QT3_SUPPORT
+
+typedef GLubyte * (*qt_glGetStringi)(GLenum, GLuint);
+
+#ifndef GL_NUM_EXTENSIONS
+#define GL_NUM_EXTENSIONS 0x821D
+#endif
+
+QGLExtensionMatcher::QGLExtensionMatcher(const char *str)
+{
+ init(str);
+}
+
+QGLExtensionMatcher::QGLExtensionMatcher()
+{
+ const char *extensionStr = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS));
+
+ if (extensionStr) {
+ init(extensionStr);
+ } else {
+ // clear error state
+ while (glGetError()) {}
+
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (ctx) {
+ qt_glGetStringi glGetStringi = (qt_glGetStringi)ctx->getProcAddress(QLatin1String("glGetStringi"));
+
+ 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 QGLExtensionMatcher::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;
+ }
+}
+
+/*
+ Returns the GL extensions for the current context.
+*/
+QGLExtensions::Extensions QGLExtensions::currentContextExtensions()
+{
+ QGLExtensionMatcher extensions;
+ Extensions glExtensions;
+
+ if (extensions.match("GL_ARB_texture_rectangle"))
+ glExtensions |= TextureRectangle;
+ if (extensions.match("GL_ARB_multisample"))
+ glExtensions |= SampleBuffers;
+ if (extensions.match("GL_SGIS_generate_mipmap"))
+ glExtensions |= GenerateMipmap;
+ if (extensions.match("GL_ARB_texture_compression"))
+ glExtensions |= TextureCompression;
+ if (extensions.match("GL_EXT_texture_compression_s3tc"))
+ glExtensions |= DDSTextureCompression;
+ if (extensions.match("GL_OES_compressed_ETC1_RGB8_texture"))
+ glExtensions |= ETC1TextureCompression;
+ if (extensions.match("GL_IMG_texture_compression_pvrtc"))
+ glExtensions |= PVRTCTextureCompression;
+ if (extensions.match("GL_ARB_fragment_program"))
+ glExtensions |= FragmentProgram;
+ if (extensions.match("GL_ARB_fragment_shader"))
+ glExtensions |= FragmentShader;
+ if (extensions.match("GL_ARB_shader_objects"))
+ glExtensions |= FragmentShader;
+ if (extensions.match("GL_ARB_texture_mirrored_repeat"))
+ glExtensions |= MirroredRepeat;
+ if (extensions.match("GL_EXT_framebuffer_object"))
+ glExtensions |= FramebufferObject;
+ if (extensions.match("GL_EXT_stencil_two_side"))
+ glExtensions |= StencilTwoSide;
+ if (extensions.match("GL_EXT_stencil_wrap"))
+ glExtensions |= StencilWrap;
+ if (extensions.match("GL_EXT_packed_depth_stencil"))
+ glExtensions |= PackedDepthStencil;
+ if (extensions.match("GL_NV_float_buffer"))
+ glExtensions |= NVFloatBuffer;
+ if (extensions.match("GL_ARB_pixel_buffer_object"))
+ glExtensions |= PixelBufferObject;
+ if (extensions.match("GL_IMG_texture_format_BGRA8888"))
+ glExtensions |= BGRATextureFormat;
+#if defined(QT_OPENGL_ES_2)
+ glExtensions |= FramebufferObject;
+ glExtensions |= GenerateMipmap;
+ glExtensions |= FragmentShader;
+#endif
+#if defined(QT_OPENGL_ES_1)
+ if (extensions.match("GL_OES_framebuffer_object"))
+ glExtensions |= FramebufferObject;
+#endif
+#if defined(QT_OPENGL_ES)
+ if (extensions.match("GL_OES_packed_depth_stencil"))
+ glExtensions |= PackedDepthStencil;
+ if (extensions.match("GL_OES_element_index_uint"))
+ glExtensions |= ElementIndexUint;
+ if (extensions.match("GL_OES_depth24"))
+ glExtensions |= Depth24;
+#else
+ glExtensions |= ElementIndexUint;
+#endif
+ if (extensions.match("GL_ARB_framebuffer_object")) {
+ // ARB_framebuffer_object also includes EXT_framebuffer_blit.
+ glExtensions |= FramebufferObject;
+ glExtensions |= FramebufferBlit;
+ }
+
+ if (extensions.match("GL_EXT_framebuffer_blit"))
+ glExtensions |= FramebufferBlit;
+
+ if (extensions.match("GL_ARB_texture_non_power_of_two"))
+ glExtensions |= NPOTTextures;
+
+ if (extensions.match("GL_EXT_bgra"))
+ glExtensions |= BGRATextureFormat;
+
+ return glExtensions;
+}
+
+
+class QGLDefaultExtensions
+{
+public:
+ QGLDefaultExtensions() {
+ QGLTemporaryContext tempContext;
+ extensions = QGLExtensions::currentContextExtensions();
+ }
+
+ QGLExtensions::Extensions extensions;
+};
+
+Q_GLOBAL_STATIC(QGLDefaultExtensions, qtDefaultExtensions)
+
+/*
+ Returns the GL extensions for the current QGLContext. If there is no
+ current QGLContext, a default context will be created and the extensions
+ for that context will be returned instead.
+*/
+QGLExtensions::Extensions QGLExtensions::glExtensions()
+{
+ Extensions extensionFlags = 0;
+ QGLContext *currentCtx = const_cast<QGLContext *>(QGLContext::currentContext());
+
+ if (currentCtx && currentCtx->d_func()->extension_flags_cached)
+ return currentCtx->d_func()->extension_flags;
+
+ if (!currentCtx) {
+ extensionFlags = qtDefaultExtensions()->extensions;
+ } else {
+ extensionFlags = currentContextExtensions();
+ currentCtx->d_func()->extension_flags_cached = true;
+ currentCtx->d_func()->extension_flags = extensionFlags;
+ }
+ return extensionFlags;
+}
+
+/*
+ This is the shared initialization for all platforms. Called from QGLWidgetPrivate::init()
+*/
+void QGLWidgetPrivate::initContext(QGLContext *context, const QGLWidget* shareWidget)
+{
+ Q_Q(QGLWidget);
+
+ glDevice.setWidget(q);
+
+ glcx = 0;
+ autoSwap = true;
+
+ if (context && !context->device())
+ context->setDevice(q);
+ q->setContext(context, shareWidget ? shareWidget->context() : 0);
+
+ if (!glcx)
+ glcx = new QGLContext(QGLFormat::defaultFormat(), q);
+}
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS) || defined(Q_WS_QPA)
+Q_GLOBAL_STATIC(QString, qt_gl_lib_name)
+
+Q_OPENGL_EXPORT void qt_set_gl_library_name(const QString& name)
+{
+ qt_gl_lib_name()->operator=(name);
+}
+
+Q_OPENGL_EXPORT const QString qt_gl_library_name()
+{
+ if (qt_gl_lib_name()->isNull()) {
+#ifdef Q_WS_MAC
+ return QLatin1String("/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib");
+#else
+# if defined(QT_OPENGL_ES_1)
+ return QLatin1String("GLES_CM");
+# elif defined(QT_OPENGL_ES_2)
+ return QLatin1String("GLESv2");
+# else
+ return QLatin1String("GL");
+# endif
+#endif // defined Q_WS_MAC
+ }
+ return *qt_gl_lib_name();
+}
+#endif
+
+void QGLContextGroup::addShare(const QGLContext *context, const QGLContext *share) {
+ Q_ASSERT(context && share);
+ if (context->d_ptr->group == share->d_ptr->group)
+ return;
+
+ // Make sure 'context' is not already shared with another group of contexts.
+ Q_ASSERT(context->d_ptr->group->m_refs == 1);
+
+ // Free 'context' group resources and make it use the same resources as 'share'.
+ QGLContextGroup *group = share->d_ptr->group;
+ delete context->d_ptr->group;
+ context->d_ptr->group = group;
+ group->m_refs.ref();
+
+ // Maintain a list of all the contexts in each group of sharing contexts.
+ // The list is empty if the "share" context wasn't sharing already.
+ if (group->m_shares.isEmpty())
+ group->m_shares.append(share);
+ group->m_shares.append(context);
+}
+
+void QGLContextGroup::removeShare(const QGLContext *context) {
+ // Remove the context from the group.
+ QGLContextGroup *group = context->d_ptr->group;
+ if (group->m_shares.isEmpty())
+ return;
+ group->m_shares.removeAll(context);
+
+ // Update context group representative.
+ Q_ASSERT(group->m_shares.size() != 0);
+ if (group->m_context == context)
+ group->m_context = group->m_shares[0];
+
+ // If there is only one context left, then make the list empty.
+ if (group->m_shares.size() == 1)
+ group->m_shares.clear();
+}
+
+QGLContextGroupResourceBase::QGLContextGroupResourceBase()
+ : active(0)
+{
+#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
+ qDebug("Creating context group resource object %p.", this);
+#endif
+}
+
+QGLContextGroupResourceBase::~QGLContextGroupResourceBase()
+{
+#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
+ qDebug("Deleting context group resource %p. Group size: %d.", this, m_groups.size());
+#endif
+ for (int i = 0; i < m_groups.size(); ++i) {
+ m_groups.at(i)->m_resources.remove(this);
+ active.deref();
+ }
+#ifndef QT_NO_DEBUG
+ if (active != 0) {
+ qWarning("QtOpenGL: Resources are still available at program shutdown.\n"
+ " This is possibly caused by a leaked QGLWidget, \n"
+ " QGLFramebufferObject or QGLPixelBuffer.");
+ }
+#endif
+}
+
+void QGLContextGroupResourceBase::insert(const QGLContext *context, void *value)
+{
+#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
+ qDebug("Inserting context group resource %p for context %p, managed by %p.", value, context, this);
+#endif
+ QGLContextGroup *group = QGLContextPrivate::contextGroup(context);
+ Q_ASSERT(!group->m_resources.contains(this));
+ group->m_resources.insert(this, value);
+ m_groups.append(group);
+ active.ref();
+}
+
+void *QGLContextGroupResourceBase::value(const QGLContext *context)
+{
+ QGLContextGroup *group = QGLContextPrivate::contextGroup(context);
+ return group->m_resources.value(this, 0);
+}
+
+void QGLContextGroupResourceBase::cleanup(const QGLContext *ctx)
+{
+ void *resource = value(ctx);
+
+ if (resource != 0) {
+ QGLShareContextScope scope(ctx);
+ freeResource(resource);
+
+ QGLContextGroup *group = QGLContextPrivate::contextGroup(ctx);
+ group->m_resources.remove(this);
+ m_groups.removeOne(group);
+ active.deref();
+ }
+}
+
+void QGLContextGroupResourceBase::cleanup(const QGLContext *ctx, void *value)
+{
+#ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
+ qDebug("Cleaning up context group resource %p, for context %p in thread %p.", this, ctx, QThread::currentThread());
+#endif
+ QGLShareContextScope scope(ctx);
+ freeResource(value);
+ active.deref();
+
+ QGLContextGroup *group = QGLContextPrivate::contextGroup(ctx);
+ m_groups.removeOne(group);
+}
+
+void QGLContextGroup::cleanupResources(const QGLContext *context)
+{
+ // If there are still shares, then no cleanup to be done yet.
+ if (m_shares.size() > 1)
+ return;
+
+ // Iterate over all resources and free each in turn.
+ QHash<QGLContextGroupResourceBase *, void *>::ConstIterator it;
+ for (it = m_resources.begin(); it != m_resources.end(); ++it)
+ it.key()->cleanup(context, it.value());
+}
+
+QGLSharedResourceGuard::~QGLSharedResourceGuard()
+{
+ if (m_group)
+ m_group->removeGuard(this);
+}
+
+void QGLSharedResourceGuard::setContext(const QGLContext *context)
+{
+ if (m_group)
+ m_group->removeGuard(this);
+ if (context) {
+ m_group = QGLContextPrivate::contextGroup(context);
+ m_group->addGuard(this);
+ } else {
+ m_group = 0;
+ }
+}
+
+QSize QGLTexture::bindCompressedTexture
+ (const QString& fileName, const char *format)
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly))
+ return QSize();
+ QByteArray contents = file.readAll();
+ file.close();
+ return bindCompressedTexture
+ (contents.constData(), contents.size(), format);
+}
+
+// PVR header format for container files that store textures compressed
+// with the ETC1, PVRTC2, and PVRTC4 encodings. Format information from the
+// PowerVR SDK at http://www.imgtec.com/powervr/insider/powervr-sdk.asp
+// "PVRTexTool Reference Manual, version 1.11f".
+struct PvrHeader
+{
+ quint32 headerSize;
+ quint32 height;
+ quint32 width;
+ quint32 mipMapCount;
+ quint32 flags;
+ quint32 dataSize;
+ quint32 bitsPerPixel;
+ quint32 redMask;
+ quint32 greenMask;
+ quint32 blueMask;
+ quint32 alphaMask;
+ quint32 magic;
+ quint32 surfaceCount;
+};
+
+#define PVR_MAGIC 0x21525650 // "PVR!" in little-endian
+
+#define PVR_FORMAT_MASK 0x000000FF
+#define PVR_FORMAT_PVRTC2 0x00000018
+#define PVR_FORMAT_PVRTC4 0x00000019
+#define PVR_FORMAT_ETC1 0x00000036
+
+#define PVR_HAS_MIPMAPS 0x00000100
+#define PVR_TWIDDLED 0x00000200
+#define PVR_NORMAL_MAP 0x00000400
+#define PVR_BORDER_ADDED 0x00000800
+#define PVR_CUBE_MAP 0x00001000
+#define PVR_FALSE_COLOR_MIPMAPS 0x00002000
+#define PVR_VOLUME_TEXTURE 0x00004000
+#define PVR_ALPHA_IN_TEXTURE 0x00008000
+#define PVR_VERTICAL_FLIP 0x00010000
+
+#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG
+#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
+#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
+#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
+#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
+#endif
+
+#ifndef GL_ETC1_RGB8_OES
+#define GL_ETC1_RGB8_OES 0x8D64
+#endif
+
+bool QGLTexture::canBindCompressedTexture
+ (const char *buf, int len, const char *format, bool *hasAlpha)
+{
+ if (QSysInfo::ByteOrder != QSysInfo::LittleEndian) {
+ // Compressed texture loading only supported on little-endian
+ // systems such as x86 and ARM at the moment.
+ return false;
+ }
+ if (!format) {
+ // Auto-detect the format from the header.
+ if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) {
+ *hasAlpha = true;
+ return true;
+ } else if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) {
+ const PvrHeader *pvrHeader =
+ reinterpret_cast<const PvrHeader *>(buf);
+ *hasAlpha = (pvrHeader->alphaMask != 0);
+ return true;
+ }
+ } else {
+ // Validate the format against the header.
+ if (!qstricmp(format, "DDS")) {
+ if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) {
+ *hasAlpha = true;
+ return true;
+ }
+ } else if (!qstricmp(format, "PVR") || !qstricmp(format, "ETC1")) {
+ if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) {
+ const PvrHeader *pvrHeader =
+ reinterpret_cast<const PvrHeader *>(buf);
+ *hasAlpha = (pvrHeader->alphaMask != 0);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+#define ctx QGLContext::currentContext()
+
+QSize QGLTexture::bindCompressedTexture
+ (const char *buf, int len, const char *format)
+{
+ if (QSysInfo::ByteOrder != QSysInfo::LittleEndian) {
+ // Compressed texture loading only supported on little-endian
+ // systems such as x86 and ARM at the moment.
+ return QSize();
+ }
+#if !defined(QT_OPENGL_ES)
+ if (!glCompressedTexImage2D) {
+ if (!(QGLExtensions::glExtensions() & QGLExtensions::TextureCompression)) {
+ qWarning("QGLContext::bindTexture(): The GL implementation does "
+ "not support texture compression extensions.");
+ return QSize();
+ }
+ glCompressedTexImage2D = (_glCompressedTexImage2DARB) ctx->getProcAddress(QLatin1String("glCompressedTexImage2DARB"));
+ if (!glCompressedTexImage2D) {
+ qWarning("QGLContext::bindTexture(): could not resolve "
+ "glCompressedTexImage2DARB.");
+ return QSize();
+ }
+ }
+#endif
+ if (!format) {
+ // Auto-detect the format from the header.
+ if (len >= 4 && !qstrncmp(buf, "DDS ", 4))
+ return bindCompressedTextureDDS(buf, len);
+ else if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4))
+ return bindCompressedTexturePVR(buf, len);
+ } else {
+ // Validate the format against the header.
+ if (!qstricmp(format, "DDS")) {
+ if (len >= 4 && !qstrncmp(buf, "DDS ", 4))
+ return bindCompressedTextureDDS(buf, len);
+ } else if (!qstricmp(format, "PVR") || !qstricmp(format, "ETC1")) {
+ if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4))
+ return bindCompressedTexturePVR(buf, len);
+ }
+ }
+ return QSize();
+}
+
+QSize QGLTexture::bindCompressedTextureDDS(const char *buf, int len)
+{
+ // We only support 2D texture loading at present.
+ if (target != GL_TEXTURE_2D)
+ return QSize();
+
+ // Bail out if the necessary extension is not present.
+ if (!(QGLExtensions::glExtensions() & QGLExtensions::DDSTextureCompression)) {
+ qWarning("QGLContext::bindTexture(): DDS texture compression is not supported.");
+ return QSize();
+ }
+
+ const DDSFormat *ddsHeader = reinterpret_cast<const DDSFormat *>(buf + 4);
+ if (!ddsHeader->dwLinearSize) {
+ qWarning("QGLContext::bindTexture(): DDS image size is not valid.");
+ return QSize();
+ }
+
+ int blockSize = 16;
+ GLenum format;
+
+ switch(ddsHeader->ddsPixelFormat.dwFourCC) {
+ case FOURCC_DXT1:
+ format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+ blockSize = 8;
+ break;
+ case FOURCC_DXT3:
+ format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+ break;
+ case FOURCC_DXT5:
+ format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+ break;
+ default:
+ qWarning("QGLContext::bindTexture(): DDS image format not supported.");
+ return QSize();
+ }
+
+ const GLubyte *pixels =
+ reinterpret_cast<const GLubyte *>(buf + ddsHeader->dwSize + 4);
+
+ glGenTextures(1, &id);
+ glBindTexture(GL_TEXTURE_2D, id);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ int size;
+ int offset = 0;
+ int available = len - int(ddsHeader->dwSize + 4);
+ int w = ddsHeader->dwWidth;
+ int h = ddsHeader->dwHeight;
+
+ // load mip-maps
+ for(int i = 0; i < (int) ddsHeader->dwMipMapCount; ++i) {
+ if (w == 0) w = 1;
+ if (h == 0) h = 1;
+
+ size = ((w+3)/4) * ((h+3)/4) * blockSize;
+ if (size > available)
+ break;
+ glCompressedTexImage2D(GL_TEXTURE_2D, i, format, w, h, 0,
+ size, pixels + offset);
+ offset += size;
+ available -= size;
+
+ // half size for each mip-map level
+ w = w/2;
+ h = h/2;
+ }
+
+ // DDS images are not inverted.
+ options &= ~QGLContext::InvertedYBindOption;
+
+ return QSize(ddsHeader->dwWidth, ddsHeader->dwHeight);
+}
+
+QSize QGLTexture::bindCompressedTexturePVR(const char *buf, int len)
+{
+ // We only support 2D texture loading at present. Cube maps later.
+ if (target != GL_TEXTURE_2D)
+ return QSize();
+
+ // Determine which texture format we will be loading.
+ const PvrHeader *pvrHeader = reinterpret_cast<const PvrHeader *>(buf);
+ GLenum textureFormat;
+ quint32 minWidth, minHeight;
+ switch (pvrHeader->flags & PVR_FORMAT_MASK) {
+ case PVR_FORMAT_PVRTC2:
+ if (pvrHeader->alphaMask)
+ textureFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
+ else
+ textureFormat = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
+ minWidth = 16;
+ minHeight = 8;
+ break;
+
+ case PVR_FORMAT_PVRTC4:
+ if (pvrHeader->alphaMask)
+ textureFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
+ else
+ textureFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
+ minWidth = 8;
+ minHeight = 8;
+ break;
+
+ case PVR_FORMAT_ETC1:
+ textureFormat = GL_ETC1_RGB8_OES;
+ minWidth = 4;
+ minHeight = 4;
+ break;
+
+ default:
+ qWarning("QGLContext::bindTexture(): PVR image format 0x%x not supported.", int(pvrHeader->flags & PVR_FORMAT_MASK));
+ return QSize();
+ }
+
+ // Bail out if the necessary extension is not present.
+ if (textureFormat == GL_ETC1_RGB8_OES) {
+ if (!(QGLExtensions::glExtensions() &
+ QGLExtensions::ETC1TextureCompression)) {
+ qWarning("QGLContext::bindTexture(): ETC1 texture compression is not supported.");
+ return QSize();
+ }
+ } else {
+ if (!(QGLExtensions::glExtensions() &
+ QGLExtensions::PVRTCTextureCompression)) {
+ qWarning("QGLContext::bindTexture(): PVRTC texture compression is not supported.");
+ return QSize();
+ }
+ }
+
+ // Boundary check on the buffer size.
+ quint32 bufferSize = pvrHeader->headerSize + pvrHeader->dataSize;
+ if (bufferSize > quint32(len)) {
+ qWarning("QGLContext::bindTexture(): PVR image size is not valid.");
+ return QSize();
+ }
+
+ // Create the texture.
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glGenTextures(1, &id);
+ glBindTexture(GL_TEXTURE_2D, id);
+ if (pvrHeader->mipMapCount) {
+ if ((options & QGLContext::LinearFilteringBindOption) != 0) {
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ } else {
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
+ }
+ } else if ((options & QGLContext::LinearFilteringBindOption) != 0) {
+ 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);
+ }
+
+ // Load the compressed mipmap levels.
+ const GLubyte *buffer =
+ reinterpret_cast<const GLubyte *>(buf + pvrHeader->headerSize);
+ bufferSize = pvrHeader->dataSize;
+ quint32 level = 0;
+ quint32 width = pvrHeader->width;
+ quint32 height = pvrHeader->height;
+ while (bufferSize > 0 && level <= pvrHeader->mipMapCount) {
+ quint32 size =
+ (qMax(width, minWidth) * qMax(height, minHeight) *
+ pvrHeader->bitsPerPixel) / 8;
+ if (size > bufferSize)
+ break;
+ glCompressedTexImage2D(GL_TEXTURE_2D, GLint(level), textureFormat,
+ GLsizei(width), GLsizei(height), 0,
+ GLsizei(size), buffer);
+ width /= 2;
+ height /= 2;
+ buffer += size;
+ ++level;
+ }
+
+ // Restore the default pixel alignment for later texture uploads.
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+
+ // Set the invert flag for the texture. The "vertical flip"
+ // flag in PVR is the opposite sense to our sense of inversion.
+ if ((pvrHeader->flags & PVR_VERTICAL_FLIP) != 0)
+ options &= ~QGLContext::InvertedYBindOption;
+ else
+ options |= QGLContext::InvertedYBindOption;
+
+ return QSize(pvrHeader->width, pvrHeader->height);
+}
+
+#undef ctx
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h
new file mode 100644
index 0000000000..c57995d8d0
--- /dev/null
+++ b/src/opengl/qgl.h
@@ -0,0 +1,668 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGL_H
+#define QGL_H
+
+#include <QtGui/qwidget.h>
+#include <QtGui/qpaintengine.h>
+#include <QtOpenGL/qglcolormap.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qscopedpointer.h>
+
+#ifdef Q_WS_QPA
+#include <QtGui/QPlatformWindowFormat>
+#endif
+
+QT_BEGIN_HEADER
+
+#if defined(Q_WS_WIN)
+# include <QtCore/qt_windows.h>
+#endif
+
+#if defined(Q_WS_MAC)
+# include <OpenGL/gl.h>
+#elif defined(QT_OPENGL_ES_1)
+# if defined(Q_OS_MAC)
+# include <OpenGLES/ES1/gl.h>
+# else
+# include <GLES/gl.h>
+# endif
+# ifndef GL_DOUBLE
+# define GL_DOUBLE GL_FLOAT
+# endif
+# ifndef GLdouble
+typedef GLfloat GLdouble;
+# endif
+#elif 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
+# include <GL/gl.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+#if defined(Q_WS_MAC) && defined (QT_BUILD_OPENGL_LIB) && !defined(QT_MAC_USE_COCOA) && !defined(QDOC)
+#define Q_MAC_COMPAT_GL_FUNCTIONS
+
+template <typename T>
+struct QMacGLCompatTypes
+{
+ typedef long CompatGLint;
+ typedef unsigned long CompatGLuint;
+ typedef unsigned long CompatGLenum;
+};
+
+template <>
+struct QMacGLCompatTypes<long>
+{
+ typedef int CompatGLint;
+ typedef unsigned int CompatGLuint;
+ typedef unsigned int CompatGLenum;
+};
+
+typedef QMacGLCompatTypes<GLint>::CompatGLint QMacCompatGLint;
+typedef QMacGLCompatTypes<GLint>::CompatGLuint QMacCompatGLuint;
+typedef QMacGLCompatTypes<GLint>::CompatGLenum QMacCompatGLenum;
+
+#endif
+
+#ifdef QT3_SUPPORT
+#define QGL_VERSION 460
+#define QGL_VERSION_STR "4.6"
+inline QT3_SUPPORT const char *qGLVersion() {
+ return QGL_VERSION_STR;
+}
+#endif
+
+#if defined(Q_WS_WIN) || defined(Q_WS_MAC)
+class QGLCmap;
+#endif
+
+class QPixmap;
+#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
+class QGLOverlayWidget;
+#endif
+class QGLWidgetPrivate;
+class QGLContextPrivate;
+
+// Namespace class:
+namespace QGL
+{
+ Q_OPENGL_EXPORT void setPreferredPaintEngine(QPaintEngine::Type engineType);
+
+ enum FormatOption {
+ DoubleBuffer = 0x0001,
+ DepthBuffer = 0x0002,
+ Rgba = 0x0004,
+ AlphaChannel = 0x0008,
+ AccumBuffer = 0x0010,
+ StencilBuffer = 0x0020,
+ StereoBuffers = 0x0040,
+ DirectRendering = 0x0080,
+ HasOverlay = 0x0100,
+ SampleBuffers = 0x0200,
+ DeprecatedFunctions = 0x0400,
+ SingleBuffer = DoubleBuffer << 16,
+ NoDepthBuffer = DepthBuffer << 16,
+ ColorIndex = Rgba << 16,
+ NoAlphaChannel = AlphaChannel << 16,
+ NoAccumBuffer = AccumBuffer << 16,
+ NoStencilBuffer = StencilBuffer << 16,
+ NoStereoBuffers = StereoBuffers << 16,
+ IndirectRendering = DirectRendering << 16,
+ NoOverlay = HasOverlay << 16,
+ NoSampleBuffers = SampleBuffers << 16,
+ NoDeprecatedFunctions = DeprecatedFunctions << 16
+ };
+ Q_DECLARE_FLAGS(FormatOptions, FormatOption)
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGL::FormatOptions)
+
+class QGLFormatPrivate;
+
+class Q_OPENGL_EXPORT QGLFormat
+{
+public:
+ QGLFormat();
+ QGLFormat(QGL::FormatOptions options, int plane = 0);
+ QGLFormat(const QGLFormat &other);
+ QGLFormat &operator=(const QGLFormat &other);
+ ~QGLFormat();
+
+ void setDepthBufferSize(int size);
+ int depthBufferSize() const;
+
+ void setAccumBufferSize(int size);
+ int accumBufferSize() const;
+
+ void setRedBufferSize(int size);
+ int redBufferSize() const;
+
+ void setGreenBufferSize(int size);
+ int greenBufferSize() const;
+
+ void setBlueBufferSize(int size);
+ int blueBufferSize() const;
+
+ void setAlphaBufferSize(int size);
+ int alphaBufferSize() const;
+
+ void setStencilBufferSize(int size);
+ int stencilBufferSize() const;
+
+ void setSampleBuffers(bool enable);
+ bool sampleBuffers() const;
+
+ void setSamples(int numSamples);
+ int samples() const;
+
+ void setSwapInterval(int interval);
+ int swapInterval() const;
+
+ bool doubleBuffer() const;
+ void setDoubleBuffer(bool enable);
+ bool depth() const;
+ void setDepth(bool enable);
+ bool rgba() const;
+ void setRgba(bool enable);
+ bool alpha() const;
+ void setAlpha(bool enable);
+ bool accum() const;
+ void setAccum(bool enable);
+ bool stencil() const;
+ void setStencil(bool enable);
+ bool stereo() const;
+ void setStereo(bool enable);
+ bool directRendering() const;
+ void setDirectRendering(bool enable);
+ bool hasOverlay() const;
+ void setOverlay(bool enable);
+
+ int plane() const;
+ void setPlane(int plane);
+
+ void setOption(QGL::FormatOptions opt);
+ bool testOption(QGL::FormatOptions opt) const;
+
+ static QGLFormat defaultFormat();
+ static void setDefaultFormat(const QGLFormat& f);
+
+ static QGLFormat defaultOverlayFormat();
+ static void setDefaultOverlayFormat(const QGLFormat& f);
+
+ static bool hasOpenGL();
+ static bool hasOpenGLOverlays();
+
+ void setVersion(int major, int minor);
+ int majorVersion() const;
+ int minorVersion() const;
+
+ enum OpenGLContextProfile {
+ NoProfile,
+ CoreProfile,
+ CompatibilityProfile
+ };
+
+ void setProfile(OpenGLContextProfile profile);
+ OpenGLContextProfile profile() const;
+
+ enum OpenGLVersionFlag {
+ OpenGL_Version_None = 0x00000000,
+ OpenGL_Version_1_1 = 0x00000001,
+ OpenGL_Version_1_2 = 0x00000002,
+ OpenGL_Version_1_3 = 0x00000004,
+ OpenGL_Version_1_4 = 0x00000008,
+ OpenGL_Version_1_5 = 0x00000010,
+ OpenGL_Version_2_0 = 0x00000020,
+ OpenGL_Version_2_1 = 0x00000040,
+ OpenGL_ES_Common_Version_1_0 = 0x00000080,
+ OpenGL_ES_CommonLite_Version_1_0 = 0x00000100,
+ OpenGL_ES_Common_Version_1_1 = 0x00000200,
+ OpenGL_ES_CommonLite_Version_1_1 = 0x00000400,
+ OpenGL_ES_Version_2_0 = 0x00000800,
+ OpenGL_Version_3_0 = 0x00001000,
+ OpenGL_Version_3_1 = 0x00002000,
+ OpenGL_Version_3_2 = 0x00004000,
+ OpenGL_Version_3_3 = 0x00008000,
+ OpenGL_Version_4_0 = 0x00010000
+ };
+ Q_DECLARE_FLAGS(OpenGLVersionFlags, OpenGLVersionFlag)
+
+ static OpenGLVersionFlags openGLVersionFlags();
+
+#if defined(Q_WS_QPA)
+ static QGLFormat fromPlatformWindowFormat(const QPlatformWindowFormat &format);
+ static QPlatformWindowFormat toPlatformWindowFormat(const QGLFormat &format);
+#endif
+private:
+ QGLFormatPrivate *d;
+
+ void detach();
+
+ friend Q_OPENGL_EXPORT bool operator==(const QGLFormat&, const QGLFormat&);
+ friend Q_OPENGL_EXPORT bool operator!=(const QGLFormat&, const QGLFormat&);
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_OPENGL_EXPORT QDebug operator<<(QDebug, const QGLFormat &);
+#endif
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGLFormat::OpenGLVersionFlags)
+
+Q_OPENGL_EXPORT bool operator==(const QGLFormat&, const QGLFormat&);
+Q_OPENGL_EXPORT bool operator!=(const QGLFormat&, const QGLFormat&);
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_OPENGL_EXPORT QDebug operator<<(QDebug, const QGLFormat &);
+#endif
+
+class Q_OPENGL_EXPORT QGLContext
+{
+ Q_DECLARE_PRIVATE(QGLContext)
+public:
+ QGLContext(const QGLFormat& format, QPaintDevice* device);
+ QGLContext(const QGLFormat& format);
+ virtual ~QGLContext();
+
+ virtual bool create(const QGLContext* shareContext = 0);
+ bool isValid() const;
+ bool isSharing() const;
+ void reset();
+
+ static bool areSharing(const QGLContext *context1, const QGLContext *context2);
+
+ QGLFormat format() const;
+ QGLFormat requestedFormat() const;
+ void setFormat(const QGLFormat& format);
+
+ // ### Qt 5: return bools + maybe remove virtuals
+ virtual void makeCurrent();
+ virtual void doneCurrent();
+
+ virtual void swapBuffers() const;
+
+ enum BindOption {
+ NoBindOption = 0x0000,
+ InvertedYBindOption = 0x0001,
+ MipmapBindOption = 0x0002,
+ PremultipliedAlphaBindOption = 0x0004,
+ LinearFilteringBindOption = 0x0008,
+
+ MemoryManagedBindOption = 0x0010, // internal flag
+ CanFlipNativePixmapBindOption = 0x0020, // internal flag
+ TemporarilyCachedBindOption = 0x0040, // internal flag
+
+ DefaultBindOption = LinearFilteringBindOption
+ | InvertedYBindOption
+ | MipmapBindOption,
+ InternalBindOption = MemoryManagedBindOption
+ | PremultipliedAlphaBindOption
+ };
+ Q_DECLARE_FLAGS(BindOptions, BindOption)
+
+ GLuint bindTexture(const QImage &image, GLenum target, GLint format,
+ BindOptions options);
+ GLuint bindTexture(const QPixmap &pixmap, GLenum target, GLint format,
+ BindOptions options);
+
+ GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D,
+ GLint format = GL_RGBA);
+ GLuint bindTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D,
+ GLint format = GL_RGBA);
+ GLuint bindTexture(const QString &fileName);
+
+ void deleteTexture(GLuint tx_id);
+
+ void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ GLuint bindTexture(const QImage &image, QMacCompatGLenum = GL_TEXTURE_2D,
+ QMacCompatGLint format = GL_RGBA);
+ GLuint bindTexture(const QPixmap &pixmap, QMacCompatGLenum = GL_TEXTURE_2D,
+ QMacCompatGLint format = GL_RGBA);
+ GLuint bindTexture(const QImage &image, QMacCompatGLenum, QMacCompatGLint format,
+ BindOptions);
+ GLuint bindTexture(const QPixmap &pixmap, QMacCompatGLenum, QMacCompatGLint format,
+ BindOptions);
+
+ void deleteTexture(QMacCompatGLuint tx_id);
+
+ void drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+#endif
+
+ static void setTextureCacheLimit(int size);
+ static int textureCacheLimit();
+
+ void *getProcAddress(const QString &proc) const;
+ QPaintDevice* device() const;
+ QColor overlayTransparentColor() const;
+
+ static const QGLContext* currentContext();
+
+#ifdef Q_WS_QPA
+ static QGLContext *fromPlatformGLContext(QPlatformGLContext *platformContext);
+#endif
+protected:
+ virtual bool chooseContext(const QGLContext* shareContext = 0);
+
+#if defined(Q_WS_WIN)
+ virtual int choosePixelFormat(void* pfd, HDC pdc);
+#endif
+#if defined(Q_WS_X11) && defined(QT_NO_EGL)
+ virtual void* tryVisual(const QGLFormat& f, int bufDepth = 1);
+ virtual void* chooseVisual();
+#endif
+#if defined(Q_WS_MAC)
+ virtual void* chooseMacVisual(GDHandle);
+#endif
+
+ bool deviceIsPixmap() const;
+ bool windowCreated() const;
+ void setWindowCreated(bool on);
+ bool initialized() const;
+ void setInitialized(bool on);
+ void generateFontDisplayLists(const QFont & fnt, int listBase); // ### Qt 5: remove
+
+ uint colorIndex(const QColor& c) const;
+ void setValid(bool valid);
+ void setDevice(QPaintDevice *pDev);
+
+protected:
+ static QGLContext* currentCtx;
+
+private:
+#ifdef Q_WS_QPA
+ QGLContext(QPlatformGLContext *platformContext);
+#endif
+
+ QScopedPointer<QGLContextPrivate> d_ptr;
+
+ friend class QGLPixelBuffer;
+ friend class QGLPixelBufferPrivate;
+ friend class QGLWidget;
+ friend class QGLWidgetPrivate;
+ friend class QGLGlyphCache;
+ friend class QOpenGLPaintEngine;
+ friend class QOpenGLPaintEnginePrivate;
+ friend class QGL2PaintEngineEx;
+ friend class QGL2PaintEngineExPrivate;
+ friend class QGLEngineShaderManager;
+ friend class QGLWindowSurface;
+ friend class QGLPixmapData;
+ friend class QGLPixmapFilterBase;
+ friend class QGLTextureGlyphCache;
+ friend struct QGLGlyphTexture;
+ friend class QGLContextGroup;
+ friend class QGLSharedResourceGuard;
+ friend class QGLPixmapBlurFilter;
+ friend class QGLExtensions;
+ friend class QGLTexture;
+ friend QGLFormat::OpenGLVersionFlags QGLFormat::openGLVersionFlags();
+#ifdef Q_WS_MAC
+public:
+ void updatePaintDevice();
+private:
+ friend class QMacGLWindowChangeEvent;
+ friend QGLContextPrivate *qt_phonon_get_dptr(const QGLContext *);
+#endif
+ friend class QGLFramebufferObject;
+ friend class QGLFramebufferObjectPrivate;
+ friend class QGLFBOGLPaintDevice;
+ friend class QGLPaintDevice;
+ friend class QGLWidgetGLPaintDevice;
+ friend class QX11GLPixmapData;
+ friend class QX11GLSharedContexts;
+ friend class QGLContextResourceBase;
+private:
+ Q_DISABLE_COPY(QGLContext)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGLContext::BindOptions)
+
+class Q_OPENGL_EXPORT QGLWidget : public QWidget
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QGLWidget)
+public:
+ explicit QGLWidget(QWidget* parent=0,
+ const QGLWidget* shareWidget = 0, Qt::WindowFlags f=0);
+ explicit QGLWidget(QGLContext *context, QWidget* parent=0,
+ const QGLWidget* shareWidget = 0, Qt::WindowFlags f=0);
+ explicit QGLWidget(const QGLFormat& format, QWidget* parent=0,
+ const QGLWidget* shareWidget = 0, Qt::WindowFlags f=0);
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QGLWidget(QWidget* parent, const char* name,
+ const QGLWidget* shareWidget = 0, Qt::WindowFlags f=0);
+ QT3_SUPPORT_CONSTRUCTOR QGLWidget(QGLContext *context, QWidget* parent, const char* name,
+ const QGLWidget* shareWidget = 0, Qt::WindowFlags f=0);
+ QT3_SUPPORT_CONSTRUCTOR QGLWidget(const QGLFormat& format, QWidget* parent, const char* name,
+ const QGLWidget* shareWidget = 0, Qt::WindowFlags f=0);
+#endif
+ ~QGLWidget();
+
+ void qglColor(const QColor& c) const;
+ void qglClearColor(const QColor& c) const;
+
+ bool isValid() const;
+ bool isSharing() const;
+
+ // ### Qt 5: return bools
+ void makeCurrent();
+ void doneCurrent();
+
+ bool doubleBuffer() const;
+ void swapBuffers();
+
+ QGLFormat format() const;
+ void setFormat(const QGLFormat& format);
+
+ const QGLContext* context() const;
+ void setContext(QGLContext* context, const QGLContext* shareContext = 0,
+ bool deleteOldContext = true);
+
+ QPixmap renderPixmap(int w = 0, int h = 0, bool useContext = false);
+ QImage grabFrameBuffer(bool withAlpha = false);
+
+ void makeOverlayCurrent();
+ const QGLContext* overlayContext() const;
+
+ static QImage convertToGLFormat(const QImage& img);
+
+ void setMouseTracking(bool enable);
+
+ const QGLColormap & colormap() const;
+ void setColormap(const QGLColormap & map);
+
+ void renderText(int x, int y, const QString & str,
+ const QFont & fnt = QFont(), int listBase = 2000);
+ void renderText(double x, double y, double z, const QString & str,
+ const QFont & fnt = QFont(), int listBase = 2000);
+ QPaintEngine *paintEngine() const;
+
+ GLuint bindTexture(const QImage &image, GLenum target, GLint format,
+ QGLContext::BindOptions options);
+ GLuint bindTexture(const QPixmap &pixmap, GLenum target, GLint format,
+ QGLContext::BindOptions options);
+
+ GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D,
+ GLint format = GL_RGBA);
+ GLuint bindTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D,
+ GLint format = GL_RGBA);
+
+ GLuint bindTexture(const QString &fileName);
+
+ void deleteTexture(GLuint tx_id);
+
+ void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ GLuint bindTexture(const QImage &image, QMacCompatGLenum = GL_TEXTURE_2D,
+ QMacCompatGLint format = GL_RGBA);
+ GLuint bindTexture(const QPixmap &pixmap, QMacCompatGLenum = GL_TEXTURE_2D,
+ QMacCompatGLint format = GL_RGBA);
+ GLuint bindTexture(const QImage &image, QMacCompatGLenum, QMacCompatGLint format,
+ QGLContext::BindOptions);
+ GLuint bindTexture(const QPixmap &pixmap, QMacCompatGLenum, QMacCompatGLint format,
+ QGLContext::BindOptions);
+
+ void deleteTexture(QMacCompatGLuint tx_id);
+
+ void drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+#endif
+
+public Q_SLOTS:
+ virtual void updateGL();
+ virtual void updateOverlayGL();
+
+protected:
+ bool event(QEvent *);
+ virtual void initializeGL();
+ virtual void resizeGL(int w, int h);
+ virtual void paintGL();
+
+ virtual void initializeOverlayGL();
+ virtual void resizeOverlayGL(int w, int h);
+ virtual void paintOverlayGL();
+
+ void setAutoBufferSwap(bool on);
+ bool autoBufferSwap() const;
+
+ void paintEvent(QPaintEvent*);
+ void resizeEvent(QResizeEvent*);
+
+ virtual void glInit();
+ virtual void glDraw();
+ int fontDisplayListBase(const QFont & fnt, int listBase = 2000); // ### Qt 5: remove
+
+private:
+ Q_DISABLE_COPY(QGLWidget)
+
+#ifdef Q_WS_MAC
+ friend class QMacGLWindowChangeEvent;
+#endif
+ friend class QGLDrawable;
+ friend class QGLPixelBuffer;
+ friend class QGLPixelBufferPrivate;
+ friend class QGLContext;
+ friend class QGLContextPrivate;
+ friend class QGLOverlayWidget;
+ friend class QOpenGLPaintEngine;
+ friend class QGLPaintDevice;
+ friend class QGLWidgetGLPaintDevice;
+};
+
+
+//
+// QGLFormat inline functions
+//
+
+inline bool QGLFormat::doubleBuffer() const
+{
+ return testOption(QGL::DoubleBuffer);
+}
+
+inline bool QGLFormat::depth() const
+{
+ return testOption(QGL::DepthBuffer);
+}
+
+inline bool QGLFormat::rgba() const
+{
+ return testOption(QGL::Rgba);
+}
+
+inline bool QGLFormat::alpha() const
+{
+ return testOption(QGL::AlphaChannel);
+}
+
+inline bool QGLFormat::accum() const
+{
+ return testOption(QGL::AccumBuffer);
+}
+
+inline bool QGLFormat::stencil() const
+{
+ return testOption(QGL::StencilBuffer);
+}
+
+inline bool QGLFormat::stereo() const
+{
+ return testOption(QGL::StereoBuffers);
+}
+
+inline bool QGLFormat::directRendering() const
+{
+ return testOption(QGL::DirectRendering);
+}
+
+inline bool QGLFormat::hasOverlay() const
+{
+ return testOption(QGL::HasOverlay);
+}
+
+inline bool QGLFormat::sampleBuffers() const
+{
+ return testOption(QGL::SampleBuffers);
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGL_H
diff --git a/src/opengl/qgl_egl.cpp b/src/opengl/qgl_egl.cpp
new file mode 100644
index 0000000000..ef36eb94ba
--- /dev/null
+++ b/src/opengl/qgl_egl.cpp
@@ -0,0 +1,351 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qdebug.h>
+#include <QtOpenGL/qgl.h>
+#include <QtOpenGL/qglpixelbuffer.h>
+#include "qgl_p.h"
+#include "qgl_egl_p.h"
+#include "qglpixelbuffer_p.h"
+
+#ifdef Q_WS_X11
+#include <QtGui/private/qpixmap_x11_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QEglProperties *QGLContextPrivate::extraWindowSurfaceCreationProps = NULL;
+
+void qt_eglproperties_set_glformat(QEglProperties& eglProperties, const QGLFormat& glFormat)
+{
+ int redSize = glFormat.redBufferSize();
+ int greenSize = glFormat.greenBufferSize();
+ int blueSize = glFormat.blueBufferSize();
+ int alphaSize = glFormat.alphaBufferSize();
+ int depthSize = glFormat.depthBufferSize();
+ int stencilSize = glFormat.stencilBufferSize();
+ int sampleCount = glFormat.samples();
+
+ // QGLFormat uses a magic value of -1 to indicate "don't care", even when a buffer of that
+ // type has been requested. So we must check QGLFormat's booleans too if size is -1:
+ if (glFormat.alpha() && alphaSize <= 0)
+ alphaSize = 1;
+ if (glFormat.depth() && depthSize <= 0)
+ depthSize = 1;
+ if (glFormat.stencil() && stencilSize <= 0)
+ stencilSize = 1;
+ if (glFormat.sampleBuffers() && sampleCount <= 0)
+ sampleCount = 1;
+
+ // We want to make sure 16-bit configs are chosen over 32-bit configs as they will provide
+ // the best performance. The EGL config selection algorithm is a bit stange in this regard:
+ // The selection criteria for EGL_BUFFER_SIZE is "AtLeast", so we can't use it to discard
+ // 32-bit configs completely from the selection. So it then comes to the sorting algorithm.
+ // The red/green/blue sizes have a sort priority of 3, so they are sorted by first. The sort
+ // order is special and described as "by larger _total_ number of color bits.". So EGL will
+ // put 32-bit configs in the list before the 16-bit configs. However, the spec also goes on
+ // to say "If the requested number of bits in attrib_list for a particular component is 0,
+ // then the number of bits for that component is not considered". This part of the spec also
+ // seems to imply that setting the red/green/blue bits to zero means none of the components
+ // are considered and EGL disregards the entire sorting rule. It then looks to the next
+ // highest priority rule, which is EGL_BUFFER_SIZE. Despite the selection criteria being
+ // "AtLeast" for EGL_BUFFER_SIZE, it's sort order is "smaller" meaning 16-bit configs are
+ // put in the list before 32-bit configs. So, to make sure 16-bit is preffered over 32-bit,
+ // we must set the red/green/blue sizes to zero. This has an unfortunate consequence that
+ // if the application sets the red/green/blue size to 5/6/5 on the QGLFormat, they will
+ // probably get a 32-bit config, even when there's an RGB565 config available. Oh well.
+
+ // Now normalize the values so -1 becomes 0
+ redSize = redSize > 0 ? redSize : 0;
+ greenSize = greenSize > 0 ? greenSize : 0;
+ blueSize = blueSize > 0 ? blueSize : 0;
+ alphaSize = alphaSize > 0 ? alphaSize : 0;
+ depthSize = depthSize > 0 ? depthSize : 0;
+ stencilSize = stencilSize > 0 ? stencilSize : 0;
+ sampleCount = sampleCount > 0 ? sampleCount : 0;
+
+ eglProperties.setValue(EGL_RED_SIZE, redSize);
+ eglProperties.setValue(EGL_GREEN_SIZE, greenSize);
+ eglProperties.setValue(EGL_BLUE_SIZE, blueSize);
+ eglProperties.setValue(EGL_ALPHA_SIZE, alphaSize);
+ eglProperties.setValue(EGL_DEPTH_SIZE, depthSize);
+ eglProperties.setValue(EGL_STENCIL_SIZE, stencilSize);
+ eglProperties.setValue(EGL_SAMPLES, sampleCount);
+ eglProperties.setValue(EGL_SAMPLE_BUFFERS, sampleCount ? 1 : 0);
+}
+
+// Updates "format" with the parameters of the selected configuration.
+void qt_glformat_from_eglconfig(QGLFormat& format, const EGLConfig config)
+{
+ EGLint redSize = 0;
+ EGLint greenSize = 0;
+ EGLint blueSize = 0;
+ EGLint alphaSize = 0;
+ EGLint depthSize = 0;
+ EGLint stencilSize = 0;
+ EGLint sampleCount = 0;
+ EGLint level = 0;
+
+ EGLDisplay display = QEgl::display();
+ eglGetConfigAttrib(display, config, EGL_RED_SIZE, &redSize);
+ eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &greenSize);
+ eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blueSize);
+ eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &alphaSize);
+ eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE, &depthSize);
+ eglGetConfigAttrib(display, config, EGL_STENCIL_SIZE, &stencilSize);
+ eglGetConfigAttrib(display, config, EGL_SAMPLES, &sampleCount);
+ eglGetConfigAttrib(display, config, EGL_LEVEL, &level);
+
+ format.setRedBufferSize(redSize);
+ format.setGreenBufferSize(greenSize);
+ format.setBlueBufferSize(blueSize);
+ format.setAlphaBufferSize(alphaSize);
+ format.setDepthBufferSize(depthSize);
+ format.setStencilBufferSize(stencilSize);
+ format.setSamples(sampleCount);
+ format.setPlane(level);
+ format.setDirectRendering(true); // All EGL contexts are direct-rendered
+ format.setRgba(true); // EGL doesn't support colour index rendering
+ format.setStereo(false); // EGL doesn't support stereo buffers
+ format.setAccumBufferSize(0); // EGL doesn't support accululation buffers
+ format.setDoubleBuffer(true); // We don't support single buffered EGL contexts
+
+ // Clear the EGL error state because some of the above may
+ // have errored out because the attribute is not applicable
+ // to the surface type. Such errors don't matter.
+ eglGetError();
+}
+
+bool QGLFormat::hasOpenGL()
+{
+ return true;
+}
+
+void QGLContext::reset()
+{
+ Q_D(QGLContext);
+ if (!d->valid)
+ return;
+ d->cleanup();
+ doneCurrent();
+ if (d->eglContext && d->ownsEglContext) {
+ d->destroyEglSurfaceForDevice();
+ delete d->eglContext;
+ }
+ d->ownsEglContext = false;
+ d->eglContext = 0;
+ d->eglSurface = EGL_NO_SURFACE;
+ d->crWin = false;
+ d->sharing = false;
+ d->valid = false;
+ d->transpColor = QColor();
+ d->initDone = false;
+ QGLContextGroup::removeShare(this);
+}
+
+void QGLContext::makeCurrent()
+{
+ Q_D(QGLContext);
+ if (!d->valid || !d->eglContext || d->eglSurfaceForDevice() == EGL_NO_SURFACE) {
+ qWarning("QGLContext::makeCurrent(): Cannot make invalid context current");
+ return;
+ }
+
+ if (d->eglContext->makeCurrent(d->eglSurfaceForDevice())) {
+ QGLContextPrivate::setCurrentContext(this);
+ if (!d->workaroundsCached) {
+ d->workaroundsCached = true;
+ const char *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
+ if (renderer && (strstr(renderer, "SGX") || strstr(renderer, "MBX"))) {
+ // PowerVR MBX/SGX chips needs to clear all buffers when starting to render
+ // a new frame, otherwise there will be a performance penalty to pay for
+ // each frame.
+ qDebug() << "Found SGX/MBX driver, enabling FullClearOnEveryFrame";
+ d->workaround_needsFullClearOnEveryFrame = true;
+
+ // Older PowerVR SGX drivers (like the one in the N900) have a
+ // bug which prevents glCopyTexSubImage2D() to work with a POT
+ // or GL_ALPHA texture bound to an FBO. The only way to
+ // identify that driver is to check the EGL version number for it.
+ const char *egl_version = eglQueryString(d->eglContext->display(), EGL_VERSION);
+
+ if (egl_version && strstr(egl_version, "1.3")) {
+ qDebug() << "Found v1.3 driver, enabling brokenFBOReadBack";
+ d->workaround_brokenFBOReadBack = true;
+ } else if (egl_version && strstr(egl_version, "1.4")) {
+ qDebug() << "Found v1.4 driver, enabling brokenTexSubImage";
+ d->workaround_brokenTexSubImage = true;
+
+ // this is a bit complicated; 1.4 version SGX drivers from
+ // Nokia have fixed the brokenFBOReadBack problem, but
+ // official drivers from TI haven't, meaning that things
+ // like the beagleboard are broken unless we hack around it
+ // - but at the same time, we want to not reduce performance
+ // by not enabling this elsewhere.
+ //
+ // so, let's check for a Nokia-specific addon, and only
+ // enable if it isn't present.
+ // (see MeeGo bug #5616)
+ if (!QEgl::hasExtension("EGL_NOK_image_shared")) {
+ // no Nokia extension, this is probably a standard SGX
+ // driver, so enable the workaround
+ qDebug() << "Found non-Nokia v1.4 driver, enabling brokenFBOReadBack";
+ d->workaround_brokenFBOReadBack = true;
+ }
+ }
+ }
+ }
+ }
+}
+
+void QGLContext::doneCurrent()
+{
+ Q_D(QGLContext);
+ if (d->eglContext)
+ d->eglContext->doneCurrent();
+
+ QGLContextPrivate::setCurrentContext(0);
+}
+
+
+void QGLContext::swapBuffers() const
+{
+ Q_D(const QGLContext);
+ if (!d->valid || !d->eglContext)
+ return;
+
+ d->eglContext->swapBuffers(d->eglSurfaceForDevice());
+}
+
+void QGLContextPrivate::destroyEglSurfaceForDevice()
+{
+ if (eglSurface != EGL_NO_SURFACE) {
+#if defined(Q_WS_X11) || defined(Q_OS_SYMBIAN)
+ // Make sure we don't call eglDestroySurface on a surface which
+ // was created for a different winId. This applies only to QGLWidget
+ // paint device, so make sure this is the one we're operating on
+ // (as opposed to a QGLWindowSurface use case).
+ if (paintDevice && paintDevice->devType() == QInternal::Widget) {
+ QWidget *w = static_cast<QWidget *>(paintDevice);
+ if (QGLWidget *wgl = qobject_cast<QGLWidget *>(w)) {
+ if (wgl->d_func()->eglSurfaceWindowId != wgl->winId()) {
+ qWarning("WARNING: Potential EGL surface leak! Not destroying surface.");
+ eglSurface = EGL_NO_SURFACE;
+ return;
+ }
+ }
+ }
+#endif
+ eglDestroySurface(eglContext->display(), eglSurface);
+ eglSurface = EGL_NO_SURFACE;
+ }
+}
+
+EGLSurface QGLContextPrivate::eglSurfaceForDevice() const
+{
+ // If a QPixmapData had to create the QGLContext, we don't have a paintDevice
+ if (!paintDevice)
+ return eglSurface;
+
+#ifdef Q_WS_X11
+ if (paintDevice->devType() == QInternal::Pixmap) {
+ QPixmapData *pmd = static_cast<QPixmap*>(paintDevice)->data_ptr().data();
+ if (pmd->classId() == QPixmapData::X11Class) {
+ QX11PixmapData* x11PixmapData = static_cast<QX11PixmapData*>(pmd);
+ return (EGLSurface)x11PixmapData->gl_surface;
+ }
+ }
+#endif
+
+ if (paintDevice->devType() == QInternal::Pbuffer) {
+ QGLPixelBuffer* pbuf = static_cast<QGLPixelBuffer*>(paintDevice);
+ return pbuf->d_func()->pbuf;
+ }
+
+ return eglSurface;
+}
+
+void QGLContextPrivate::swapRegion(const QRegion &region)
+{
+ if (!valid || !eglContext)
+ return;
+
+ eglContext->swapBuffersRegion2NOK(eglSurfaceForDevice(), &region);
+}
+
+void QGLContextPrivate::setExtraWindowSurfaceCreationProps(QEglProperties *props)
+{
+ extraWindowSurfaceCreationProps = props;
+}
+
+void QGLWidget::setMouseTracking(bool enable)
+{
+ QWidget::setMouseTracking(enable);
+}
+
+QColor QGLContext::overlayTransparentColor() const
+{
+ return d_func()->transpColor;
+}
+
+uint QGLContext::colorIndex(const QColor &c) const
+{
+ Q_UNUSED(c);
+ return 0;
+}
+
+void QGLContext::generateFontDisplayLists(const QFont & fnt, int listBase)
+{
+ Q_UNUSED(fnt);
+ Q_UNUSED(listBase);
+}
+
+void *QGLContext::getProcAddress(const QString &proc) const
+{
+ return (void*)eglGetProcAddress(reinterpret_cast<const char *>(proc.toLatin1().data()));
+}
+
+bool QGLWidgetPrivate::renderCxPm(QPixmap*)
+{
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_egl_p.h b/src/opengl/qgl_egl_p.h
new file mode 100644
index 0000000000..72a84c51f2
--- /dev/null
+++ b/src/opengl/qgl_egl_p.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGL_EGL_P_H
+#define QGL_EGL_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 QGLWidget class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qegl_p.h>
+#include <QtGui/private/qeglcontext_p.h>
+#include <QtGui/private/qeglproperties_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGLFormat;
+
+void qt_eglproperties_set_glformat(QEglProperties& props, const QGLFormat& format);
+void qt_glformat_from_eglconfig(QGLFormat& format, const EGLConfig config);
+
+QT_END_NAMESPACE
+
+#endif // QGL_EGL_P_H
diff --git a/src/opengl/qgl_mac.mm b/src/opengl/qgl_mac.mm
new file mode 100644
index 0000000000..370861073c
--- /dev/null
+++ b/src/opengl/qgl_mac.mm
@@ -0,0 +1,996 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgl.h"
+
+// There are functions that are deprecated in 10.5, but really there's no way around them
+// for Carbon, so just undefine them.
+#undef DEPRECATED_ATTRIBUTE
+#define DEPRECATED_ATTRIBUTE
+#if defined(Q_WS_MAC)
+#ifndef QT_MAC_USE_COCOA
+#ifdef qDebug
+# undef qDebug
+# include <AGL/agl.h>
+# include <AGL/aglRenderers.h>
+# include <OpenGL/gl.h>
+# ifdef QT_NO_DEBUG
+# define qDebug qt_noop(),1?(void)0:qDebug
+# endif
+#else
+# include <AGL/agl.h>
+# include <AGL/aglRenderers.h>
+# include <OpenGL/gl.h>
+#endif
+#else
+#include <private/qcocoaview_mac_p.h>
+#endif
+
+
+#include <OpenGL/gl.h>
+#include <CoreServices/CoreServices.h>
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qgl_p.h>
+#include <private/qpaintengine_opengl_p.h>
+#include <private/qt_mac_p.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+#include <qapplication.h>
+#include <qstack.h>
+#include <qdesktopwidget.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+#ifdef QT_MAC_USE_COCOA
+QT_END_NAMESPACE
+
+QT_FORWARD_DECLARE_CLASS(QWidget)
+QT_FORWARD_DECLARE_CLASS(QWidgetPrivate)
+QT_FORWARD_DECLARE_CLASS(QGLWidgetPrivate)
+
+QT_BEGIN_NAMESPACE
+
+void *qt_current_nsopengl_context()
+{
+ return [NSOpenGLContext currentContext];
+}
+
+static GLint attribValue(NSOpenGLPixelFormat *fmt, NSOpenGLPixelFormatAttribute attrib)
+{
+ GLint res;
+ [fmt getValues:&res forAttribute:attrib forVirtualScreen:0];
+ return res;
+}
+
+static int def(int val, int defVal)
+{
+ return val != -1 ? val : defVal;
+}
+#else
+QRegion qt_mac_get_widget_rgn(const QWidget *widget);
+#endif
+
+extern quint32 *qt_mac_pixmap_get_base(const QPixmap *);
+extern int qt_mac_pixmap_get_bytes_per_line(const QPixmap *);
+extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
+extern void qt_mac_dispose_rgn(RgnHandle); //qregion_mac.cpp
+extern QRegion qt_mac_convert_mac_region(RgnHandle); //qregion_mac.cpp
+extern void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding encoding=0, int len=-1); //qglobal.cpp
+
+/*
+ QGLTemporaryContext implementation
+*/
+
+class QGLTemporaryContextPrivate
+{
+public:
+#ifndef QT_MAC_USE_COCOA
+ AGLContext ctx;
+#else
+ NSOpenGLContext *ctx;
+#endif
+};
+
+QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
+ : d(new QGLTemporaryContextPrivate)
+{
+ d->ctx = 0;
+#ifndef QT_MAC_USE_COCOA
+ GLint attribs[] = {AGL_RGBA, AGL_NONE};
+ AGLPixelFormat fmt = aglChoosePixelFormat(0, 0, attribs);
+ if (!fmt) {
+ qDebug("QGLTemporaryContext: Couldn't find any RGB visuals");
+ return;
+ }
+ d->ctx = aglCreateContext(fmt, 0);
+ if (!d->ctx)
+ qDebug("QGLTemporaryContext: Unable to create context");
+ else
+ aglSetCurrentContext(d->ctx);
+ aglDestroyPixelFormat(fmt);
+#else
+ QMacCocoaAutoReleasePool pool;
+ NSOpenGLPixelFormatAttribute attribs[] = { 0 };
+ NSOpenGLPixelFormat *fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
+ if (!fmt) {
+ qWarning("QGLTemporaryContext: Cannot find any visuals");
+ return;
+ }
+
+ d->ctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:0];
+ if (!d->ctx)
+ qWarning("QGLTemporaryContext: Cannot create context");
+ else
+ [d->ctx makeCurrentContext];
+ [fmt release];
+#endif
+}
+
+QGLTemporaryContext::~QGLTemporaryContext()
+{
+ if (d->ctx) {
+#ifndef QT_MAC_USE_COCOA
+ aglSetCurrentContext(0);
+ aglDestroyContext(d->ctx);
+#else
+ [NSOpenGLContext clearCurrentContext];
+ [d->ctx release];
+#endif
+ }
+}
+
+bool QGLFormat::hasOpenGL()
+{
+ return true;
+}
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ return false;
+}
+
+bool QGLContext::chooseContext(const QGLContext *shareContext)
+{
+ QMacCocoaAutoReleasePool pool;
+ Q_D(QGLContext);
+ d->cx = 0;
+ d->vi = chooseMacVisual(0);
+ if (!d->vi)
+ return false;
+
+#ifndef QT_MAC_USE_COCOA
+ AGLPixelFormat fmt = (AGLPixelFormat)d->vi;
+ GLint res;
+ aglDescribePixelFormat(fmt, AGL_LEVEL, &res);
+ d->glFormat.setPlane(res);
+ if (deviceIsPixmap())
+ res = 0;
+ else
+ aglDescribePixelFormat(fmt, AGL_DOUBLEBUFFER, &res);
+ d->glFormat.setDoubleBuffer(res);
+ aglDescribePixelFormat(fmt, AGL_DEPTH_SIZE, &res);
+ d->glFormat.setDepth(res);
+ if (d->glFormat.depth())
+ d->glFormat.setDepthBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_RGBA, &res);
+ d->glFormat.setRgba(res);
+ aglDescribePixelFormat(fmt, AGL_RED_SIZE, &res);
+ d->glFormat.setRedBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_GREEN_SIZE, &res);
+ d->glFormat.setGreenBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_BLUE_SIZE, &res);
+ d->glFormat.setBlueBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_ALPHA_SIZE, &res);
+ d->glFormat.setAlpha(res);
+ if (d->glFormat.alpha())
+ d->glFormat.setAlphaBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_ACCUM_RED_SIZE, &res);
+ // Bug in Apple OpenGL (rdr://5015603), when we don't have an accumulation
+ // buffer, it still claims that we have a 16-bit one (which is pretty rare).
+ // So, we just assume we can never have a buffer that small.
+ d->glFormat.setAccum(res > 5);
+ if (d->glFormat.accum())
+ d->glFormat.setAccumBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_STENCIL_SIZE, &res);
+ d->glFormat.setStencil(res);
+ if (d->glFormat.stencil())
+ d->glFormat.setStencilBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_STEREO, &res);
+ d->glFormat.setStereo(res);
+ aglDescribePixelFormat(fmt, AGL_SAMPLE_BUFFERS_ARB, &res);
+ d->glFormat.setSampleBuffers(res);
+ if (d->glFormat.sampleBuffers()) {
+ aglDescribePixelFormat(fmt, AGL_SAMPLES_ARB, &res);
+ d->glFormat.setSamples(res);
+ }
+#else
+ NSOpenGLPixelFormat *fmt = static_cast<NSOpenGLPixelFormat *>(d->vi);
+
+ d->glFormat = QGLFormat();
+
+ // ### make sure to reset other options
+ d->glFormat.setDoubleBuffer(attribValue(fmt, NSOpenGLPFADoubleBuffer));
+
+ int depthSize = attribValue(fmt, NSOpenGLPFADepthSize);
+ d->glFormat.setDepth(depthSize > 0);
+ if (depthSize > 0)
+ d->glFormat.setDepthBufferSize(depthSize);
+
+ int alphaSize = attribValue(fmt, NSOpenGLPFAAlphaSize);
+ d->glFormat.setAlpha(alphaSize > 0);
+ if (alphaSize > 0)
+ d->glFormat.setAlphaBufferSize(alphaSize);
+
+ int accumSize = attribValue(fmt, NSOpenGLPFAAccumSize);
+ d->glFormat.setAccum(accumSize > 0);
+ if (accumSize > 0)
+ d->glFormat.setAccumBufferSize(accumSize);
+
+ int stencilSize = attribValue(fmt, NSOpenGLPFAStencilSize);
+ d->glFormat.setStencil(stencilSize > 0);
+ if (stencilSize > 0)
+ d->glFormat.setStencilBufferSize(stencilSize);
+
+ d->glFormat.setStereo(attribValue(fmt, NSOpenGLPFAStereo));
+
+ int sampleBuffers = attribValue(fmt, NSOpenGLPFASampleBuffers);
+ d->glFormat.setSampleBuffers(sampleBuffers);
+ if (sampleBuffers > 0)
+ d->glFormat.setSamples(attribValue(fmt, NSOpenGLPFASamples));
+#endif
+ if (shareContext && (!shareContext->isValid() || !shareContext->d_func()->cx)) {
+ qWarning("QGLContext::chooseContext: Cannot share with invalid context");
+ shareContext = 0;
+ }
+
+ // sharing between rgba and color-index will give wrong colors
+ if (shareContext && (format().rgba() != shareContext->format().rgba()))
+ shareContext = 0;
+
+#ifndef QT_MAC_USE_COCOA
+ AGLContext ctx = aglCreateContext(fmt, (AGLContext) (shareContext ? shareContext->d_func()->cx : 0));
+#else
+ NSOpenGLContext *ctx = [[NSOpenGLContext alloc] initWithFormat:fmt
+ shareContext:(shareContext ? static_cast<NSOpenGLContext *>(shareContext->d_func()->cx)
+ : 0)];
+#endif
+ if (!ctx) {
+#ifndef QT_MAC_USE_COCOA
+ GLenum err = aglGetError();
+ if (err == AGL_BAD_MATCH || err == AGL_BAD_CONTEXT) {
+ if (shareContext && shareContext->d_func()->cx) {
+ qWarning("QGLContext::chooseContext(): Context sharing mismatch!");
+ if (!(ctx = aglCreateContext(fmt, 0)))
+ return false;
+ shareContext = 0;
+ }
+ }
+#else
+ if (shareContext) {
+ ctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:0];
+ if (ctx) {
+ qWarning("QGLContext::chooseContext: Context sharing mismatch");
+ shareContext = 0;
+ }
+ }
+#endif
+ if (!ctx) {
+ qWarning("QGLContext::chooseContext: Unable to create QGLContext");
+ return false;
+ }
+ }
+ d->cx = ctx;
+ if (shareContext && shareContext->d_func()->cx) {
+ QGLContext *share = const_cast<QGLContext *>(shareContext);
+ d->sharing = true;
+ share->d_func()->sharing = true;
+ }
+ if (deviceIsPixmap())
+ updatePaintDevice();
+
+ // vblank syncing
+ GLint interval = d->reqFormat.swapInterval();
+ if (interval != -1) {
+#ifndef QT_MAC_USE_COCOA
+ aglSetInteger((AGLContext)d->cx, AGL_SWAP_INTERVAL, &interval);
+ if (interval != 0)
+ aglEnable((AGLContext)d->cx, AGL_SWAP_INTERVAL);
+ else
+ aglDisable((AGLContext)d->cx, AGL_SWAP_INTERVAL);
+#else
+ [ctx setValues:&interval forParameter:NSOpenGLCPSwapInterval];
+#endif
+ }
+#ifndef QT_MAC_USE_COCOA
+ aglGetInteger((AGLContext)d->cx, AGL_SWAP_INTERVAL, &interval);
+#else
+ [ctx getValues:&interval forParameter:NSOpenGLCPSwapInterval];
+#endif
+ d->glFormat.setSwapInterval(interval);
+ return true;
+}
+
+void *QGLContextPrivate::tryFormat(const QGLFormat &format)
+{
+ static const int Max = 40;
+#ifndef QT_MAC_USE_COCOA
+ GLint attribs[Max], cnt = 0;
+ bool device_is_pixmap = (paintDevice->devType() == QInternal::Pixmap);
+
+ attribs[cnt++] = AGL_RGBA;
+ attribs[cnt++] = AGL_BUFFER_SIZE;
+ attribs[cnt++] = device_is_pixmap ? static_cast<QPixmap *>(paintDevice)->depth() : 32;
+ attribs[cnt++] = AGL_LEVEL;
+ attribs[cnt++] = format.plane();
+
+ if (format.redBufferSize() != -1) {
+ attribs[cnt++] = AGL_RED_SIZE;
+ attribs[cnt++] = format.redBufferSize();
+ }
+ if (format.greenBufferSize() != -1) {
+ attribs[cnt++] = AGL_GREEN_SIZE;
+ attribs[cnt++] = format.greenBufferSize();
+ }
+ if (format.blueBufferSize() != -1) {
+ attribs[cnt++] = AGL_BLUE_SIZE;
+ attribs[cnt++] = format.blueBufferSize();
+ }
+ if (device_is_pixmap) {
+ attribs[cnt++] = AGL_PIXEL_SIZE;
+ attribs[cnt++] = static_cast<QPixmap *>(paintDevice)->depth();
+ attribs[cnt++] = AGL_OFFSCREEN;
+ if (!format.alpha()) {
+ attribs[cnt++] = AGL_ALPHA_SIZE;
+ attribs[cnt++] = 8;
+ }
+ } else {
+ if (format.doubleBuffer())
+ attribs[cnt++] = AGL_DOUBLEBUFFER;
+ }
+
+ if (format.stereo())
+ attribs[cnt++] = AGL_STEREO;
+ if (format.alpha()) {
+ attribs[cnt++] = AGL_ALPHA_SIZE;
+ attribs[cnt++] = format.alphaBufferSize() == -1 ? 8 : format.alphaBufferSize();
+ }
+ if (format.stencil()) {
+ attribs[cnt++] = AGL_STENCIL_SIZE;
+ attribs[cnt++] = format.stencilBufferSize() == -1 ? 8 : format.stencilBufferSize();
+ }
+ if (format.depth()) {
+ attribs[cnt++] = AGL_DEPTH_SIZE;
+ attribs[cnt++] = format.depthBufferSize() == -1 ? 32 : format.depthBufferSize();
+ }
+ if (format.accum()) {
+ attribs[cnt++] = AGL_ACCUM_RED_SIZE;
+ attribs[cnt++] = format.accumBufferSize() == -1 ? 1 : format.accumBufferSize();
+ attribs[cnt++] = AGL_ACCUM_BLUE_SIZE;
+ attribs[cnt++] = format.accumBufferSize() == -1 ? 1 : format.accumBufferSize();
+ attribs[cnt++] = AGL_ACCUM_GREEN_SIZE;
+ attribs[cnt++] = format.accumBufferSize() == -1 ? 1 : format.accumBufferSize();
+ attribs[cnt++] = AGL_ACCUM_ALPHA_SIZE;
+ attribs[cnt++] = format.accumBufferSize() == -1 ? 1 : format.accumBufferSize();
+ }
+ if (format.sampleBuffers()) {
+ attribs[cnt++] = AGL_SAMPLE_BUFFERS_ARB;
+ attribs[cnt++] = 1;
+ attribs[cnt++] = AGL_SAMPLES_ARB;
+ attribs[cnt++] = format.samples() == -1 ? 4 : format.samples();
+ }
+
+ attribs[cnt] = AGL_NONE;
+ Q_ASSERT(cnt < Max);
+ return aglChoosePixelFormat(0, 0, attribs);
+#else
+ NSOpenGLPixelFormatAttribute attribs[Max];
+ int cnt = 0;
+ int devType = paintDevice->devType();
+ bool device_is_pixmap = (devType == QInternal::Pixmap);
+ int depth = device_is_pixmap ? static_cast<QPixmap *>(paintDevice)->depth() : 32;
+
+ attribs[cnt++] = NSOpenGLPFAColorSize;
+ attribs[cnt++] = depth;
+
+ if (device_is_pixmap) {
+ attribs[cnt++] = NSOpenGLPFAOffScreen;
+ } else {
+ if (format.doubleBuffer())
+ attribs[cnt++] = NSOpenGLPFADoubleBuffer;
+ }
+ if (glFormat.stereo())
+ attribs[cnt++] = NSOpenGLPFAStereo;
+ if (device_is_pixmap || format.alpha()) {
+ attribs[cnt++] = NSOpenGLPFAAlphaSize;
+ attribs[cnt++] = def(format.alphaBufferSize(), 8);
+ }
+ if (format.stencil()) {
+ attribs[cnt++] = NSOpenGLPFAStencilSize;
+ attribs[cnt++] = def(format.stencilBufferSize(), 8);
+ }
+ if (format.depth()) {
+ attribs[cnt++] = NSOpenGLPFADepthSize;
+ attribs[cnt++] = def(format.depthBufferSize(), 32);
+ }
+ if (format.accum()) {
+ attribs[cnt++] = NSOpenGLPFAAccumSize;
+ attribs[cnt++] = def(format.accumBufferSize(), 1);
+ }
+ if (format.sampleBuffers()) {
+ attribs[cnt++] = NSOpenGLPFASampleBuffers;
+ attribs[cnt++] = 1;
+ attribs[cnt++] = NSOpenGLPFASamples;
+ attribs[cnt++] = def(format.samples(), 4);
+ }
+
+ if (format.directRendering())
+ attribs[cnt++] = NSOpenGLPFAAccelerated;
+
+ if (devType == QInternal::Pbuffer)
+ attribs[cnt++] = NSOpenGLPFAPixelBuffer;
+
+ attribs[cnt] = 0;
+ Q_ASSERT(cnt < Max);
+ return [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
+#endif
+}
+
+void QGLContextPrivate::clearDrawable()
+{
+ [static_cast<NSOpenGLContext *>(cx) clearDrawable];
+}
+
+/*!
+ \bold{Mac OS X only:} This virtual function tries to find a visual that
+ matches the format, reducing the demands if the original request
+ cannot be met.
+
+ The algorithm for reducing the demands of the format is quite
+ simple-minded, so override this method in your subclass if your
+ application has spcific requirements on visual selection.
+
+ The \a handle argument is always zero and is not used
+
+ \sa chooseContext()
+*/
+
+void *QGLContext::chooseMacVisual(GDHandle /* handle */)
+{
+ Q_D(QGLContext);
+
+ void *fmt = d->tryFormat(d->glFormat);
+ if (!fmt && d->glFormat.stereo()) {
+ d->glFormat.setStereo(false);
+ fmt = d->tryFormat(d->glFormat);
+ }
+ if (!fmt && d->glFormat.sampleBuffers()) {
+ d->glFormat.setSampleBuffers(false);
+ fmt = d->tryFormat(d->glFormat);
+ }
+ if (!fmt)
+ qWarning("QGLContext::chooseMacVisual: Unable to choose a pixel format");
+ return fmt;
+}
+
+void QGLContext::reset()
+{
+ Q_D(QGLContext);
+ if (!d->valid)
+ return;
+ d->cleanup();
+ doneCurrent();
+#ifndef QT_MAC_USE_COCOA
+ if (d->cx)
+ aglDestroyContext((AGLContext)d->cx);
+#else
+ QMacCocoaAutoReleasePool pool;
+ [static_cast<NSOpenGLContext *>(d->cx) release];
+#endif
+ d->cx = 0;
+#ifndef QT_MAC_USE_COCOA
+ if (d->vi)
+ aglDestroyPixelFormat((AGLPixelFormat)d->vi);
+#else
+ [static_cast<NSOpenGLPixelFormat *>(d->vi) release];
+#endif
+ d->vi = 0;
+ d->crWin = false;
+ d->sharing = false;
+ d->valid = false;
+ d->transpColor = QColor();
+ d->initDone = false;
+ QGLContextGroup::removeShare(this);
+}
+
+void QGLContext::makeCurrent()
+{
+ Q_D(QGLContext);
+
+ if (!d->valid) {
+ qWarning("QGLContext::makeCurrent: Cannot make invalid context current");
+ return;
+ }
+#ifndef QT_MAC_USE_COCOA
+ aglSetCurrentContext((AGLContext)d->cx);
+ if (d->update)
+ updatePaintDevice();
+#else
+ [static_cast<NSOpenGLContext *>(d->cx) makeCurrentContext];
+#endif
+ QGLContextPrivate::setCurrentContext(this);
+}
+
+#ifndef QT_MAC_USE_COCOA
+/*
+ Returns the effective scale factor for a widget. For this value to be
+ different than 1, the following must be true:
+ - The system scale factor must be greater than 1.
+ - The widget window must have WA_MacFrameworkScaled set.
+*/
+float qt_mac_get_scale_factor(QWidget *widget)
+{
+ if (!widget | !widget->window())
+ return 1;
+
+ if (widget->window()->testAttribute(Qt::WA_MacFrameworkScaled) == false)
+ return 1;
+
+ float systemScale = QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4 ? HIGetScaleFactor() : 1;
+ if (systemScale == float(1))
+ return 1;
+
+ return systemScale;
+}
+#endif
+
+/*! \internal
+*/
+void QGLContext::updatePaintDevice()
+{
+ Q_D(QGLContext);
+#ifndef QT_MAC_USE_COCOA
+ d->update = false;
+ if (d->paintDevice->devType() == QInternal::Widget) {
+ //get control information
+ QWidget *w = (QWidget *)d->paintDevice;
+ HIViewRef hiview = (HIViewRef)w->winId();
+ WindowRef window = HIViewGetWindow(hiview);
+#ifdef DEBUG_OPENGL_REGION_UPDATE
+ static int serial_no_gl = 0;
+ qDebug("[%d] %p setting on %s::%s %p/%p [%s]", ++serial_no_gl, w,
+ w->metaObject()->className(), w->objectName().toLatin1().constData(),
+ hiview, window, w->handle() ? "Inside" : "Outside");
+#endif
+
+ //update drawable
+ if (0 && w->isWindow() && w->isFullScreen()) {
+ aglSetDrawable((AGLContext)d->cx, 0);
+ aglSetFullScreen((AGLContext)d->cx, w->width(), w->height(), 0, QApplication::desktop()->screenNumber(w));
+ w->hide();
+ } else {
+ AGLDrawable old_draw = aglGetDrawable((AGLContext)d->cx), new_draw = GetWindowPort(window);
+ if (old_draw != new_draw)
+ aglSetDrawable((AGLContext)d->cx, new_draw);
+ }
+
+ float scale = qt_mac_get_scale_factor(w);
+
+ if (!w->isWindow()) {
+ QRegion clp = qt_mac_get_widget_rgn(w); //get drawable area
+
+#ifdef DEBUG_OPENGL_REGION_UPDATE
+ if (clp.isEmpty()) {
+ qDebug(" Empty area!");
+ } else {
+ QVector<QRect> rs = clp.rects();
+ for(int i = 0; i < rs.count(); i++)
+ qDebug(" %d %d %d %d", rs[i].x(), rs[i].y(), rs[i].width(), rs[i].height());
+ }
+#endif
+ //update the clip
+ if (!aglIsEnabled((AGLContext)d->cx, AGL_BUFFER_RECT))
+ aglEnable((AGLContext)d->cx, AGL_BUFFER_RECT);
+ if (clp.isEmpty()) {
+ GLint offs[4] = { 0, 0, 0, 0 };
+ aglSetInteger((AGLContext)d->cx, AGL_BUFFER_RECT, offs);
+ if (aglIsEnabled((AGLContext)d->cx, AGL_CLIP_REGION))
+ aglDisable((AGLContext)d->cx, AGL_CLIP_REGION);
+ } else {
+ HIPoint origin = { 0., 0. };
+ HIViewConvertPoint(&origin, HIViewRef(w->winId()), 0);
+ const GLint offs[4] = { qRound(origin.x),
+ w->window()->frameGeometry().height() * scale
+ - (qRound(origin.y) + w->height() * scale),
+ w->width() * scale, w->height() * scale};
+
+ RgnHandle region = clp.handle(true);
+
+ if (scale != float(1)) {
+ // Sacle the clip region by the scale factor
+ Rect regionBounds;
+ GetRegionBounds(region, &regionBounds);
+ Rect regionBoundsDest = regionBounds;
+ regionBoundsDest.bottom *= scale;
+ regionBoundsDest.right *= scale;
+ MapRgn(region, &regionBounds, &regionBoundsDest);
+ }
+
+ aglSetInteger((AGLContext)d->cx, AGL_BUFFER_RECT, offs);
+ aglSetInteger((AGLContext)d->cx, AGL_CLIP_REGION, (const GLint *)region);
+ if (!aglIsEnabled((AGLContext)d->cx, AGL_CLIP_REGION))
+ aglEnable((AGLContext)d->cx, AGL_CLIP_REGION);
+ }
+ } else {
+ // Set the buffer rect for top-level gl contexts when scaled.
+ if (scale != float(1)) {
+ aglEnable((AGLContext)d->cx, AGL_BUFFER_RECT);
+ const GLint offs[4] = { 0, 0, w->width() * scale , w->height() * scale};
+ aglSetInteger((AGLContext)d->cx, AGL_BUFFER_RECT, offs);
+ }
+ }
+ } else if (d->paintDevice->devType() == QInternal::Pixmap) {
+ QPixmap *pm = reinterpret_cast<QPixmap *>(d->paintDevice);
+
+ unsigned long qdformat = k32ARGBPixelFormat;
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+ qdformat = k32BGRAPixelFormat;
+ Rect rect;
+ SetRect(&rect, 0, 0, pm->width(), pm->height());
+
+ GWorldPtr gworld;
+ NewGWorldFromPtr(&gworld, qdformat, &rect, 0, 0, 0,
+ reinterpret_cast<char *>(qt_mac_pixmap_get_base(pm)),
+ qt_mac_pixmap_get_bytes_per_line(pm));
+
+ PixMapHandle pixmapHandle = GetGWorldPixMap(gworld);
+ aglSetOffScreen(reinterpret_cast<AGLContext>(d->cx), pm->width(), pm->height(),
+ GetPixRowBytes(pixmapHandle), GetPixBaseAddr(pixmapHandle));
+ } else {
+ qWarning("QGLContext::updatePaintDevice(): Not sure how to render OpenGL on this device!");
+ }
+ aglUpdateContext((AGLContext)d->cx);
+
+#else
+ QMacCocoaAutoReleasePool pool;
+
+ if (d->paintDevice->devType() == QInternal::Widget) {
+ //get control information
+ QWidget *w = (QWidget *)d->paintDevice;
+ NSView *view = qt_mac_nativeview_for(w);
+
+ // Trying to attach the GL context to the NSView will fail with
+ // "invalid drawable" if done too soon, but we have to make sure
+ // the connection is made before the first paint event. Using
+ // the NSView do to this check fails as the NSView is visible
+ // before it's safe to connect, and using the NSWindow fails as
+ // the NSWindow will become visible after the first paint event.
+ // This leaves us with the QWidget, who's visible state seems
+ // to match the point in time when it's safe to connect.
+ if (!w || !w->isVisible())
+ return; // Not safe to attach GL context to view yet
+
+ if ([static_cast<NSOpenGLContext *>(d->cx) view] != view && ![view isHidden])
+ [static_cast<NSOpenGLContext *>(d->cx) setView:view];
+ } else if (d->paintDevice->devType() == QInternal::Pixmap) {
+ const QPixmap *pm = static_cast<const QPixmap *>(d->paintDevice);
+ [static_cast<NSOpenGLContext *>(d->cx) setOffScreen:qt_mac_pixmap_get_base(pm)
+ width:pm->width()
+ height:pm->height()
+ rowbytes:qt_mac_pixmap_get_bytes_per_line(pm)];
+ } else {
+ qWarning("QGLContext::updatePaintDevice: Not sure how to render OpenGL on this device");
+ }
+ [static_cast<NSOpenGLContext *>(d->cx) update];
+#endif
+}
+
+void QGLContext::doneCurrent()
+{
+
+ if (
+#ifndef QT_MAC_USE_COCOA
+ aglGetCurrentContext() != (AGLContext) d_func()->cx
+#else
+ [NSOpenGLContext currentContext] != d_func()->cx
+#endif
+ )
+ return;
+
+ QGLContextPrivate::setCurrentContext(0);
+#ifndef QT_MAC_USE_COCOA
+ aglSetCurrentContext(0);
+#else
+ [NSOpenGLContext clearCurrentContext];
+#endif
+}
+
+void QGLContext::swapBuffers() const
+{
+ Q_D(const QGLContext);
+ if (!d->valid)
+ return;
+#ifndef QT_MAC_USE_COCOA
+ aglSwapBuffers((AGLContext)d->cx);
+#else
+ [static_cast<NSOpenGLContext *>(d->cx) flushBuffer];
+#endif
+}
+
+QColor QGLContext::overlayTransparentColor() const
+{
+ return QColor(0, 0, 0); // Invalid color
+}
+
+#ifndef QT_MAC_USE_COCOA
+static QColor cmap[256];
+static bool cmap_init = false;
+#endif
+uint QGLContext::colorIndex(const QColor &c) const
+{
+#ifndef QT_MAC_USE_COCOA
+ int ret = -1;
+ if(!cmap_init) {
+ cmap_init = true;
+ for(int i = 0; i < 256; i++)
+ cmap[i] = QColor();
+ } else {
+ for(int i = 0; i < 256; i++) {
+ if(cmap[i].isValid() && cmap[i] == c) {
+ ret = i;
+ break;
+ }
+ }
+ }
+ if(ret == -1) {
+ for(ret = 0; ret < 256; ret++)
+ if(!cmap[ret].isValid())
+ break;
+ if(ret == 256) {
+ ret = -1;
+ qWarning("QGLContext::colorIndex(): Internal error!");
+ } else {
+ cmap[ret] = c;
+
+ GLint vals[4];
+ vals[0] = ret;
+ vals[1] = c.red();
+ vals[2] = c.green();
+ vals[3] = c.blue();
+ aglSetInteger((AGLContext)d_func()->cx, AGL_COLORMAP_ENTRY, vals);
+ }
+ }
+ return (uint)(ret == -1 ? 0 : ret);
+#else
+ Q_UNUSED(c);
+ return 0;
+#endif
+}
+
+void QGLContext::generateFontDisplayLists(const QFont & /* fnt */, int /* listBase */)
+{
+}
+
+static CFBundleRef qt_getOpenGLBundle()
+{
+ CFBundleRef bundle = 0;
+ CFStringRef urlString = QCFString::toCFStringRef(QLatin1String("/System/Library/Frameworks/OpenGL.framework"));
+ QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
+ urlString, kCFURLPOSIXPathStyle, false);
+ if (url)
+ bundle = CFBundleCreate(kCFAllocatorDefault, url);
+ CFRelease(urlString);
+ return bundle;
+}
+
+void *QGLContext::getProcAddress(const QString &proc) const
+{
+ CFStringRef procName = QCFString(proc).toCFStringRef(proc);
+ void *result = CFBundleGetFunctionPointerForName(QCFType<CFBundleRef>(qt_getOpenGLBundle()),
+ procName);
+ CFRelease(procName);
+ return result;
+}
+#ifndef QT_MAC_USE_COCOA
+/*****************************************************************************
+ QGLWidget AGL-specific code
+ *****************************************************************************/
+
+/****************************************************************************
+ Hacks to glue AGL to an HIView
+ ***************************************************************************/
+QRegion qt_mac_get_widget_rgn(const QWidget *widget)
+{
+ if(!widget->isVisible() || widget->isMinimized())
+ return QRegion();
+ const QRect wrect = QRect(qt_mac_posInWindow(widget), widget->size());
+ if(!wrect.isValid())
+ return QRegion();
+
+ RgnHandle macr = qt_mac_get_rgn();
+ GetControlRegion((HIViewRef)widget->winId(), kControlStructureMetaPart, macr);
+ OffsetRgn(macr, wrect.x(), wrect.y());
+ QRegion ret = qt_mac_convert_mac_region(macr);
+
+ QPoint clip_pos = wrect.topLeft();
+ for(const QWidget *last_clip = 0, *clip = widget; clip; last_clip = clip, clip = clip->parentWidget()) {
+ if(clip != widget) {
+ GetControlRegion((HIViewRef)clip->winId(), kControlStructureMetaPart, macr);
+ OffsetRgn(macr, clip_pos.x(), clip_pos.y());
+ ret &= qt_mac_convert_mac_region(macr);
+ }
+ const QObjectList &children = clip->children();
+ for(int i = children.size()-1; i >= 0; --i) {
+ if(QWidget *child = qobject_cast<QWidget*>(children.at(i))) {
+ if(child == last_clip)
+ break;
+
+ // This check may seem weird, but when we are using a unified toolbar
+ // The widget is actually being owned by that toolbar and not by Qt.
+ // This means that the geometry it reports will be wrong
+ // and will accidentally cause problems when calculating the region
+ // So, it is better to skip these widgets since they aren't the hierarchy
+ // anyway.
+ if (HIViewGetSuperview(HIViewRef(child->winId())) != HIViewRef(clip->winId()))
+ continue;
+
+ if(child->isVisible() && !child->isMinimized() && !child->isTopLevel()) {
+ const QRect childRect = QRect(clip_pos+child->pos(), child->size());
+ if(childRect.isValid() && wrect.intersects(childRect)) {
+ GetControlRegion((HIViewRef)child->winId(), kControlStructureMetaPart, macr);
+ OffsetRgn(macr, childRect.x(), childRect.y());
+ ret -= qt_mac_convert_mac_region(macr);
+ }
+ }
+ }
+ }
+ if(clip->isWindow())
+ break;
+ clip_pos -= clip->pos();
+ }
+ qt_mac_dispose_rgn(macr);
+ return ret;
+}
+
+#endif
+
+void QGLWidget::setMouseTracking(bool enable)
+{
+ QWidget::setMouseTracking(enable);
+}
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+#ifndef QT_MAC_USE_COCOA
+ if (!isWindow())
+ d->glcx->d_func()->update = true;
+#endif
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+#ifdef QT_MAC_USE_COCOA
+ d->glcx->updatePaintDevice();
+#endif
+#ifndef QT_MAC_USE_COCOA
+ float scale = qt_mac_get_scale_factor(this);
+ resizeGL(width() * scale, height() * scale);
+#else
+ resizeGL(width(), height());
+#endif
+}
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return 0;
+}
+
+void QGLWidget::makeOverlayCurrent()
+{
+}
+
+void QGLWidget::updateOverlayGL()
+{
+}
+
+void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+ if (!d->glcx->isValid())
+ d->glcx->create(shareContext ? shareContext : oldcx);
+ if (deleteOldContext && oldcx)
+ delete oldcx;
+}
+
+void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget *shareWidget)
+{
+ Q_Q(QGLWidget);
+
+ initContext(context, shareWidget);
+
+ QWidget *current = q;
+ while (current) {
+ qt_widget_private(current)->glWidgets.append(QWidgetPrivate::GlWidgetInfo(q));
+ if (current->isWindow())
+ break;
+ current = current->parentWidget();
+ }
+}
+
+bool QGLWidgetPrivate::renderCxPm(QPixmap*)
+{
+ return false;
+}
+
+void QGLWidgetPrivate::cleanupColormaps()
+{
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ return d_func()->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap &)
+{
+}
+
+void QGLWidgetPrivate::updatePaintDevice()
+{
+ Q_Q(QGLWidget);
+ glcx->updatePaintDevice();
+ q->update();
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h
new file mode 100644
index 0000000000..50d13c9540
--- /dev/null
+++ b/src/opengl/qgl_p.h
@@ -0,0 +1,903 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGL_P_H
+#define QGL_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 QGLWidget class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtOpenGL/qgl.h"
+#include "QtOpenGL/qglcolormap.h"
+#include "QtCore/qmap.h"
+#include "QtCore/qthread.h"
+#include "QtCore/qthreadstorage.h"
+#include "QtCore/qhash.h"
+#include "QtCore/qatomic.h"
+#include "private/qwidget_p.h"
+#include "qcache.h"
+#include "qglpaintdevice_p.h"
+
+#ifndef QT_NO_EGL
+#include <QtGui/private/qegl_p.h>
+#endif
+
+#if defined(Q_WS_QPA)
+#include <QtGui/QPlatformGLContext>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QGLContext;
+class QGLOverlayWidget;
+class QPixmap;
+class QPixmapFilter;
+#ifdef Q_WS_MAC
+# ifdef qDebug
+# define old_qDebug qDebug
+# undef qDebug
+# endif
+QT_BEGIN_INCLUDE_NAMESPACE
+#ifndef QT_MAC_USE_COCOA
+# include <AGL/agl.h>
+#endif
+QT_END_INCLUDE_NAMESPACE
+# ifdef old_qDebug
+# undef qDebug
+# define qDebug QT_NO_QDEBUG_MACRO
+# undef old_qDebug
+# endif
+class QMacWindowChangeEvent;
+#endif
+
+#ifdef Q_WS_QWS
+class QWSGLWindowSurface;
+#endif
+
+#ifndef QT_NO_EGL
+class QEglContext;
+#endif
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <QtOpenGL/private/qglextensions_p.h>
+QT_END_INCLUDE_NAMESPACE
+
+class QGLFormatPrivate
+{
+public:
+ QGLFormatPrivate()
+ : ref(1)
+ {
+ opts = QGL::DoubleBuffer | QGL::DepthBuffer | QGL::Rgba | QGL::DirectRendering
+ | QGL::StencilBuffer | QGL::DeprecatedFunctions;
+ pln = 0;
+ depthSize = accumSize = stencilSize = redSize = greenSize = blueSize = alphaSize = -1;
+ numSamples = -1;
+ swapInterval = -1;
+ majorVersion = 1;
+ minorVersion = 0;
+ profile = QGLFormat::NoProfile;
+ }
+ QGLFormatPrivate(const QGLFormatPrivate *other)
+ : ref(1),
+ opts(other->opts),
+ pln(other->pln),
+ depthSize(other->depthSize),
+ accumSize(other->accumSize),
+ stencilSize(other->stencilSize),
+ redSize(other->redSize),
+ greenSize(other->greenSize),
+ blueSize(other->blueSize),
+ alphaSize(other->alphaSize),
+ numSamples(other->numSamples),
+ swapInterval(other->swapInterval),
+ majorVersion(other->majorVersion),
+ minorVersion(other->minorVersion),
+ profile(other->profile)
+ {
+ }
+ QAtomicInt ref;
+ QGL::FormatOptions opts;
+ int pln;
+ int depthSize;
+ int accumSize;
+ int stencilSize;
+ int redSize;
+ int greenSize;
+ int blueSize;
+ int alphaSize;
+ int numSamples;
+ int swapInterval;
+ int majorVersion;
+ int minorVersion;
+ QGLFormat::OpenGLContextProfile profile;
+};
+
+class QGLWidgetPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QGLWidget)
+public:
+ QGLWidgetPrivate() : QWidgetPrivate()
+ , disable_clear_on_painter_begin(false)
+#if defined(Q_WS_QWS)
+ , wsurf(0)
+#endif
+#if defined(Q_WS_X11) && !defined(QT_NO_EGL)
+ , eglSurfaceWindowId(0)
+#endif
+#if defined(Q_OS_SYMBIAN)
+ , eglSurfaceWindowId(0)
+#endif
+ {
+ isGLWidget = 1;
+ }
+
+ ~QGLWidgetPrivate() {}
+
+ void init(QGLContext *context, const QGLWidget* shareWidget);
+ void initContext(QGLContext *context, const QGLWidget* shareWidget);
+ bool renderCxPm(QPixmap *pixmap);
+ void cleanupColormaps();
+ void aboutToDestroy() {
+ if (glcx)
+ glcx->reset();
+ }
+
+ QGLContext *glcx;
+ QGLWidgetGLPaintDevice glDevice;
+ bool autoSwap;
+
+ QGLColormap cmap;
+#ifndef QT_OPENGL_ES
+ QMap<QString, int> displayListCache;
+#endif
+
+ bool disable_clear_on_painter_begin;
+
+#if defined(Q_WS_WIN)
+ void updateColormap();
+ QGLContext *olcx;
+#elif defined(Q_WS_X11)
+ QGLOverlayWidget *olw;
+#ifndef QT_NO_EGL
+ void recreateEglSurface();
+ WId eglSurfaceWindowId;
+#endif
+#elif defined(Q_WS_MAC)
+ QGLContext *olcx;
+ void updatePaintDevice();
+#elif defined(Q_WS_QWS)
+ QWSGLWindowSurface *wsurf;
+#endif
+#ifdef Q_OS_SYMBIAN
+ void recreateEglSurface();
+ WId eglSurfaceWindowId;
+#endif
+};
+
+class QGLContextGroupResourceBase;
+class QGLSharedResourceGuard;
+
+// QGLContextPrivate has the responsibility of creating context groups.
+// QGLContextPrivate maintains the reference counter and destroys
+// context groups when needed.
+class QGLContextGroup
+{
+public:
+ ~QGLContextGroup();
+
+ QGLExtensionFuncs &extensionFuncs() {return m_extensionFuncs;}
+ const QGLContext *context() const {return m_context;}
+ bool isSharing() const { return m_shares.size() >= 2; }
+ QList<const QGLContext *> shares() const { return m_shares; }
+
+ void addGuard(QGLSharedResourceGuard *guard);
+ void removeGuard(QGLSharedResourceGuard *guard);
+
+ static void addShare(const QGLContext *context, const QGLContext *share);
+ static void removeShare(const QGLContext *context);
+
+private:
+ QGLContextGroup(const QGLContext *context);
+
+ QGLExtensionFuncs m_extensionFuncs;
+ const QGLContext *m_context; // context group's representative
+ QList<const QGLContext *> m_shares;
+ QHash<QGLContextGroupResourceBase *, void *> m_resources;
+ QGLSharedResourceGuard *m_guards; // double-linked list of active guards.
+ QAtomicInt m_refs;
+
+ void cleanupResources(const QGLContext *ctx);
+
+ friend class QGLContext;
+ friend class QGLContextPrivate;
+ friend class QGLContextGroupResourceBase;
+};
+
+// Get the context that resources for "ctx" will transfer to once
+// "ctx" is destroyed. Returns null if nothing is sharing with ctx.
+Q_OPENGL_EXPORT const QGLContext *qt_gl_transfer_context(const QGLContext *);
+
+// GL extension definitions
+class QGLExtensions {
+public:
+ enum Extension {
+ TextureRectangle = 0x00000001,
+ SampleBuffers = 0x00000002,
+ GenerateMipmap = 0x00000004,
+ TextureCompression = 0x00000008,
+ FragmentProgram = 0x00000010,
+ MirroredRepeat = 0x00000020,
+ FramebufferObject = 0x00000040,
+ StencilTwoSide = 0x00000080,
+ StencilWrap = 0x00000100,
+ PackedDepthStencil = 0x00000200,
+ NVFloatBuffer = 0x00000400,
+ PixelBufferObject = 0x00000800,
+ FramebufferBlit = 0x00001000,
+ NPOTTextures = 0x00002000,
+ BGRATextureFormat = 0x00004000,
+ DDSTextureCompression = 0x00008000,
+ ETC1TextureCompression = 0x00010000,
+ PVRTCTextureCompression = 0x00020000,
+ FragmentShader = 0x00040000,
+ ElementIndexUint = 0x00080000,
+ Depth24 = 0x00100000
+ };
+ Q_DECLARE_FLAGS(Extensions, Extension)
+
+ static Extensions glExtensions();
+ static Extensions currentContextExtensions();
+};
+
+/*
+ QGLTemporaryContext - the main objective of this class is to have a way of
+ creating a GL context and making it current, without going via QGLWidget
+ and friends. At certain points during GL initialization we need a current
+ context in order decide what GL features are available, and to resolve GL
+ extensions. Having a light-weight way of creating such a context saves
+ initial application startup time, and it doesn't wind up creating recursive
+ conflicts.
+ The class currently uses a private d pointer to hide the platform specific
+ types. This could possibly been done inline with #ifdef'ery, but it causes
+ major headaches on e.g. X11 due to namespace pollution.
+*/
+class QGLTemporaryContextPrivate;
+class QGLTemporaryContext {
+public:
+ QGLTemporaryContext(bool directRendering = true, QWidget *parent = 0);
+ ~QGLTemporaryContext();
+
+private:
+ QScopedPointer<QGLTemporaryContextPrivate> d;
+};
+
+class QGLTexture;
+class QGLTextureDestroyer;
+
+// 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 QGLContextResourceBase;
+
+class QGLContextPrivate
+{
+ Q_DECLARE_PUBLIC(QGLContext)
+public:
+ explicit QGLContextPrivate(QGLContext *context);
+ ~QGLContextPrivate();
+ QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format,
+ QGLContext::BindOptions options);
+ QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format, const qint64 key,
+ QGLContext::BindOptions options);
+ QGLTexture *bindTexture(const QPixmap &pixmap, GLenum target, GLint format,
+ QGLContext::BindOptions options);
+ QGLTexture *textureCacheLookup(const qint64 key, GLenum target);
+ void init(QPaintDevice *dev, const QGLFormat &format);
+ QImage convertToGLFormat(const QImage &image, bool force_premul, GLenum texture_format);
+ int maxTextureSize();
+
+ void cleanup();
+
+ void setVertexAttribArrayEnabled(int arrayIndex, bool enabled = true);
+ void syncGlState(); // Makes sure the GL context's state is what we think it is
+ void swapRegion(const QRegion &region);
+
+#if defined(Q_WS_WIN)
+ void updateFormatVersion();
+#endif
+
+#if defined(Q_WS_WIN)
+ HGLRC rc;
+ HDC dc;
+ WId win;
+ int pixelFormatId;
+ QGLCmap* cmap;
+ HBITMAP hbitmap;
+ HDC hbitmap_hdc;
+ Qt::HANDLE threadId;
+#endif
+#ifndef QT_NO_EGL
+ QEglContext *eglContext;
+ EGLSurface eglSurface;
+ void destroyEglSurfaceForDevice();
+ EGLSurface eglSurfaceForDevice() const;
+ static QEglProperties *extraWindowSurfaceCreationProps;
+ static void setExtraWindowSurfaceCreationProps(QEglProperties *props);
+#endif
+
+#if defined(Q_WS_QPA)
+ QPlatformGLContext *platformContext;
+ void setupSharing();
+
+#elif defined(Q_WS_X11) || defined(Q_WS_MAC)
+ void* cx;
+#endif
+#if defined(Q_WS_X11) || defined(Q_WS_MAC)
+ void* vi;
+#endif
+#if defined(Q_WS_X11)
+ void* pbuf;
+ quint32 gpm;
+ int screen;
+ QHash<QPixmapData*, QPixmap> boundPixmaps;
+ QGLTexture *bindTextureFromNativePixmap(QPixmap*, const qint64 key,
+ QGLContext::BindOptions options);
+ static void destroyGlSurfaceForPixmap(QPixmapData*);
+ static void unbindPixmapFromTexture(QPixmapData*);
+#endif
+#if defined(Q_WS_MAC)
+ bool update;
+ void *tryFormat(const QGLFormat &format);
+ void clearDrawable();
+#endif
+ QGLFormat glFormat;
+ QGLFormat reqFormat;
+ GLuint fbo;
+
+ uint valid : 1;
+ uint sharing : 1;
+ uint initDone : 1;
+ uint crWin : 1;
+ uint internal_context : 1;
+ uint version_flags_cached : 1;
+ uint extension_flags_cached : 1;
+
+ // workarounds for driver/hw bugs on different platforms
+ uint workaround_needsFullClearOnEveryFrame : 1;
+ uint workaround_brokenFBOReadBack : 1;
+ uint workaround_brokenTexSubImage : 1;
+ uint workaroundsCached : 1;
+
+ uint workaround_brokenTextureFromPixmap : 1;
+ uint workaround_brokenTextureFromPixmap_init : 1;
+
+ uint workaround_brokenAlphaTexSubImage : 1;
+ uint workaround_brokenAlphaTexSubImage_init : 1;
+
+#ifndef QT_NO_EGL
+ uint ownsEglContext : 1;
+#endif
+
+ QPaintDevice *paintDevice;
+ QColor transpColor;
+ QGLContext *q_ptr;
+ QGLFormat::OpenGLVersionFlags version_flags;
+ QGLExtensions::Extensions extension_flags;
+
+ QGLContextGroup *group;
+ GLint max_texture_size;
+
+ GLuint current_fbo;
+ GLuint default_fbo;
+ QPaintEngine *active_engine;
+ QHash<QGLContextResourceBase *, void *> m_resources;
+ QGLTextureDestroyer *texture_destroyer;
+
+ bool vertexAttributeArraysEnabledState[QT_GL_VERTEX_ARRAY_TRACKED_COUNT];
+
+ static inline QGLContextGroup *contextGroup(const QGLContext *ctx) { return ctx->d_ptr->group; }
+
+#ifdef Q_WS_WIN
+ static inline QGLExtensionFuncs& extensionFuncs(const QGLContext *ctx) { return ctx->d_ptr->group->extensionFuncs(); }
+#endif
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_SYMBIAN)
+ static Q_OPENGL_EXPORT QGLExtensionFuncs qt_extensionFuncs;
+ static Q_OPENGL_EXPORT QGLExtensionFuncs& extensionFuncs(const QGLContext *);
+#endif
+
+ static void setCurrentContext(QGLContext *context);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGLExtensions::Extensions)
+
+// Temporarily make a context current if not already current or
+// shared with the current contex. The previous context is made
+// current when the object goes out of scope.
+class Q_OPENGL_EXPORT QGLShareContextScope
+{
+public:
+ QGLShareContextScope(const QGLContext *ctx)
+ : m_oldContext(0)
+ {
+ QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext());
+ if (currentContext != ctx && !QGLContext::areSharing(ctx, currentContext)) {
+ m_oldContext = currentContext;
+ m_ctx = const_cast<QGLContext *>(ctx);
+ m_ctx->makeCurrent();
+ } else {
+ m_ctx = currentContext;
+ }
+ }
+
+ operator QGLContext *()
+ {
+ return m_ctx;
+ }
+
+ QGLContext *operator->()
+ {
+ return m_ctx;
+ }
+
+ ~QGLShareContextScope()
+ {
+ if (m_oldContext)
+ m_oldContext->makeCurrent();
+ }
+
+private:
+ QGLContext *m_oldContext;
+ QGLContext *m_ctx;
+};
+
+class QGLTextureDestroyer : public QObject
+{
+ Q_OBJECT
+public:
+ QGLTextureDestroyer() : QObject() {
+ qRegisterMetaType<GLuint>("GLuint");
+ connect(this, SIGNAL(freeTexture(QGLContext *, QPixmapData *, GLuint)),
+ this, SLOT(freeTexture_slot(QGLContext *, QPixmapData *, GLuint)));
+ }
+ void emitFreeTexture(QGLContext *context, QPixmapData *boundPixmap, GLuint id) {
+ emit freeTexture(context, boundPixmap, id);
+ }
+
+Q_SIGNALS:
+ void freeTexture(QGLContext *context, QPixmapData *boundPixmap, GLuint id);
+
+private slots:
+ void freeTexture_slot(QGLContext *context, QPixmapData *boundPixmap, GLuint id) {
+ Q_UNUSED(boundPixmap);
+#if defined(Q_WS_X11)
+ if (boundPixmap) {
+ QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
+ context->makeCurrent();
+ // Although glXReleaseTexImage is a glX call, it must be called while there
+ // is a current context - the context the pixmap was bound to a texture in.
+ // Otherwise the release doesn't do anything and you get BadDrawable errors
+ // when you come to delete the context.
+ QGLContextPrivate::unbindPixmapFromTexture(boundPixmap);
+ glDeleteTextures(1, &id);
+ if (oldContext)
+ oldContext->makeCurrent();
+ return;
+ }
+#endif
+ QGLShareContextScope scope(context);
+ glDeleteTextures(1, &id);
+ }
+};
+
+// ### make QGLContext a QObject in 5.0 and remove the proxy stuff
+class Q_OPENGL_EXPORT QGLSignalProxy : public QObject
+{
+ Q_OBJECT
+public:
+ void emitAboutToDestroyContext(const QGLContext *context) {
+ emit aboutToDestroyContext(context);
+ }
+ static QGLSignalProxy *instance();
+Q_SIGNALS:
+ void aboutToDestroyContext(const QGLContext *context);
+};
+
+class QGLTexture {
+public:
+ QGLTexture(QGLContext *ctx = 0, GLuint tx_id = 0, GLenum tx_target = GL_TEXTURE_2D,
+ QGLContext::BindOptions opt = QGLContext::DefaultBindOption)
+ : context(ctx),
+ id(tx_id),
+ target(tx_target),
+ options(opt)
+#if defined(Q_WS_X11)
+ , boundPixmap(0)
+#endif
+ {}
+
+ ~QGLTexture() {
+ if (options & QGLContext::MemoryManagedBindOption) {
+ Q_ASSERT(context);
+#if !defined(Q_WS_X11)
+ QPixmapData *boundPixmap = 0;
+#endif
+ context->d_ptr->texture_destroyer->emitFreeTexture(context, boundPixmap, id);
+ }
+ }
+
+ QGLContext *context;
+ GLuint id;
+ GLenum target;
+
+ QGLContext::BindOptions options;
+
+#if defined(Q_WS_X11)
+ QPixmapData* boundPixmap;
+#endif
+
+ bool canBindCompressedTexture
+ (const char *buf, int len, const char *format, bool *hasAlpha);
+ QSize bindCompressedTexture
+ (const QString& fileName, const char *format = 0);
+ QSize bindCompressedTexture
+ (const char *buf, int len, const char *format = 0);
+ QSize bindCompressedTextureDDS(const char *buf, int len);
+ QSize bindCompressedTexturePVR(const char *buf, int len);
+};
+
+struct QGLTextureCacheKey {
+ qint64 key;
+ QGLContextGroup *group;
+};
+
+inline bool operator==(const QGLTextureCacheKey &a, const QGLTextureCacheKey &b)
+{
+ return a.key == b.key && a.group == b.group;
+}
+
+inline uint qHash(const QGLTextureCacheKey &key)
+{
+ return qHash(key.key) ^ qHash(key.group);
+}
+
+
+class Q_AUTOTEST_EXPORT QGLTextureCache {
+public:
+ QGLTextureCache();
+ ~QGLTextureCache();
+
+ void insert(QGLContext *ctx, qint64 key, QGLTexture *texture, int cost);
+ void remove(qint64 key);
+ inline int size();
+ inline void setMaxCost(int newMax);
+ inline int maxCost();
+ inline QGLTexture* getTexture(QGLContext *ctx, qint64 key);
+
+ bool remove(QGLContext *ctx, GLuint textureId);
+ void removeContextTextures(QGLContext *ctx);
+ static QGLTextureCache *instance();
+ static void cleanupTexturesForCacheKey(qint64 cacheKey);
+ static void cleanupTexturesForPixampData(QPixmapData* pixmap);
+ static void cleanupBeforePixmapDestruction(QPixmapData* pixmap);
+
+private:
+ QCache<QGLTextureCacheKey, QGLTexture> m_cache;
+ QReadWriteLock m_lock;
+};
+
+int QGLTextureCache::size() {
+ QReadLocker locker(&m_lock);
+ return m_cache.size();
+}
+
+void QGLTextureCache::setMaxCost(int newMax)
+{
+ QWriteLocker locker(&m_lock);
+ m_cache.setMaxCost(newMax);
+}
+
+int QGLTextureCache::maxCost()
+{
+ QReadLocker locker(&m_lock);
+ return m_cache.maxCost();
+}
+
+QGLTexture* QGLTextureCache::getTexture(QGLContext *ctx, qint64 key)
+{
+ QReadLocker locker(&m_lock);
+ const QGLTextureCacheKey cacheKey = {key, QGLContextPrivate::contextGroup(ctx)};
+ return m_cache.object(cacheKey);
+}
+
+extern Q_OPENGL_EXPORT QPaintEngine* qt_qgl_paint_engine();
+
+bool qt_gl_preferGL2Engine();
+
+inline GLenum qt_gl_preferredTextureFormat()
+{
+ return (QGLExtensions::glExtensions() & QGLExtensions::BGRATextureFormat) && QSysInfo::ByteOrder == QSysInfo::LittleEndian
+ ? GL_BGRA : GL_RGBA;
+}
+
+inline GLenum qt_gl_preferredTextureTarget()
+{
+#if defined(QT_OPENGL_ES_2)
+ return GL_TEXTURE_2D;
+#else
+ return (QGLExtensions::glExtensions() & QGLExtensions::TextureRectangle)
+ && !qt_gl_preferGL2Engine()
+ ? GL_TEXTURE_RECTANGLE_NV
+ : GL_TEXTURE_2D;
+#endif
+}
+
+/*
+ Base for resources that are shared in a context group.
+*/
+class Q_OPENGL_EXPORT QGLContextGroupResourceBase
+{
+public:
+ QGLContextGroupResourceBase();
+ virtual ~QGLContextGroupResourceBase();
+ void insert(const QGLContext *context, void *value);
+ void *value(const QGLContext *context);
+ void cleanup(const QGLContext *context);
+ void cleanup(const QGLContext *context, void *value);
+ virtual void freeResource(void *value) = 0;
+
+protected:
+ QList<QGLContextGroup *> m_groups;
+
+private:
+ QAtomicInt active;
+};
+
+/*
+ The QGLContextGroupResource template is used to manage a resource
+ for a group of sharing GL contexts. When the last context in the
+ group is destroyed, or when the QGLContextGroupResource object
+ itself is destroyed (implies potential context switches), the
+ resource will be freed.
+
+ The class used as the template class type needs to have a
+ constructor with the following signature:
+ T(const QGLContext *);
+*/
+template <class T>
+class QGLContextGroupResource : public QGLContextGroupResourceBase
+{
+public:
+ ~QGLContextGroupResource() {
+ for (int i = 0; i < m_groups.size(); ++i) {
+ const QGLContext *context = m_groups.at(i)->context();
+ T *resource = reinterpret_cast<T *>(QGLContextGroupResourceBase::value(context));
+ if (resource) {
+ QGLShareContextScope scope(context);
+ delete resource;
+ }
+ }
+ }
+
+ T *value(const QGLContext *context) {
+ T *resource = reinterpret_cast<T *>(QGLContextGroupResourceBase::value(context));
+ if (!resource) {
+ resource = new T(context);
+ insert(context, resource);
+ }
+ return resource;
+ }
+
+protected:
+ void freeResource(void *resource) {
+ delete reinterpret_cast<T *>(resource);
+ }
+};
+
+/*
+ Base for resources that are context specific.
+*/
+class Q_OPENGL_EXPORT QGLContextResourceBase
+{
+public:
+ virtual ~QGLContextResourceBase() {
+ for (int i = 0; i < m_contexts.size(); ++i)
+ m_contexts.at(i)->d_ptr->m_resources.remove(this);
+ }
+
+ void insert(const QGLContext *context, void *value) {
+ context->d_ptr->m_resources.insert(this, value);
+ }
+
+ void *value(const QGLContext *context) {
+ return context->d_ptr->m_resources.value(this, 0);
+ }
+ virtual void freeResource(void *value) = 0;
+
+protected:
+ QList<const QGLContext *> m_contexts;
+};
+
+/*
+ The QGLContextResource template is used to manage a resource for a
+ single GL context. Just before the context is destroyed (while it's
+ still the current context), or when the QGLContextResource object
+ itself is destroyed (implies potential context switches), the
+ resource will be freed. The class used as the template class type
+ needs to have a constructor with the following signature: T(const
+ QGLContext *);
+*/
+template <class T>
+class QGLContextResource : public QGLContextResourceBase
+{
+public:
+ ~QGLContextResource() {
+ for (int i = 0; i < m_contexts.size(); ++i) {
+ const QGLContext *context = m_contexts.at(i);
+ T *resource = reinterpret_cast<T *>(QGLContextResourceBase::value(context));
+ if (resource) {
+ QGLShareContextScope scope(context);
+ delete resource;
+ }
+ }
+ }
+
+ T *value(const QGLContext *context) {
+ T *resource = reinterpret_cast<T *>(QGLContextResourceBase::value(context));
+ if (!resource) {
+ resource = new T(context);
+ insert(context, resource);
+ }
+ return resource;
+ }
+
+protected:
+ void freeResource(void *resource) {
+ delete reinterpret_cast<T *>(resource);
+ }
+};
+
+// Put a guard around a GL object identifier and its context.
+// When the context goes away, a shared context will be used
+// in its place. If there are no more shared contexts, then
+// the identifier is returned as zero - it is assumed that the
+// context destruction cleaned up the identifier in this case.
+class Q_OPENGL_EXPORT QGLSharedResourceGuard
+{
+public:
+ QGLSharedResourceGuard(const QGLContext *context)
+ : m_group(0), m_id(0), m_next(0), m_prev(0)
+ {
+ setContext(context);
+ }
+ QGLSharedResourceGuard(const QGLContext *context, GLuint id)
+ : m_group(0), m_id(id), m_next(0), m_prev(0)
+ {
+ setContext(context);
+ }
+ ~QGLSharedResourceGuard();
+
+ const QGLContext *context() const
+ {
+ return m_group ? m_group->context() : 0;
+ }
+
+ void setContext(const QGLContext *context);
+
+ GLuint id() const
+ {
+ return m_id;
+ }
+
+ void setId(GLuint id)
+ {
+ m_id = id;
+ }
+
+private:
+ QGLContextGroup *m_group;
+ GLuint m_id;
+ QGLSharedResourceGuard *m_next;
+ QGLSharedResourceGuard *m_prev;
+
+ friend class QGLContextGroup;
+};
+
+
+class QGLExtensionMatcher
+{
+public:
+ QGLExtensionMatcher(const char *str);
+ QGLExtensionMatcher();
+
+ 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
+
+template <class T>
+class QGLEngineThreadStorage
+{
+public:
+ QPaintEngine *engine() {
+ QPaintEngine *&localEngine = storage.localData();
+ if (!localEngine)
+ localEngine = new T;
+ return localEngine;
+ }
+
+private:
+ QThreadStorage<QPaintEngine *> storage;
+};
+QT_END_NAMESPACE
+
+#endif // QGL_P_H
diff --git a/src/opengl/qgl_qpa.cpp b/src/opengl/qgl_qpa.cpp
new file mode 100644
index 0000000000..994344c6eb
--- /dev/null
+++ b/src/opengl/qgl_qpa.cpp
@@ -0,0 +1,394 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+#include <QtGui/private/qapplication_p.h>
+#include <QPixmap>
+#include <QDebug>
+
+#include <QtGui/private/qapplication_p.h>
+#include <QtGui/QPlatformWindow>
+
+#include "qgl.h"
+#include "qgl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QGLFormat QGLFormat::fromPlatformWindowFormat(const QPlatformWindowFormat &format)
+{
+ QGLFormat retFormat;
+ retFormat.setAccum(format.accum());
+ if (format.accumBufferSize() >= 0)
+ retFormat.setAccumBufferSize(format.accumBufferSize());
+ retFormat.setAlpha(format.alpha());
+ if (format.alphaBufferSize() >= 0)
+ retFormat.setAlphaBufferSize(format.alphaBufferSize());
+ if (format.blueBufferSize() >= 0)
+ retFormat.setBlueBufferSize(format.blueBufferSize());
+ retFormat.setDepth(format.depth());
+ if (format.depthBufferSize() >= 0)
+ retFormat.setDepthBufferSize(format.depthBufferSize());
+ retFormat.setDirectRendering(format.directRendering());
+ retFormat.setDoubleBuffer(format.doubleBuffer());
+ if (format.greenBufferSize() >= 0)
+ retFormat.setGreenBufferSize(format.greenBufferSize());
+ if (format.redBufferSize() >= 0)
+ retFormat.setRedBufferSize(format.redBufferSize());
+ retFormat.setRgba(format.rgba());
+ retFormat.setSampleBuffers(format.sampleBuffers());
+ retFormat.setSamples(format.sampleBuffers());
+ retFormat.setStencil(format.stencil());
+ if (format.stencilBufferSize() >= 0)
+ retFormat.setStencilBufferSize(format.stencilBufferSize());
+ retFormat.setStereo(format.stereo());
+ retFormat.setSwapInterval(format.swapInterval());
+ return retFormat;
+}
+
+QPlatformWindowFormat QGLFormat::toPlatformWindowFormat(const QGLFormat &format)
+{
+ QPlatformWindowFormat retFormat;
+ retFormat.setAccum(format.accum());
+ if (format.accumBufferSize() >= 0)
+ retFormat.setAccumBufferSize(format.accumBufferSize());
+ retFormat.setAlpha(format.alpha());
+ if (format.alphaBufferSize() >= 0)
+ retFormat.setAlphaBufferSize(format.alphaBufferSize());
+ if (format.blueBufferSize() >= 0)
+ retFormat.setBlueBufferSize(format.blueBufferSize());
+ retFormat.setDepth(format.depth());
+ if (format.depthBufferSize() >= 0)
+ retFormat.setDepthBufferSize(format.depthBufferSize());
+ retFormat.setDirectRendering(format.directRendering());
+ retFormat.setDoubleBuffer(format.doubleBuffer());
+ if (format.greenBufferSize() >= 0)
+ retFormat.setGreenBufferSize(format.greenBufferSize());
+ if (format.redBufferSize() >= 0)
+ retFormat.setRedBufferSize(format.redBufferSize());
+ retFormat.setRgba(format.rgba());
+ retFormat.setSampleBuffers(format.sampleBuffers());
+ if (format.samples() >= 0)
+ retFormat.setSamples(format.samples());
+ retFormat.setStencil(format.stencil());
+ if (format.stencilBufferSize() >= 0)
+ retFormat.setStencilBufferSize(format.stencilBufferSize());
+ retFormat.setStereo(format.stereo());
+ retFormat.setSwapInterval(format.swapInterval());
+ return retFormat;
+}
+
+void QGLContextPrivate::setupSharing() {
+ Q_Q(QGLContext);
+ QPlatformGLContext *sharedPlatformGLContext = platformContext->platformWindowFormat().sharedGLContext();
+ if (sharedPlatformGLContext) {
+ QGLContext *actualSharedContext = QGLContext::fromPlatformGLContext(sharedPlatformGLContext);
+ sharing = true;
+ QGLContextGroup::addShare(q,actualSharedContext);
+ }
+}
+
+bool QGLFormat::hasOpenGL()
+{
+ return QApplicationPrivate::platformIntegration()
+ ->hasCapability(QPlatformIntegration::OpenGL);
+}
+
+void qDeleteQGLContext(void *handle)
+{
+ QGLContext *context = static_cast<QGLContext *>(handle);
+ delete context;
+}
+
+bool QGLContext::chooseContext(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+ if(!d->paintDevice || d->paintDevice->devType() != QInternal::Widget) {
+ d->valid = false;
+ }else {
+ QWidget *widget = static_cast<QWidget *>(d->paintDevice);
+ if (!widget->platformWindow()){
+ QGLFormat glformat = format();
+ QPlatformWindowFormat winFormat = QGLFormat::toPlatformWindowFormat(glformat);
+ if (shareContext) {
+ winFormat.setSharedContext(shareContext->d_func()->platformContext);
+ }
+ winFormat.setWindowApi(QPlatformWindowFormat::OpenGL);
+ winFormat.setWindowSurface(false);
+ widget->setPlatformWindowFormat(winFormat);
+ widget->winId();//make window
+ }
+ d->platformContext = widget->platformWindow()->glContext();
+ Q_ASSERT(d->platformContext);
+ d->glFormat = QGLFormat::fromPlatformWindowFormat(d->platformContext->platformWindowFormat());
+ d->valid =(bool) d->platformContext;
+ if (d->valid) {
+ d->platformContext->setQGLContextHandle(this,qDeleteQGLContext);
+ }
+ d->setupSharing();
+ }
+
+
+ return d->valid;
+}
+
+void QGLContext::reset()
+{
+ Q_D(QGLContext);
+ if (!d->valid)
+ return;
+ d->cleanup();
+
+ d->crWin = false;
+ d->sharing = false;
+ d->valid = false;
+ d->transpColor = QColor();
+ d->initDone = false;
+ QGLContextGroup::removeShare(this);
+ if (d->platformContext) {
+ d->platformContext->setQGLContextHandle(0,0);
+ }
+}
+
+void QGLContext::makeCurrent()
+{
+ Q_D(QGLContext);
+ d->platformContext->makeCurrent();
+
+ if (!d->workaroundsCached) {
+ d->workaroundsCached = true;
+ const char *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
+ if (renderer && strstr(renderer, "Mali")) {
+ d->workaround_brokenFBOReadBack = true;
+ }
+ }
+
+}
+
+void QGLContext::doneCurrent()
+{
+ Q_D(QGLContext);
+ d->platformContext->doneCurrent();
+}
+
+void QGLContext::swapBuffers() const
+{
+ Q_D(const QGLContext);
+ d->platformContext->swapBuffers();
+}
+
+void *QGLContext::getProcAddress(const QString &procName) const
+{
+ Q_D(const QGLContext);
+ return d->platformContext->getProcAddress(procName);
+}
+
+void QGLWidget::setContext(QGLContext *context,
+ const QGLContext* shareContext,
+ bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+
+ if (context->device() == 0) // a context may refere to more than 1 window.
+ context->setDevice(this); //but its better to point to 1 of them than none of them.
+
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+
+ if (!d->glcx->isValid())
+ d->glcx->create(shareContext ? shareContext : oldcx);
+
+ if (deleteOldContext)
+ delete oldcx;
+}
+
+void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget *shareWidget)
+{
+ initContext(context, shareWidget);
+}
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ return false;
+}
+
+QColor QGLContext::overlayTransparentColor() const
+{
+ return QColor(); // Invalid color
+}
+
+uint QGLContext::colorIndex(const QColor&) const
+{
+ return 0;
+}
+
+void QGLContext::generateFontDisplayLists(const QFont & fnt, int listBase)
+{
+ Q_UNUSED(fnt);
+ Q_UNUSED(listBase);
+}
+
+/*
+ QGLTemporaryContext implementation
+*/
+class QGLTemporaryContextPrivate
+{
+public:
+ QWidget *widget;
+ QPlatformGLContext *context;
+};
+
+QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
+ : d(new QGLTemporaryContextPrivate)
+{
+ d->context = const_cast<QPlatformGLContext *>(QPlatformGLContext::currentContext());
+ if (d->context)
+ d->context->doneCurrent();
+ d->widget = new QWidget;
+ d->widget->setGeometry(0,0,3,3);
+ QPlatformWindowFormat format = d->widget->platformWindowFormat();
+ format.setWindowApi(QPlatformWindowFormat::OpenGL);
+ format.setWindowSurface(false);
+ d->widget->setPlatformWindowFormat(format);
+ d->widget->winId();
+
+ d->widget->platformWindow()->glContext()->makeCurrent();
+}
+
+QGLTemporaryContext::~QGLTemporaryContext()
+{
+ d->widget->platformWindow()->glContext()->doneCurrent();
+ if (d->context)
+ d->context->makeCurrent();
+ delete d->widget;
+}
+
+
+bool QGLWidgetPrivate::renderCxPm(QPixmap*)
+{
+ return false;
+}
+
+/*! \internal
+ Free up any allocated colormaps. This fn is only called for
+ top-level widgets.
+*/
+void QGLWidgetPrivate::cleanupColormaps()
+{
+}
+
+void QGLWidget::setMouseTracking(bool enable)
+{
+ Q_UNUSED(enable);
+}
+
+bool QGLWidget::event(QEvent *e)
+{
+ return QWidget::event(e);
+}
+
+void QGLWidget::resizeEvent(QResizeEvent *e)
+{
+ Q_D(QGLWidget);
+
+ QWidget::resizeEvent(e);
+ if (!isValid())
+ return;
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ resizeGL(width(), height());
+}
+
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return 0;
+}
+
+void QGLWidget::makeOverlayCurrent()
+{
+}
+
+
+void QGLWidget::updateOverlayGL()
+{
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ Q_D(const QGLWidget);
+ return d->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap & c)
+{
+ Q_UNUSED(c);
+}
+
+QGLContext::QGLContext(QPlatformGLContext *platformContext)
+ : d_ptr(new QGLContextPrivate(this))
+{
+ Q_D(QGLContext);
+ d->init(0,QGLFormat::fromPlatformWindowFormat(platformContext->platformWindowFormat()));
+ d->platformContext = platformContext;
+ d->platformContext->setQGLContextHandle(this,qDeleteQGLContext);
+ d->valid = true;
+ d->setupSharing();
+}
+
+QGLContext *QGLContext::fromPlatformGLContext(QPlatformGLContext *platformContext)
+{
+ if (!platformContext)
+ return 0;
+ if (platformContext->qGLContextHandle()) {
+ return reinterpret_cast<QGLContext *>(platformContext->qGLContextHandle());
+ }
+ QGLContext *glContext = new QGLContext(platformContext);
+ //Dont call create on context. This can cause the platformFormat to be set on the widget, which
+ //will cause the platformWindow to be recreated.
+ return glContext;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_qws.cpp b/src/opengl/qgl_qws.cpp
new file mode 100644
index 0000000000..6ad2774eaf
--- /dev/null
+++ b/src/opengl/qgl_qws.cpp
@@ -0,0 +1,318 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgl.h"
+#include "qgl_egl_p.h"
+#include "qglpixelbuffer.h"
+
+#include <qglscreen_qws.h>
+#include <qscreenproxy_qws.h>
+#include <private/qglwindowsurface_qws_p.h>
+
+#include <private/qbackingstore_p.h>
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qgl_p.h>
+#include <private/qpaintengine_opengl_p.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+#include <qapplication.h>
+#include <qstack.h>
+#include <qdesktopwidget.h>
+#include <qdebug.h>
+#include <qvarlengtharray.h>
+
+QT_BEGIN_NAMESPACE
+
+static QGLScreen *glScreenForDevice(QPaintDevice *device)
+{
+ QScreen *screen = qt_screen;
+ if (screen->classId() == QScreen::MultiClass) {
+ int screenNumber;
+ if (device && device->devType() == QInternal::Widget)
+ screenNumber = qApp->desktop()->screenNumber(static_cast<QWidget *>(device));
+ else
+ screenNumber = 0;
+ screen = screen->subScreens()[screenNumber];
+ }
+ while (screen->classId() == QScreen::ProxyClass ||
+ screen->classId() == QScreen::TransformedClass) {
+ screen = static_cast<QProxyScreen *>(screen)->screen();
+ }
+ if (screen->classId() == QScreen::GLClass)
+ return static_cast<QGLScreen *>(screen);
+ else
+ return 0;
+}
+
+/*
+ QGLTemporaryContext implementation
+*/
+
+class QGLTemporaryContextPrivate
+{
+public:
+ QGLWidget *widget;
+};
+
+QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
+ : d(new QGLTemporaryContextPrivate)
+{
+ d->widget = new QGLWidget;
+ d->widget->makeCurrent();
+}
+
+QGLTemporaryContext::~QGLTemporaryContext()
+{
+ delete d->widget;
+}
+
+/*****************************************************************************
+ QOpenGL debug facilities
+ *****************************************************************************/
+//#define DEBUG_OPENGL_REGION_UPDATE
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ QGLScreen *glScreen = glScreenForDevice(0);
+ if (glScreen)
+ return (glScreen->options() & QGLScreen::Overlays);
+ else
+ return false;
+}
+
+static EGLSurface qt_egl_create_surface
+ (QEglContext *context, QPaintDevice *device,
+ const QEglProperties *properties = 0)
+{
+ // Get the screen surface functions, which are used to create native ids.
+ QGLScreen *glScreen = glScreenForDevice(device);
+ if (!glScreen)
+ return EGL_NO_SURFACE;
+ QGLScreenSurfaceFunctions *funcs = glScreen->surfaceFunctions();
+ if (!funcs)
+ return EGL_NO_SURFACE;
+
+ // Create the native drawable for the paint device.
+ int devType = device->devType();
+ EGLNativePixmapType pixmapDrawable = 0;
+ EGLNativeWindowType windowDrawable = 0;
+ bool ok;
+ if (devType == QInternal::Pixmap) {
+ ok = funcs->createNativePixmap(static_cast<QPixmap *>(device), &pixmapDrawable);
+ } else if (devType == QInternal::Image) {
+ ok = funcs->createNativeImage(static_cast<QImage *>(device), &pixmapDrawable);
+ } else {
+ ok = funcs->createNativeWindow(static_cast<QWidget *>(device), &windowDrawable);
+ }
+ if (!ok) {
+ qWarning("QEglContext::createSurface(): Cannot create the native EGL drawable");
+ return EGL_NO_SURFACE;
+ }
+
+ // Create the EGL surface to draw into, based on the native drawable.
+ const int *props;
+ if (properties)
+ props = properties->properties();
+ else
+ props = 0;
+ EGLSurface surf;
+ if (devType == QInternal::Widget) {
+ surf = eglCreateWindowSurface
+ (context->display(), context->config(), windowDrawable, props);
+ } else {
+ surf = eglCreatePixmapSurface
+ (context->display(), context->config(), pixmapDrawable, props);
+ }
+ if (surf == EGL_NO_SURFACE)
+ qWarning("QEglContext::createSurface(): Unable to create EGL surface, error = 0x%x", eglGetError());
+ return surf;
+}
+
+bool QGLContext::chooseContext(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+
+ // Validate the device.
+ if (!device())
+ return false;
+ int devType = device()->devType();
+ if (devType != QInternal::Pixmap && devType != QInternal::Image && devType != QInternal::Widget) {
+ qWarning("QGLContext::chooseContext(): Cannot create QGLContext's for paint device type %d", devType);
+ return false;
+ }
+
+ // Get the display and initialize it.
+ d->eglContext = new QEglContext();
+ d->ownsEglContext = true;
+ d->eglContext->setApi(QEgl::OpenGL);
+
+ // Construct the configuration we need for this surface.
+ QEglProperties configProps;
+ qt_eglproperties_set_glformat(configProps, d->glFormat);
+ configProps.setDeviceType(devType);
+ configProps.setPaintDeviceFormat(device());
+ configProps.setRenderableType(QEgl::OpenGL);
+
+ // Search for a matching configuration, reducing the complexity
+ // each time until we get something that matches.
+ if (!d->eglContext->chooseConfig(configProps)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ // Inform the higher layers about the actual format properties.
+ qt_glformat_from_eglconfig(d->glFormat, d->eglContext->config());
+
+ // Create a new context for the configuration.
+ if (!d->eglContext->createContext
+ (shareContext ? shareContext->d_func()->eglContext : 0)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+ d->sharing = d->eglContext->isSharing();
+ if (d->sharing && shareContext)
+ const_cast<QGLContext *>(shareContext)->d_func()->sharing = true;
+
+#if defined(EGL_VERSION_1_1)
+ if (d->glFormat.swapInterval() != -1 && devType == QInternal::Widget)
+ eglSwapInterval(d->eglContext->display(), d->glFormat.swapInterval());
+#endif
+
+ // Create the EGL surface to draw into. We cannot use
+ // QEglContext::createSurface() because it does not have
+ // access to the QGLScreen.
+ d->eglSurface = qt_egl_create_surface(d->eglContext, device());
+ if (d->eglSurface == EGL_NO_SURFACE) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ return true;
+}
+
+
+bool QGLWidget::event(QEvent *e)
+{
+ return QWidget::event(e);
+}
+
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ resizeGL(width(), height());
+ //handle overlay
+}
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return 0;
+}
+
+void QGLWidget::makeOverlayCurrent()
+{
+ //handle overlay
+}
+
+void QGLWidget::updateOverlayGL()
+{
+ //handle overlay
+}
+
+void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if(context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+
+ if(d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+ if(!d->glcx->isValid())
+ d->glcx->create(shareContext ? shareContext : oldcx);
+ if(deleteOldContext)
+ delete oldcx;
+}
+
+void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget* shareWidget)
+{
+ Q_Q(QGLWidget);
+
+ QGLScreen *glScreen = glScreenForDevice(q);
+ if (glScreen) {
+ wsurf = static_cast<QWSGLWindowSurface*>(glScreen->createSurface(q));
+ q->setWindowSurface(wsurf);
+ }
+
+ initContext(context, shareWidget);
+
+ if(q->isValid() && glcx->format().hasOverlay()) {
+ //no overlay
+ qWarning("QtOpenGL ES doesn't currently support overlays");
+ }
+}
+
+void QGLWidgetPrivate::cleanupColormaps()
+{
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ return d_func()->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap &)
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_symbian.cpp b/src/opengl/qgl_symbian.cpp
new file mode 100644
index 0000000000..1b41db47db
--- /dev/null
+++ b/src/opengl/qgl_symbian.cpp
@@ -0,0 +1,472 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qgl.h"
+#include <fbs.h>
+#include <private/qt_s60_p.h>
+#include <private/qpixmap_s60_p.h>
+#include <private/qimagepixmapcleanuphooks_p.h>
+#include <private/qgl_p.h>
+#include <private/qpaintengine_opengl_p.h>
+#include <private/qwidget_p.h> // to access QWExtra
+#include <private/qnativeimagehandleprovider_p.h>
+#include "qgl_egl_p.h"
+#include "qpixmapdata_gl_p.h"
+#include "qgltexturepool_p.h"
+#include "qcolormap.h"
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+// Turn off "direct to window" rendering if EGL cannot support it.
+#if !defined(EGL_RENDER_BUFFER) || !defined(EGL_SINGLE_BUFFER)
+#if defined(QGL_DIRECT_TO_WINDOW)
+#undef QGL_DIRECT_TO_WINDOW
+#endif
+#endif
+
+// Determine if preserved window contents should be used.
+#if !defined(EGL_SWAP_BEHAVIOR) || !defined(EGL_BUFFER_PRESERVED)
+#if !defined(QGL_NO_PRESERVED_SWAP)
+#define QGL_NO_PRESERVED_SWAP 1
+#endif
+#endif
+
+extern int qt_gl_pixmap_serial;
+
+/*
+ QGLTemporaryContext implementation
+*/
+
+
+class QGLTemporaryContextPrivate
+{
+public:
+ bool initialized;
+ RWindow *window;
+ EGLContext context;
+ EGLSurface surface;
+ EGLDisplay display;
+};
+
+QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
+ : d(new QGLTemporaryContextPrivate)
+{
+ d->initialized = false;
+ d->window = 0;
+ d->context = 0;
+ d->surface = 0;
+
+ d->display = d->display = QEgl::display();
+
+ EGLConfig config;
+ int numConfigs = 0;
+ EGLint attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+#ifdef QT_OPENGL_ES_2
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+#endif
+ EGL_NONE
+ };
+
+ eglChooseConfig(d->display, attribs, &config, 1, &numConfigs);
+ if (!numConfigs) {
+ qWarning("QGLTemporaryContext: No EGL configurations available.");
+ return;
+ }
+
+ d->window = new RWindow(CCoeEnv::Static()->WsSession());
+ d->window->Construct(CCoeEnv::Static()->RootWin(),(uint)this);
+
+ d->surface = eglCreateWindowSurface(d->display, config, (EGLNativeWindowType) d->window, NULL);
+
+ if (d->surface == EGL_NO_SURFACE) {
+ qWarning("QGLTemporaryContext: Error creating EGL surface.");
+ delete d->window;
+ d->window = 0;
+ return;
+ }
+
+ EGLint contextAttribs[] = {
+#ifdef QT_OPENGL_ES_2
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+#endif
+ EGL_NONE
+ };
+ d->context = eglCreateContext(d->display, config, 0, contextAttribs);
+ if (d->context != EGL_NO_CONTEXT
+ && eglMakeCurrent(d->display, d->surface, d->surface, d->context))
+ {
+ d->initialized = true;
+ } else {
+ qWarning("QGLTemporaryContext: Error creating EGL context.");
+ d->window = 0;
+ return;
+ }
+}
+
+QGLTemporaryContext::~QGLTemporaryContext()
+{
+ if (d->initialized) {
+ eglMakeCurrent(d->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(d->display, d->context);
+ eglDestroySurface(d->display, d->surface);
+ delete d->window;
+ }
+}
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ return false;
+}
+
+// Chooses the EGL config and creates the EGL context
+bool QGLContext::chooseContext(const QGLContext* shareContext) // almost same as in qgl_x11egl.cpp
+{
+ Q_D(QGLContext);
+
+ if (!device())
+ return false;
+
+ int devType = device()->devType();
+
+ if ((devType != QInternal::Widget) && (devType != QInternal::Pbuffer)) {
+ qWarning("WARNING: Creating a QGLContext not supported on device type %d", devType);
+ return false;
+ }
+
+ // Get the display and initialize it.
+ if (d->eglContext == 0) {
+ d->eglContext = new QEglContext();
+ d->ownsEglContext = true;
+ d->eglContext->setApi(QEgl::OpenGL);
+
+ // If the device is a widget with WA_TranslucentBackground set, make sure the glFormat
+ // has the alpha channel option set:
+ if (devType == QInternal::Widget) {
+ QWidget* widget = static_cast<QWidget*>(device());
+ if (widget->testAttribute(Qt::WA_TranslucentBackground))
+ d->glFormat.setAlpha(true);
+ }
+
+ // Construct the configuration we need for this surface.
+ QEglProperties configProps;
+ configProps.setDeviceType(devType);
+ configProps.setPaintDeviceFormat(device());
+ configProps.setRenderableType(QEgl::OpenGL);
+ configProps.setValue(EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_SWAP_BEHAVIOR_PRESERVED_BIT);
+
+ qt_eglproperties_set_glformat(configProps, d->glFormat);
+
+ if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ // Create a new context for the configuration.
+ QEglContext* eglSharedContext = shareContext ? shareContext->d_func()->eglContext : 0;
+ if (!d->eglContext->createContext(eglSharedContext)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+ d->sharing = d->eglContext->isSharing();
+ if (d->sharing && shareContext)
+ const_cast<QGLContext *>(shareContext)->d_func()->sharing = true;
+ }
+
+ // Inform the higher layers about the actual format properties
+ qt_glformat_from_eglconfig(d->glFormat, d->eglContext->config());
+
+ // Do don't create the EGLSurface for everything.
+ // QWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface
+ // QGLWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface
+ // QGLPixelBuffer - no, it creates the surface itself and stores it in QGLPixelBufferPrivate::pbuf
+
+ if (devType == QInternal::Widget) {
+ if (d->eglSurface != EGL_NO_SURFACE)
+ eglDestroySurface(d->eglContext->display(), d->eglSurface);
+
+ d->eglSurface = QEgl::createSurface(device(), d->eglContext->config());
+
+ eglGetError(); // Clear error state first.
+
+#ifdef QGL_NO_PRESERVED_SWAP
+ eglSurfaceAttrib(QEgl::display(), d->eglSurface,
+ EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
+
+ if (eglGetError() != EGL_SUCCESS)
+ qWarning("QGLContext: could not enable destroyed swap behaviour");
+#else
+ eglSurfaceAttrib(QEgl::display(), d->eglSurface,
+ EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
+
+ if (eglGetError() != EGL_SUCCESS)
+ qWarning("QGLContext: could not enable preserved swap behaviour");
+#endif
+
+ setWindowCreated(true);
+ }
+
+ return true;
+}
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+
+ if (QGLContext::currentContext())
+ doneCurrent();
+
+ // Symbian needs to recreate the surface on resize.
+ d->recreateEglSurface();
+
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ resizeGL(width(), height());
+ //handle overlay
+}
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return 0;
+}
+
+void QGLWidget::makeOverlayCurrent()
+{
+ //handle overlay
+}
+
+void QGLWidget::updateOverlayGL()
+{
+ //handle overlay
+}
+
+void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+ if (!context->deviceIsPixmap() && context->device() != this) {
+ qWarning("QGLWidget::setContext: Context must refer to this widget");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+
+ bool createFailed = false;
+ if (!d->glcx->isValid()) {
+ // Create the QGLContext here, which in turn chooses the EGL config
+ // and creates the EGL context:
+ if (!d->glcx->create(shareContext ? shareContext : oldcx))
+ createFailed = true;
+ }
+ if (createFailed) {
+ if (deleteOldContext)
+ delete oldcx;
+ return;
+ }
+
+ d->eglSurfaceWindowId = winId(); // Remember the window id we created the surface for
+}
+
+void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget* shareWidget)
+{
+ Q_Q(QGLWidget);
+
+ initContext(context, shareWidget);
+
+ if(q->isValid() && glcx->format().hasOverlay()) {
+ //no overlay
+ qWarning("QtOpenGL ES doesn't currently support overlays");
+ }
+}
+
+void QGLWidgetPrivate::cleanupColormaps()
+{
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ return d_func()->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap &)
+{
+}
+
+void QGLWidgetPrivate::recreateEglSurface()
+{
+ Q_Q(QGLWidget);
+
+ WId currentId = q->winId();
+
+ if (glcx->d_func()->eglSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(glcx->d_func()->eglContext->display(),
+ glcx->d_func()->eglSurface);
+ }
+
+ glcx->d_func()->eglSurface = QEgl::createSurface(glcx->device(),
+ glcx->d_func()->eglContext->config());
+
+#if !defined(QGL_NO_PRESERVED_SWAP)
+ eglGetError(); // Clear error state first.
+ eglSurfaceAttrib(QEgl::display(), glcx->d_func()->eglSurface,
+ EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
+ if (eglGetError() != EGL_SUCCESS) {
+ qWarning("QGLContext: could not enable preserved swap");
+ }
+#endif
+
+ eglSurfaceWindowId = currentId;
+}
+
+static inline bool knownGoodFormat(QImage::Format format)
+{
+ switch (format) {
+ case QImage::Format_RGB16: // EColor64K
+ case QImage::Format_RGB32: // EColor16MU
+ case QImage::Format_ARGB32_Premultiplied: // EColor16MAP
+ return true;
+ default:
+ return false;
+ }
+}
+
+void QGLPixmapData::fromNativeType(void* pixmap, NativeType type)
+{
+ if (type == QPixmapData::FbsBitmap) {
+ CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap *>(pixmap);
+ QSize size(bitmap->SizeInPixels().iWidth, bitmap->SizeInPixels().iHeight);
+ if (size.width() == w && size.height() == h)
+ setSerialNumber(++qt_gl_pixmap_serial);
+ resize(size.width(), size.height());
+ m_source = QVolatileImage(bitmap);
+ if (pixelType() == BitmapType) {
+ m_source.ensureFormat(QImage::Format_MonoLSB);
+ } else if (!knownGoodFormat(m_source.format())) {
+ m_source.beginDataAccess();
+ QImage::Format format = idealFormat(m_source.imageRef(), Qt::AutoColor);
+ m_source.endDataAccess(true);
+ m_source.ensureFormat(format);
+ }
+ m_hasAlpha = m_source.hasAlphaChannel();
+ m_hasFillColor = false;
+ m_dirty = true;
+
+ } else if (type == QPixmapData::VolatileImage && pixmap) {
+ // Support QS60Style in more efficient skin graphics retrieval.
+ QVolatileImage *img = static_cast<QVolatileImage *>(pixmap);
+ if (img->width() == w && img->height() == h)
+ setSerialNumber(++qt_gl_pixmap_serial);
+ resize(img->width(), img->height());
+ m_source = *img;
+ m_hasAlpha = m_source.hasAlphaChannel();
+ m_hasFillColor = false;
+ m_dirty = true;
+ } else if (type == QPixmapData::NativeImageHandleProvider && pixmap) {
+ destroyTexture();
+ nativeImageHandleProvider = static_cast<QNativeImageHandleProvider *>(pixmap);
+ // Cannot defer the retrieval, we need at least the size right away.
+ createFromNativeImageHandleProvider();
+ }
+}
+
+void* QGLPixmapData::toNativeType(NativeType type)
+{
+ if (type == QPixmapData::FbsBitmap) {
+ if (m_source.isNull())
+ m_source = QVolatileImage(w, h, QImage::Format_ARGB32_Premultiplied);
+ return m_source.duplicateNativeImage();
+ }
+
+ return 0;
+}
+
+bool QGLPixmapData::initFromNativeImageHandle(void *handle, const QString &type)
+{
+ if (type == QLatin1String("RSgImage")) {
+ fromNativeType(handle, QPixmapData::SgImage);
+ return true;
+ } else if (type == QLatin1String("CFbsBitmap")) {
+ fromNativeType(handle, QPixmapData::FbsBitmap);
+ return true;
+ }
+ return false;
+}
+
+void QGLPixmapData::createFromNativeImageHandleProvider()
+{
+ void *handle = 0;
+ QString type;
+ nativeImageHandleProvider->get(&handle, &type);
+ if (handle) {
+ if (initFromNativeImageHandle(handle, type)) {
+ nativeImageHandle = handle;
+ nativeImageType = type;
+ } else {
+ qWarning("QGLPixmapData: Unknown native image type '%s'", qPrintable(type));
+ }
+ } else {
+ qWarning("QGLPixmapData: Native handle is null");
+ }
+}
+
+void QGLPixmapData::releaseNativeImageHandle()
+{
+ if (nativeImageHandleProvider && nativeImageHandle) {
+ nativeImageHandleProvider->release(nativeImageHandle, nativeImageType);
+ nativeImageHandle = 0;
+ nativeImageType = QString();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_win.cpp b/src/opengl/qgl_win.cpp
new file mode 100644
index 0000000000..70016a0825
--- /dev/null
+++ b/src/opengl/qgl_win.cpp
@@ -0,0 +1,1601 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <qgl.h>
+#include <qlist.h>
+#include <qmap.h>
+#include <qpixmap.h>
+#include <qevent.h>
+#include <private/qgl_p.h>
+#include <qcolormap.h>
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+#include <qcolor.h>
+
+#include <qt_windows.h>
+
+typedef bool (APIENTRY *PFNWGLGETPIXELFORMATATTRIBIVARB)(HDC hdc,
+ int iPixelFormat,
+ int iLayerPlane,
+ uint nAttributes,
+ const int *piAttributes,
+ int *piValues);
+typedef bool (APIENTRY *PFNWGLCHOOSEPIXELFORMATARB)(HDC hdc,
+ const int *piAttribList,
+ const float *pfAttribFList,
+ uint nMaxFormats,
+ int *piFormats,
+ UINT *nNumFormats);
+#ifndef WGL_ARB_multisample
+#define WGL_SAMPLE_BUFFERS_ARB 0x2041
+#define WGL_SAMPLES_ARB 0x2042
+#endif
+
+#ifndef WGL_ARB_pixel_format
+#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
+#define WGL_DRAW_TO_WINDOW_ARB 0x2001
+#define WGL_DRAW_TO_BITMAP_ARB 0x2002
+#define WGL_ACCELERATION_ARB 0x2003
+#define WGL_NEED_PALETTE_ARB 0x2004
+#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
+#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
+#define WGL_SWAP_METHOD_ARB 0x2007
+#define WGL_NUMBER_OVERLAYS_ARB 0x2008
+#define WGL_NUMBER_UNDERLAYS_ARB 0x2009
+#define WGL_TRANSPARENT_ARB 0x200A
+#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
+#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
+#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
+#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
+#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
+#define WGL_SHARE_DEPTH_ARB 0x200C
+#define WGL_SHARE_STENCIL_ARB 0x200D
+#define WGL_SHARE_ACCUM_ARB 0x200E
+#define WGL_SUPPORT_GDI_ARB 0x200F
+#define WGL_SUPPORT_OPENGL_ARB 0x2010
+#define WGL_DOUBLE_BUFFER_ARB 0x2011
+#define WGL_STEREO_ARB 0x2012
+#define WGL_PIXEL_TYPE_ARB 0x2013
+#define WGL_COLOR_BITS_ARB 0x2014
+#define WGL_RED_BITS_ARB 0x2015
+#define WGL_RED_SHIFT_ARB 0x2016
+#define WGL_GREEN_BITS_ARB 0x2017
+#define WGL_GREEN_SHIFT_ARB 0x2018
+#define WGL_BLUE_BITS_ARB 0x2019
+#define WGL_BLUE_SHIFT_ARB 0x201A
+#define WGL_ALPHA_BITS_ARB 0x201B
+#define WGL_ALPHA_SHIFT_ARB 0x201C
+#define WGL_ACCUM_BITS_ARB 0x201D
+#define WGL_ACCUM_RED_BITS_ARB 0x201E
+#define WGL_ACCUM_GREEN_BITS_ARB 0x201F
+#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
+#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
+#define WGL_DEPTH_BITS_ARB 0x2022
+#define WGL_STENCIL_BITS_ARB 0x2023
+#define WGL_AUX_BUFFERS_ARB 0x2024
+#define WGL_NO_ACCELERATION_ARB 0x2025
+#define WGL_GENERIC_ACCELERATION_ARB 0x2026
+#define WGL_FULL_ACCELERATION_ARB 0x2027
+#define WGL_SWAP_EXCHANGE_ARB 0x2028
+#define WGL_SWAP_COPY_ARB 0x2029
+#define WGL_SWAP_UNDEFINED_ARB 0x202A
+#define WGL_TYPE_RGBA_ARB 0x202B
+#define WGL_TYPE_COLORINDEX_ARB 0x202C
+#endif
+
+#ifndef WGL_ARB_create_context
+#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
+#define WGL_CONTEXT_FLAGS_ARB 0x2094
+#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
+#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
+#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x0001
+#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x0002
+// Error codes returned by GetLastError().
+#define ERROR_INVALID_VERSION_ARB 0x2095
+#define ERROR_INVALID_PROFILE_ARB 0x2096
+#endif
+
+#ifndef GL_VERSION_3_2
+#define GL_CONTEXT_PROFILE_MASK 0x9126
+#define GL_MAJOR_VERSION 0x821B
+#define GL_MINOR_VERSION 0x821C
+#define GL_NUM_EXTENSIONS 0x821D
+#define GL_CONTEXT_FLAGS 0x821E
+#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QGLCmapPrivate
+{
+public:
+ QGLCmapPrivate() : count(1) { }
+ void ref() { ++count; }
+ bool deref() { return !--count; }
+ uint count;
+
+ enum AllocState{ UnAllocated = 0, Allocated = 0x01, Reserved = 0x02 };
+
+ int maxSize;
+ QVector<uint> colorArray;
+ QVector<quint8> allocArray;
+ QVector<quint8> contextArray;
+ QMap<uint,int> colorMap;
+};
+
+/*****************************************************************************
+ QColorMap class - temporarily here, until it is ready for prime time
+ *****************************************************************************/
+
+/****************************************************************************
+**
+** Definition of QColorMap class
+**
+****************************************************************************/
+
+class QGLCmapPrivate;
+
+class /*Q_EXPORT*/ QGLCmap
+{
+public:
+ enum Flags { Reserved = 0x01 };
+
+ QGLCmap(int maxSize = 256);
+ QGLCmap(const QGLCmap& map);
+ ~QGLCmap();
+
+ QGLCmap& operator=(const QGLCmap& map);
+
+ // isEmpty and/or isNull ?
+ int size() const;
+ int maxSize() const;
+
+ void resize(int newSize);
+
+ int find(QRgb color) const;
+ int findNearest(QRgb color) const;
+ int allocate(QRgb color, uint flags = 0, quint8 context = 0);
+
+ void setEntry(int idx, QRgb color, uint flags = 0, quint8 context = 0);
+
+ const QRgb* colors() const;
+
+private:
+ void detach();
+ QGLCmapPrivate* d;
+};
+
+
+QGLCmap::QGLCmap(int maxSize) // add a bool prealloc?
+{
+ d = new QGLCmapPrivate;
+ d->maxSize = maxSize;
+}
+
+
+QGLCmap::QGLCmap(const QGLCmap& map)
+{
+ d = map.d;
+ d->ref();
+}
+
+
+QGLCmap::~QGLCmap()
+{
+ if (d && d->deref())
+ delete d;
+ d = 0;
+}
+
+
+QGLCmap& QGLCmap::operator=(const QGLCmap& map)
+{
+ map.d->ref();
+ if (d->deref())
+ delete d;
+ d = map.d;
+ return *this;
+}
+
+
+int QGLCmap::size() const
+{
+ return d->colorArray.size();
+}
+
+
+int QGLCmap::maxSize() const
+{
+ return d->maxSize;
+}
+
+
+void QGLCmap::detach()
+{
+ if (d->count != 1) {
+ d->deref();
+ QGLCmapPrivate* newd = new QGLCmapPrivate;
+ newd->maxSize = d->maxSize;
+ newd->colorArray = d->colorArray;
+ newd->allocArray = d->allocArray;
+ newd->contextArray = d->contextArray;
+ newd->colorArray.detach();
+ newd->allocArray.detach();
+ newd->contextArray.detach();
+ newd->colorMap = d->colorMap;
+ d = newd;
+ }
+}
+
+
+void QGLCmap::resize(int newSize)
+{
+ if (newSize < 0 || newSize > d->maxSize) {
+ qWarning("QGLCmap::resize(): size out of range");
+ return;
+ }
+ int oldSize = size();
+ detach();
+ //if shrinking; remove the lost elems from colorMap
+ d->colorArray.resize(newSize);
+ d->allocArray.resize(newSize);
+ d->contextArray.resize(newSize);
+ if (newSize > oldSize) {
+ memset(d->allocArray.data() + oldSize, 0, newSize - oldSize);
+ memset(d->contextArray.data() + oldSize, 0, newSize - oldSize);
+ }
+}
+
+
+int QGLCmap::find(QRgb color) const
+{
+ QMap<uint,int>::ConstIterator it = d->colorMap.find(color);
+ if (it != d->colorMap.end())
+ return *it;
+ return -1;
+}
+
+
+int QGLCmap::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++) {
+ if (!(d->allocArray[i] & QGLCmapPrivate::Allocated))
+ continue;
+ QRgb ci = d->colorArray[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;
+}
+
+
+
+
+// Does not always allocate; returns existing c idx if found
+
+int QGLCmap::allocate(QRgb color, uint flags, quint8 context)
+{
+ int idx = find(color);
+ if (idx >= 0)
+ return idx;
+
+ int mapSize = d->colorArray.size();
+ int newIdx = d->allocArray.indexOf(QGLCmapPrivate::UnAllocated);
+
+ if (newIdx < 0) { // Must allocate more room
+ if (mapSize < d->maxSize) {
+ newIdx = mapSize;
+ mapSize++;
+ resize(mapSize);
+ }
+ else {
+ //# add a bool param that says what to do in case no more room -
+ // fail (-1) or return nearest?
+ return -1;
+ }
+ }
+
+ d->colorArray[newIdx] = color;
+ if (flags & QGLCmap::Reserved) {
+ d->allocArray[newIdx] = QGLCmapPrivate::Reserved;
+ }
+ else {
+ d->allocArray[newIdx] = QGLCmapPrivate::Allocated;
+ d->colorMap.insert(color, newIdx);
+ }
+ d->contextArray[newIdx] = context;
+ return newIdx;
+}
+
+
+void QGLCmap::setEntry(int idx, QRgb color, uint flags, quint8 context)
+{
+ if (idx < 0 || idx >= d->maxSize) {
+ qWarning("QGLCmap::set(): Index out of range");
+ return;
+ }
+ detach();
+ int mapSize = size();
+ if (idx >= mapSize) {
+ mapSize = idx + 1;
+ resize(mapSize);
+ }
+ d->colorArray[idx] = color;
+ if (flags & QGLCmap::Reserved) {
+ d->allocArray[idx] = QGLCmapPrivate::Reserved;
+ }
+ else {
+ d->allocArray[idx] = QGLCmapPrivate::Allocated;
+ d->colorMap.insert(color, idx);
+ }
+ d->contextArray[idx] = context;
+}
+
+
+const QRgb* QGLCmap::colors() const
+{
+ return d->colorArray.data();
+}
+
+
+
+/*****************************************************************************
+ QGLFormat Win32/WGL-specific code
+ *****************************************************************************/
+
+
+void qwglError(const char* method, const char* func)
+{
+#ifndef QT_NO_DEBUG
+ char* lpMsgBuf;
+ FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (char*) &lpMsgBuf, 0, 0);
+ qWarning("%s : %s failed: %s", method, func, lpMsgBuf);
+ LocalFree(lpMsgBuf);
+#else
+ Q_UNUSED(method);
+ Q_UNUSED(func);
+#endif
+}
+
+
+
+bool QGLFormat::hasOpenGL()
+{
+ return true;
+}
+
+static bool opengl32dll = false;
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ // workaround for matrox driver:
+ // make a cheap call to opengl to force loading of DLL
+ if (!opengl32dll) {
+ GLint params;
+ glGetIntegerv(GL_DEPTH_BITS, &params);
+ opengl32dll = true;
+ }
+
+ static bool checkDone = false;
+ static bool hasOl = false;
+
+ if (!checkDone) {
+ checkDone = true;
+ HDC display_dc = GetDC(0);
+ int pfiMax = DescribePixelFormat(display_dc, 0, 0, NULL);
+ PIXELFORMATDESCRIPTOR pfd;
+ for (int pfi = 1; pfi <= pfiMax; pfi++) {
+ DescribePixelFormat(display_dc, pfi, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
+ if ((pfd.bReserved & 0x0f) && (pfd.dwFlags & PFD_SUPPORT_OPENGL)) {
+ // This format has overlays/underlays
+ LAYERPLANEDESCRIPTOR lpd;
+ wglDescribeLayerPlane(display_dc, pfi, 1,
+ sizeof(LAYERPLANEDESCRIPTOR), &lpd);
+ if (lpd.dwFlags & LPD_SUPPORT_OPENGL) {
+ hasOl = true;
+ break;
+ }
+ }
+ }
+ ReleaseDC(0, display_dc);
+ }
+ return hasOl;
+}
+
+
+/*****************************************************************************
+ QGLContext Win32/WGL-specific code
+ *****************************************************************************/
+
+static uchar qgl_rgb_palette_comp(int idx, uint nbits, uint shift)
+{
+ const uchar map_3_to_8[8] = {
+ 0, 0111>>1, 0222>>1, 0333>>1, 0444>>1, 0555>>1, 0666>>1, 0377
+ };
+ const uchar map_2_to_8[4] = {
+ 0, 0x55, 0xaa, 0xff
+ };
+ const uchar map_1_to_8[2] = {
+ 0, 255
+ };
+
+ uchar val = (uchar) (idx >> shift);
+ uchar res = 0;
+ switch (nbits) {
+ case 1:
+ val &= 0x1;
+ res = map_1_to_8[val];
+ break;
+ case 2:
+ val &= 0x3;
+ res = map_2_to_8[val];
+ break;
+ case 3:
+ val &= 0x7;
+ res = map_3_to_8[val];
+ break;
+ default:
+ res = 0;
+ }
+ return res;
+}
+
+
+static QRgb* qgl_create_rgb_palette(const PIXELFORMATDESCRIPTOR* pfd)
+{
+ if ((pfd->iPixelType != PFD_TYPE_RGBA) ||
+ !(pfd->dwFlags & PFD_NEED_PALETTE) ||
+ (pfd->cColorBits != 8))
+ return 0;
+ int numEntries = 1 << pfd->cColorBits;
+ QRgb* pal = new QRgb[numEntries];
+ for (int i = 0; i < numEntries; i++) {
+ int r = qgl_rgb_palette_comp(i, pfd->cRedBits, pfd->cRedShift);
+ int g = qgl_rgb_palette_comp(i, pfd->cGreenBits, pfd->cGreenShift);
+ int b = qgl_rgb_palette_comp(i, pfd->cBlueBits, pfd->cBlueShift);
+ pal[i] = qRgb(r, g, b);
+ }
+
+ const int syscol_indices[12] = {
+ 3, 24, 27, 64, 67, 88, 173, 181, 236, 247, 164, 91
+ };
+
+ const uint syscols[20] = {
+ 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080,
+ 0x008080, 0xc0c0c0, 0xc0dcc0, 0xa6caf0, 0xfffbf0, 0xa0a0a4,
+ 0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff,
+ 0x00ffff, 0xffffff
+ }; // colors #1 - #12 are not present in pal; gets added below
+
+ if ((pfd->cColorBits == 8) &&
+ (pfd->cRedBits == 3) && (pfd->cRedShift == 0) &&
+ (pfd->cGreenBits == 3) && (pfd->cGreenShift == 3) &&
+ (pfd->cBlueBits == 2) && (pfd->cBlueShift == 6)) {
+ for (int j = 0 ; j < 12 ; j++)
+ pal[syscol_indices[j]] = QRgb(syscols[j+1]);
+ }
+
+ return pal;
+}
+
+static QGLFormat pfdToQGLFormat(const PIXELFORMATDESCRIPTOR* pfd)
+{
+ QGLFormat fmt;
+ fmt.setDoubleBuffer(pfd->dwFlags & PFD_DOUBLEBUFFER);
+ fmt.setDepth(pfd->cDepthBits);
+ if (fmt.depth())
+ fmt.setDepthBufferSize(pfd->cDepthBits);
+ fmt.setRgba(pfd->iPixelType == PFD_TYPE_RGBA);
+ fmt.setRedBufferSize(pfd->cRedBits);
+ fmt.setGreenBufferSize(pfd->cGreenBits);
+ fmt.setBlueBufferSize(pfd->cBlueBits);
+ fmt.setAlpha(pfd->cAlphaBits);
+ if (fmt.alpha())
+ fmt.setAlphaBufferSize(pfd->cAlphaBits);
+ fmt.setAccum(pfd->cAccumBits);
+ if (fmt.accum())
+ fmt.setAccumBufferSize(pfd->cAccumRedBits);
+ fmt.setStencil(pfd->cStencilBits);
+ if (fmt.stencil())
+ fmt.setStencilBufferSize(pfd->cStencilBits);
+ fmt.setStereo(pfd->dwFlags & PFD_STEREO);
+ fmt.setDirectRendering((pfd->dwFlags & PFD_GENERIC_ACCELERATED) ||
+ !(pfd->dwFlags & PFD_GENERIC_FORMAT));
+ fmt.setOverlay((pfd->bReserved & 0x0f) != 0);
+ return fmt;
+}
+
+/*
+ NB! requires a current GL context to work
+*/
+QGLFormat pfiToQGLFormat(HDC hdc, int pfi)
+{
+ QGLFormat fmt;
+ QVarLengthArray<int> iAttributes(40);
+ QVarLengthArray<int> iValues(40);
+ int i = 0;
+ bool has_sample_buffers = QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers;
+
+ iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; // 0
+ iAttributes[i++] = WGL_DEPTH_BITS_ARB; // 1
+ iAttributes[i++] = WGL_PIXEL_TYPE_ARB; // 2
+ iAttributes[i++] = WGL_RED_BITS_ARB; // 3
+ iAttributes[i++] = WGL_GREEN_BITS_ARB; // 4
+ iAttributes[i++] = WGL_BLUE_BITS_ARB; // 5
+ iAttributes[i++] = WGL_ALPHA_BITS_ARB; // 6
+ iAttributes[i++] = WGL_ACCUM_BITS_ARB; // 7
+ iAttributes[i++] = WGL_STENCIL_BITS_ARB; // 8
+ iAttributes[i++] = WGL_STEREO_ARB; // 9
+ iAttributes[i++] = WGL_ACCELERATION_ARB; // 10
+ iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB; // 11
+ if (has_sample_buffers) {
+ iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; // 12
+ iAttributes[i++] = WGL_SAMPLES_ARB; // 13
+ }
+ PFNWGLGETPIXELFORMATATTRIBIVARB wglGetPixelFormatAttribivARB =
+ (PFNWGLGETPIXELFORMATATTRIBIVARB) wglGetProcAddress("wglGetPixelFormatAttribivARB");
+
+ if (wglGetPixelFormatAttribivARB
+ && wglGetPixelFormatAttribivARB(hdc, pfi, 0, i,
+ iAttributes.constData(),
+ iValues.data()))
+ {
+ fmt.setDoubleBuffer(iValues[0]);
+ fmt.setDepth(iValues[1]);
+ if (fmt.depth())
+ fmt.setDepthBufferSize(iValues[1]);
+ fmt.setRgba(iValues[2] == WGL_TYPE_RGBA_ARB);
+ fmt.setRedBufferSize(iValues[3]);
+ fmt.setGreenBufferSize(iValues[4]);
+ fmt.setBlueBufferSize(iValues[5]);
+ fmt.setAlpha(iValues[6]);
+ if (fmt.alpha())
+ fmt.setAlphaBufferSize(iValues[6]);
+ fmt.setAccum(iValues[7]);
+ if (fmt.accum())
+ fmt.setAccumBufferSize(iValues[7]);
+ fmt.setStencil(iValues[8]);
+ if (fmt.stencil())
+ fmt.setStencilBufferSize(iValues[8]);
+ fmt.setStereo(iValues[9]);
+ if (iValues[10] == WGL_FULL_ACCELERATION_ARB)
+ fmt.setDirectRendering(true);
+ else
+ fmt.setDirectRendering(false);
+ fmt.setOverlay(iValues[11]);
+ if (has_sample_buffers) {
+ fmt.setSampleBuffers(iValues[12]);
+ if (fmt.sampleBuffers())
+ fmt.setSamples(iValues[13]);
+ }
+ }
+#if 0
+ qDebug() << "values for pfi:" << pfi;
+ qDebug() << "doublebuffer 0:" << fmt.doubleBuffer();
+ qDebug() << "depthbuffer 1:" << fmt.depthBufferSize();
+ qDebug() << "rgba 2:" << fmt.rgba();
+ qDebug() << "red size 3:" << fmt.redBufferSize();
+ qDebug() << "green size 4:" << fmt.greenBufferSize();
+ qDebug() << "blue size 5:" << fmt.blueBufferSize();
+ qDebug() << "alpha size 6:" << fmt.alphaBufferSize();
+ qDebug() << "accum size 7:" << fmt.accumBufferSize();
+ qDebug() << "stencil size 8:" << fmt.stencilBufferSize();
+ qDebug() << "stereo 9:" << fmt.stereo();
+ qDebug() << "direct 10:" << fmt.directRendering();
+ qDebug() << "has overlays 11:" << fmt.hasOverlay();
+ qDebug() << "sample buff 12:" << fmt.sampleBuffers();
+ qDebug() << "num samples 13:" << fmt.samples();
+#endif
+ return fmt;
+}
+
+
+/*
+ QGLTemporaryContext implementation
+*/
+
+Q_GUI_EXPORT const QString qt_getRegisteredWndClass();
+
+class QGLTemporaryContextPrivate
+{
+public:
+ HDC dmy_pdc;
+ HGLRC dmy_rc;
+ HDC old_dc;
+ HGLRC old_context;
+ WId dmy_id;
+};
+
+QGLTemporaryContext::QGLTemporaryContext(bool directRendering, QWidget *parent)
+ : d(new QGLTemporaryContextPrivate)
+{
+ QString windowClassName = qt_getRegisteredWndClass();
+ if (parent && !parent->internalWinId())
+ parent = parent->nativeParentWidget();
+
+ d->dmy_id = CreateWindow((const wchar_t *)windowClassName.utf16(),
+ 0, 0, 0, 0, 1, 1,
+ parent ? parent->winId() : 0, 0, qWinAppInst(), 0);
+
+ d->dmy_pdc = GetDC(d->dmy_id);
+ PIXELFORMATDESCRIPTOR dmy_pfd;
+ memset(&dmy_pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
+ dmy_pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
+ dmy_pfd.nVersion = 1;
+ dmy_pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
+ dmy_pfd.iPixelType = PFD_TYPE_RGBA;
+ if (!directRendering)
+ dmy_pfd.dwFlags |= PFD_GENERIC_FORMAT;
+
+ int dmy_pf = ChoosePixelFormat(d->dmy_pdc, &dmy_pfd);
+ SetPixelFormat(d->dmy_pdc, dmy_pf, &dmy_pfd);
+ d->dmy_rc = wglCreateContext(d->dmy_pdc);
+ d->old_dc = wglGetCurrentDC();
+ d->old_context = wglGetCurrentContext();
+ wglMakeCurrent(d->dmy_pdc, d->dmy_rc);
+}
+
+QGLTemporaryContext::~QGLTemporaryContext()
+{
+ wglMakeCurrent(d->dmy_pdc, 0);
+ wglDeleteContext(d->dmy_rc);
+ ReleaseDC(d->dmy_id, d->dmy_pdc);
+ DestroyWindow(d->dmy_id);
+ if (d->old_dc && d->old_context)
+ wglMakeCurrent(d->old_dc, d->old_context);
+}
+
+static bool qgl_create_context(HDC hdc, QGLContextPrivate *d, QGLContextPrivate *shareContext)
+{
+ d->rc = 0;
+
+ typedef HGLRC (APIENTRYP PFNWGLCREATECONTEXTATTRIBSARB)(HDC, HGLRC, const int *);
+ PFNWGLCREATECONTEXTATTRIBSARB wglCreateContextAttribsARB =
+ (PFNWGLCREATECONTEXTATTRIBSARB) wglGetProcAddress("wglCreateContextAttribsARB");
+ if (wglCreateContextAttribsARB) {
+ int attributes[11];
+ int attribIndex = 0;
+ const int major = d->reqFormat.majorVersion();
+ const int minor = d->reqFormat.minorVersion();
+ attributes[attribIndex++] = WGL_CONTEXT_MAJOR_VERSION_ARB;
+ attributes[attribIndex++] = major;
+ attributes[attribIndex++] = WGL_CONTEXT_MINOR_VERSION_ARB;
+ attributes[attribIndex++] = minor;
+
+ if (major >= 3 && !d->reqFormat.testOption(QGL::DeprecatedFunctions)) {
+ attributes[attribIndex++] = WGL_CONTEXT_FLAGS_ARB;
+ attributes[attribIndex++] = WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
+ }
+
+ if ((major == 3 && minor >= 2) || major > 3) {
+ switch (d->reqFormat.profile()) {
+ case QGLFormat::NoProfile:
+ break;
+ case QGLFormat::CoreProfile:
+ attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB;
+ attributes[attribIndex++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
+ break;
+ case QGLFormat::CompatibilityProfile:
+ attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB;
+ attributes[attribIndex++] = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
+ break;
+ default:
+ qWarning("QGLContext::chooseContext(): Context profile not supported.");
+ return false;
+ }
+ }
+
+ if (d->reqFormat.plane() != 0) {
+ attributes[attribIndex++] = WGL_CONTEXT_LAYER_PLANE_ARB;
+ attributes[attribIndex++] = d->reqFormat.plane();
+ }
+
+ attributes[attribIndex++] = 0; // Terminate list.
+ d->rc = wglCreateContextAttribsARB(hdc, shareContext && shareContext->valid
+ ? shareContext->rc : 0, attributes);
+ if (d->rc) {
+ if (shareContext)
+ shareContext->sharing = d->sharing = true;
+ return true;
+ }
+ }
+
+ d->rc = wglCreateLayerContext(hdc, d->reqFormat.plane());
+ if (d->rc && shareContext && shareContext->valid)
+ shareContext->sharing = d->sharing = wglShareLists(shareContext->rc, d->rc);
+ return d->rc != 0;
+}
+
+void QGLContextPrivate::updateFormatVersion()
+{
+ const GLubyte *s = glGetString(GL_VERSION);
+
+ if (!(s && s[0] >= '0' && s[0] <= '9' && s[1] == '.' && s[2] >= '0' && s[2] <= '9')) {
+ if (!s)
+ qWarning("QGLContext::chooseContext(): OpenGL version string is null.");
+ else
+ qWarning("QGLContext::chooseContext(): Unexpected OpenGL version string format.");
+ glFormat.setVersion(0, 0);
+ glFormat.setProfile(QGLFormat::NoProfile);
+ glFormat.setOption(QGL::DeprecatedFunctions);
+ return;
+ }
+
+ int major = s[0] - '0';
+ int minor = s[2] - '0';
+ glFormat.setVersion(major, minor);
+
+ if (major < 3) {
+ glFormat.setProfile(QGLFormat::NoProfile);
+ glFormat.setOption(QGL::DeprecatedFunctions);
+ } else {
+ GLint value = 0;
+ if (major > 3 || minor >= 2)
+ glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);
+
+ switch (value) {
+ case WGL_CONTEXT_CORE_PROFILE_BIT_ARB:
+ glFormat.setProfile(QGLFormat::CoreProfile);
+ break;
+ case WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB:
+ glFormat.setProfile(QGLFormat::CompatibilityProfile);
+ break;
+ default:
+ glFormat.setProfile(QGLFormat::NoProfile);
+ break;
+ }
+
+ glGetIntegerv(GL_CONTEXT_FLAGS, &value);
+ if (value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)
+ glFormat.setOption(QGL::NoDeprecatedFunctions);
+ else
+ glFormat.setOption(QGL::DeprecatedFunctions);
+ }
+}
+
+bool QGLContext::chooseContext(const QGLContext* shareContext)
+{
+ QGLContextPrivate *share = shareContext ? const_cast<QGLContext *>(shareContext)->d_func() : 0;
+
+ Q_D(QGLContext);
+ // workaround for matrox driver:
+ // make a cheap call to opengl to force loading of DLL
+ if (!opengl32dll) {
+ GLint params;
+ glGetIntegerv(GL_DEPTH_BITS, &params);
+ opengl32dll = true;
+ }
+
+ bool result = true;
+ HDC myDc;
+ QWidget *widget = 0;
+
+ if (deviceIsPixmap()) {
+ if (d->glFormat.plane())
+ return false; // Pixmaps can't have overlay
+ d->win = 0;
+ HDC display_dc = GetDC(0);
+ myDc = d->hbitmap_hdc = CreateCompatibleDC(display_dc);
+ QPixmap *px = static_cast<QPixmap *>(d->paintDevice);
+
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = px->width();
+ bmi.bmiHeader.biHeight = px->height();
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ d->hbitmap = CreateDIBSection(display_dc, &bmi, DIB_RGB_COLORS, 0, 0, 0);
+ SelectObject(myDc, d->hbitmap);
+ ReleaseDC(0, display_dc);
+ } else {
+ widget = static_cast<QWidget *>(d->paintDevice);
+ d->win = widget->winId();
+ myDc = GetDC(d->win);
+ }
+
+ // NB! the QGLTemporaryContext object is needed for the
+ // wglGetProcAddress() calls to succeed and are absolutely
+ // necessary - don't remove!
+ QGLTemporaryContext tmp_ctx(d->glFormat.directRendering(), widget);
+
+ if (!myDc) {
+ qWarning("QGLContext::chooseContext(): Paint device cannot be null");
+ result = false;
+ goto end;
+ }
+
+ if (d->glFormat.plane()) {
+ d->pixelFormatId = ((QGLWidget*)d->paintDevice)->context()->d_func()->pixelFormatId;
+ if (!d->pixelFormatId) { // I.e. the glwidget is invalid
+ qWarning("QGLContext::chooseContext(): Cannot create overlay context for invalid widget");
+ result = false;
+ goto end;
+ }
+
+ if (!qgl_create_context(myDc, d, share)) {
+ qwglError("QGLContext::chooseContext()", "CreateLayerContext");
+ result = false;
+ goto end;
+ }
+
+ LAYERPLANEDESCRIPTOR lpfd;
+ wglDescribeLayerPlane(myDc, d->pixelFormatId, d->glFormat.plane(), sizeof(LAYERPLANEDESCRIPTOR), &lpfd);
+ d->glFormat.setDoubleBuffer(lpfd.dwFlags & LPD_DOUBLEBUFFER);
+ d->glFormat.setDepth(lpfd.cDepthBits);
+ d->glFormat.setRgba(lpfd.iPixelType == PFD_TYPE_RGBA);
+ if (d->glFormat.rgba()) {
+ if (d->glFormat.redBufferSize() != -1)
+ d->glFormat.setRedBufferSize(lpfd.cRedBits);
+ if (d->glFormat.greenBufferSize() != -1)
+ d->glFormat.setGreenBufferSize(lpfd.cGreenBits);
+ if (d->glFormat.blueBufferSize() != -1)
+ d->glFormat.setBlueBufferSize(lpfd.cBlueBits);
+ }
+ d->glFormat.setAlpha(lpfd.cAlphaBits);
+ d->glFormat.setAccum(lpfd.cAccumBits);
+ d->glFormat.setStencil(lpfd.cStencilBits);
+ d->glFormat.setStereo(lpfd.dwFlags & LPD_STEREO);
+ d->glFormat.setDirectRendering(false);
+ if (d->glFormat.depth())
+ d->glFormat.setDepthBufferSize(lpfd.cDepthBits);
+ if (d->glFormat.alpha())
+ d->glFormat.setAlphaBufferSize(lpfd.cAlphaBits);
+ if (d->glFormat.accum())
+ d->glFormat.setAccumBufferSize(lpfd.cAccumRedBits);
+ if (d->glFormat.stencil())
+ d->glFormat.setStencilBufferSize(lpfd.cStencilBits);
+
+ if (d->glFormat.rgba()) {
+ if (lpfd.dwFlags & LPD_TRANSPARENT)
+ d->transpColor = QColor(lpfd.crTransparent & 0xff,
+ (lpfd.crTransparent >> 8) & 0xff,
+ (lpfd.crTransparent >> 16) & 0xff);
+ else
+ d->transpColor = QColor(0, 0, 0);
+ }
+ else {
+ if (lpfd.dwFlags & LPD_TRANSPARENT)
+ d->transpColor = QColor(qRgb(1, 2, 3));//, lpfd.crTransparent);
+ else
+ d->transpColor = QColor(qRgb(1, 2, 3));//, 0);
+
+ d->cmap = new QGLCmap(1 << lpfd.cColorBits);
+ d->cmap->setEntry(lpfd.crTransparent, qRgb(1, 2, 3));//, QGLCmap::Reserved);
+ }
+ } else {
+ PIXELFORMATDESCRIPTOR pfd;
+ PIXELFORMATDESCRIPTOR realPfd;
+ d->pixelFormatId = choosePixelFormat(&pfd, myDc);
+ if (d->pixelFormatId == 0) {
+ qwglError("QGLContext::chooseContext()", "ChoosePixelFormat");
+ result = false;
+ goto end;
+ }
+
+ bool overlayRequested = d->glFormat.hasOverlay();
+ DescribePixelFormat(myDc, d->pixelFormatId, sizeof(PIXELFORMATDESCRIPTOR), &realPfd);
+
+ if (!deviceIsPixmap() && wglGetProcAddress("wglGetPixelFormatAttribivARB"))
+ d->glFormat = pfiToQGLFormat(myDc, d->pixelFormatId);
+ else
+ d->glFormat = pfdToQGLFormat(&realPfd);
+
+ d->glFormat.setOverlay(d->glFormat.hasOverlay() && overlayRequested);
+
+ if (deviceIsPixmap() && !(realPfd.dwFlags & PFD_DRAW_TO_BITMAP)) {
+ qWarning("QGLContext::chooseContext(): Failed to get pixmap rendering context.");
+ result = false;
+ goto end;
+ }
+
+ if (deviceIsPixmap() &&
+ (((QPixmap*)d->paintDevice)->depth() != realPfd.cColorBits)) {
+ qWarning("QGLContext::chooseContext(): Failed to get pixmap rendering context of suitable depth.");
+ result = false;
+ goto end;
+ }
+
+ if (!SetPixelFormat(myDc, d->pixelFormatId, &realPfd)) {
+ qwglError("QGLContext::chooseContext()", "SetPixelFormat");
+ result = false;
+ goto end;
+ }
+
+ if (!qgl_create_context(myDc, d, share)) {
+ qwglError("QGLContext::chooseContext()", "wglCreateContext");
+ result = false;
+ goto end;
+ }
+
+ if(!deviceIsPixmap()) {
+ QRgb* pal = qgl_create_rgb_palette(&realPfd);
+ if (pal) {
+ QGLColormap cmap;
+ cmap.setEntries(256, pal);
+ ((QGLWidget*)d->paintDevice)->setColormap(cmap);
+ delete[] pal;
+ }
+ }
+ }
+
+end:
+ // vblanking
+ wglMakeCurrent(myDc, d->rc);
+ if (d->rc)
+ d->updateFormatVersion();
+
+ typedef BOOL (APIENTRYP PFNWGLSWAPINTERVALEXT) (int interval);
+ typedef int (APIENTRYP PFNWGLGETSWAPINTERVALEXT) (void);
+ PFNWGLSWAPINTERVALEXT wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXT) wglGetProcAddress("wglSwapIntervalEXT");
+ PFNWGLGETSWAPINTERVALEXT wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXT) wglGetProcAddress("wglGetSwapIntervalEXT");
+ if (wglSwapIntervalEXT && wglGetSwapIntervalEXT) {
+ if (d->reqFormat.swapInterval() != -1)
+ wglSwapIntervalEXT(d->reqFormat.swapInterval());
+ d->glFormat.setSwapInterval(wglGetSwapIntervalEXT());
+ }
+
+ if (d->win)
+ ReleaseDC(d->win, myDc);
+ return result;
+}
+
+
+
+static bool qLogEq(bool a, bool b)
+{
+ return (((!a) && (!b)) || (a && b));
+}
+
+/*
+ See qgl.cpp for qdoc comment.
+ */
+int QGLContext::choosePixelFormat(void* dummyPfd, HDC pdc)
+{
+ Q_D(QGLContext);
+ // workaround for matrox driver:
+ // make a cheap call to opengl to force loading of DLL
+ if (!opengl32dll) {
+ GLint params;
+ glGetIntegerv(GL_DEPTH_BITS, &params);
+ opengl32dll = true;
+ }
+
+ PFNWGLCHOOSEPIXELFORMATARB wglChoosePixelFormatARB =
+ (PFNWGLCHOOSEPIXELFORMATARB) wglGetProcAddress("wglChoosePixelFormatARB");
+ int chosenPfi = 0;
+ if (!deviceIsPixmap() && wglChoosePixelFormatARB) {
+ bool valid;
+ int pixelFormat = 0;
+ uint numFormats = 0;
+ QVarLengthArray<int> iAttributes(40);
+ int i = 0;
+ iAttributes[i++] = WGL_ACCELERATION_ARB;
+ if (d->glFormat.directRendering())
+ iAttributes[i++] = WGL_FULL_ACCELERATION_ARB;
+ else
+ iAttributes[i++] = WGL_NO_ACCELERATION_ARB;
+ iAttributes[i++] = WGL_SUPPORT_OPENGL_ARB;
+ iAttributes[i++] = TRUE;
+ iAttributes[i++] = WGL_DRAW_TO_WINDOW_ARB;
+ iAttributes[i++] = TRUE;
+ iAttributes[i++] = WGL_COLOR_BITS_ARB;
+ iAttributes[i++] = 24;
+ iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB;
+ iAttributes[i++] = d->glFormat.doubleBuffer();
+ if (d->glFormat.stereo()) {
+ iAttributes[i++] = WGL_STEREO_ARB;
+ iAttributes[i++] = TRUE;
+ }
+ if (d->glFormat.depth()) {
+ iAttributes[i++] = WGL_DEPTH_BITS_ARB;
+ iAttributes[i++] = d->glFormat.depthBufferSize() == -1 ? 24 : d->glFormat.depthBufferSize();
+ }
+ iAttributes[i++] = WGL_PIXEL_TYPE_ARB;
+ if (d->glFormat.rgba()) {
+ iAttributes[i++] = WGL_TYPE_RGBA_ARB;
+ if (d->glFormat.redBufferSize() != -1) {
+ iAttributes[i++] = WGL_RED_BITS_ARB;
+ iAttributes[i++] = d->glFormat.redBufferSize();
+ }
+ if (d->glFormat.greenBufferSize() != -1) {
+ iAttributes[i++] = WGL_GREEN_BITS_ARB;
+ iAttributes[i++] = d->glFormat.greenBufferSize();
+ }
+ if (d->glFormat.blueBufferSize() != -1) {
+ iAttributes[i++] = WGL_BLUE_BITS_ARB;
+ iAttributes[i++] = d->glFormat.blueBufferSize();
+ }
+ } else {
+ iAttributes[i++] = WGL_TYPE_COLORINDEX_ARB;
+ }
+ if (d->glFormat.alpha()) {
+ iAttributes[i++] = WGL_ALPHA_BITS_ARB;
+ iAttributes[i++] = d->glFormat.alphaBufferSize() == -1 ? 8 : d->glFormat.alphaBufferSize();
+ }
+ if (d->glFormat.accum()) {
+ iAttributes[i++] = WGL_ACCUM_BITS_ARB;
+ iAttributes[i++] = d->glFormat.accumBufferSize() == -1 ? 16 : d->glFormat.accumBufferSize();
+ }
+ if (d->glFormat.stencil()) {
+ iAttributes[i++] = WGL_STENCIL_BITS_ARB;
+ iAttributes[i++] = d->glFormat.stencilBufferSize() == -1 ? 8 : d->glFormat.stencilBufferSize();
+ }
+ if (d->glFormat.hasOverlay()) {
+ iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB;
+ iAttributes[i++] = 1;
+ }
+ int si = 0;
+ bool trySampleBuffers = QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers;
+ if (trySampleBuffers && d->glFormat.sampleBuffers()) {
+ iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB;
+ iAttributes[i++] = TRUE;
+ iAttributes[i++] = WGL_SAMPLES_ARB;
+ si = i;
+ iAttributes[i++] = d->glFormat.samples() == -1 ? 4 : d->glFormat.samples();
+ }
+ iAttributes[i] = 0;
+
+ do {
+ valid = wglChoosePixelFormatARB(pdc, iAttributes.constData(), 0, 1,
+ &pixelFormat, &numFormats);
+ if (trySampleBuffers && (!valid || numFormats < 1) && d->glFormat.sampleBuffers())
+ iAttributes[si] /= 2; // try different no. samples - we aim for the best one
+ else
+ break;
+ } while ((!valid || numFormats < 1) && iAttributes[si] > 1);
+ chosenPfi = pixelFormat;
+ }
+
+ if (!chosenPfi) { // fallback if wglChoosePixelFormatARB() failed
+ int pmDepth = deviceIsPixmap() ? ((QPixmap*)d->paintDevice)->depth() : 0;
+ PIXELFORMATDESCRIPTOR* p = (PIXELFORMATDESCRIPTOR*)dummyPfd;
+ memset(p, 0, sizeof(PIXELFORMATDESCRIPTOR));
+ p->nSize = sizeof(PIXELFORMATDESCRIPTOR);
+ p->nVersion = 1;
+ p->dwFlags = PFD_SUPPORT_OPENGL;
+ if (deviceIsPixmap())
+ p->dwFlags |= PFD_DRAW_TO_BITMAP;
+ else
+ p->dwFlags |= PFD_DRAW_TO_WINDOW;
+ if (!d->glFormat.directRendering())
+ p->dwFlags |= PFD_GENERIC_FORMAT;
+ if (d->glFormat.doubleBuffer() && !deviceIsPixmap())
+ p->dwFlags |= PFD_DOUBLEBUFFER;
+ if (d->glFormat.stereo())
+ p->dwFlags |= PFD_STEREO;
+ if (d->glFormat.depth())
+ p->cDepthBits = d->glFormat.depthBufferSize() == -1 ? 32 : d->glFormat.depthBufferSize();
+ else
+ p->dwFlags |= PFD_DEPTH_DONTCARE;
+ if (d->glFormat.rgba()) {
+ p->iPixelType = PFD_TYPE_RGBA;
+ if (d->glFormat.redBufferSize() != -1)
+ p->cRedBits = d->glFormat.redBufferSize();
+ if (d->glFormat.greenBufferSize() != -1)
+ p->cGreenBits = d->glFormat.greenBufferSize();
+ if (d->glFormat.blueBufferSize() != -1)
+ p->cBlueBits = d->glFormat.blueBufferSize();
+ if (deviceIsPixmap())
+ p->cColorBits = pmDepth;
+ else
+ p->cColorBits = 32;
+ } else {
+ p->iPixelType = PFD_TYPE_COLORINDEX;
+ p->cColorBits = 8;
+ }
+ if (d->glFormat.alpha())
+ p->cAlphaBits = d->glFormat.alphaBufferSize() == -1 ? 8 : d->glFormat.alphaBufferSize();
+ if (d->glFormat.accum()) {
+ p->cAccumRedBits = p->cAccumGreenBits = p->cAccumBlueBits = p->cAccumAlphaBits =
+ d->glFormat.accumBufferSize() == -1 ? 16 : d->glFormat.accumBufferSize();
+ }
+ if (d->glFormat.stencil())
+ p->cStencilBits = d->glFormat.stencilBufferSize() == -1 ? 8 : d->glFormat.stencilBufferSize();
+ p->iLayerType = PFD_MAIN_PLANE;
+ chosenPfi = ChoosePixelFormat(pdc, p);
+
+ if (!chosenPfi)
+ qErrnoWarning("QGLContext: ChoosePixelFormat failed");
+
+ // Since the GDI function ChoosePixelFormat() does not handle
+ // overlay and direct-rendering requests, we must roll our own here
+
+ bool doSearch = chosenPfi <= 0;
+ PIXELFORMATDESCRIPTOR pfd;
+ QGLFormat fmt;
+ if (!doSearch) {
+ DescribePixelFormat(pdc, chosenPfi, sizeof(PIXELFORMATDESCRIPTOR),
+ &pfd);
+ fmt = pfdToQGLFormat(&pfd);
+ if (d->glFormat.hasOverlay() && !fmt.hasOverlay())
+ doSearch = true;
+ else if (!qLogEq(d->glFormat.directRendering(), fmt.directRendering()))
+ doSearch = true;
+ else if (deviceIsPixmap() && (!(pfd.dwFlags & PFD_DRAW_TO_BITMAP) ||
+ pfd.cColorBits != pmDepth))
+ doSearch = true;
+ else if (!deviceIsPixmap() && !(pfd.dwFlags & PFD_DRAW_TO_WINDOW))
+ doSearch = true;
+ else if (!qLogEq(d->glFormat.rgba(), fmt.rgba()))
+ doSearch = true;
+ }
+
+ if (doSearch) {
+ int pfiMax = DescribePixelFormat(pdc, 0, 0, NULL);
+ int bestScore = -1;
+ int bestPfi = -1;
+ for (int pfi = 1; pfi <= pfiMax; pfi++) {
+ DescribePixelFormat(pdc, pfi, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
+ if (!(pfd.dwFlags & PFD_SUPPORT_OPENGL))
+ continue;
+ if (deviceIsPixmap() && (!(pfd.dwFlags & PFD_DRAW_TO_BITMAP) ||
+ pfd.cColorBits != pmDepth))
+ continue;
+ if (!deviceIsPixmap() && !(pfd.dwFlags & PFD_DRAW_TO_WINDOW))
+ continue;
+
+ fmt = pfdToQGLFormat(&pfd);
+ if (d->glFormat.hasOverlay() && !fmt.hasOverlay())
+ continue;
+
+ int score = pfd.cColorBits;
+ if (qLogEq(d->glFormat.depth(), fmt.depth()))
+ score += pfd.cDepthBits;
+ if (qLogEq(d->glFormat.alpha(), fmt.alpha()))
+ score += pfd.cAlphaBits;
+ if (qLogEq(d->glFormat.accum(), fmt.accum()))
+ score += pfd.cAccumBits;
+ if (qLogEq(d->glFormat.stencil(), fmt.stencil()))
+ score += pfd.cStencilBits;
+ if (qLogEq(d->glFormat.doubleBuffer(), fmt.doubleBuffer()))
+ score += 1000;
+ if (qLogEq(d->glFormat.stereo(), fmt.stereo()))
+ score += 2000;
+ if (qLogEq(d->glFormat.directRendering(), fmt.directRendering()))
+ score += 4000;
+ if (qLogEq(d->glFormat.rgba(), fmt.rgba()))
+ score += 8000;
+ if (score > bestScore) {
+ bestScore = score;
+ bestPfi = pfi;
+ }
+ }
+
+ if (bestPfi > 0)
+ chosenPfi = bestPfi;
+ }
+ }
+ return chosenPfi;
+}
+
+
+
+void QGLContext::reset()
+{
+ Q_D(QGLContext);
+ // workaround for matrox driver:
+ // make a cheap call to opengl to force loading of DLL
+ if (!opengl32dll) {
+ GLint params;
+ glGetIntegerv(GL_DEPTH_BITS, &params);
+ opengl32dll = true;
+ }
+
+ if (!d->valid)
+ return;
+ d->cleanup();
+ doneCurrent();
+ if (d->rc)
+ wglDeleteContext(d->rc);
+ d->rc = 0;
+ if (d->win && d->dc)
+ ReleaseDC(d->win, d->dc);
+ if (deviceIsPixmap()) {
+ DeleteDC(d->hbitmap_hdc);
+ DeleteObject(d->hbitmap);
+ d->hbitmap_hdc = 0;
+ d->hbitmap = 0;
+ }
+ d->dc = 0;
+ d->win = 0;
+ d->threadId = 0;
+ d->pixelFormatId = 0;
+ d->sharing = false;
+ d->valid = false;
+ d->transpColor = QColor();
+ delete d->cmap;
+ d->cmap = 0;
+ d->initDone = false;
+ QGLContextGroup::removeShare(this);
+}
+
+//
+// NOTE: In a multi-threaded environment, each thread has a current
+// context. If we want to make this code thread-safe, we probably
+// have to use TLS (thread local storage) for keeping current contexts.
+//
+
+void QGLContext::makeCurrent()
+{
+ Q_D(QGLContext);
+ if (d->rc == wglGetCurrentContext() || !d->valid) // already current
+ return;
+
+ if (d->win && (!d->dc || d->threadId != QThread::currentThreadId())) {
+ d->dc = GetDC(d->win);
+ d->threadId = QThread::currentThreadId();
+ if (!d->dc) {
+ qwglError("QGLContext::makeCurrent()", "GetDC()");
+ return;
+ }
+ } else if (deviceIsPixmap()) {
+ d->dc = d->hbitmap_hdc;
+ }
+
+ HPALETTE hpal = QColormap::hPal();
+ if (hpal) {
+ SelectPalette(d->dc, hpal, FALSE);
+ RealizePalette(d->dc);
+ }
+ if (d->glFormat.plane()) {
+ wglRealizeLayerPalette(d->dc, d->glFormat.plane(), TRUE);
+ }
+
+ if (wglMakeCurrent(d->dc, d->rc)) {
+ QGLContextPrivate::setCurrentContext(this);
+ } else {
+ qwglError("QGLContext::makeCurrent()", "wglMakeCurrent");
+ }
+}
+
+
+void QGLContext::doneCurrent()
+{
+ Q_D(QGLContext);
+ wglMakeCurrent(0, 0);
+ QGLContextPrivate::setCurrentContext(0);
+ if (deviceIsPixmap() && d->hbitmap) {
+ QPixmap *pm = static_cast<QPixmap *>(d->paintDevice);
+ *pm = QPixmap::fromWinHBITMAP(d->hbitmap);
+ }
+ if (d->win && d->dc) {
+ ReleaseDC(d->win, d->dc);
+ d->dc = 0;
+ d->threadId = 0;
+ }
+}
+
+void QGLContext::swapBuffers() const
+{
+ Q_D(const QGLContext);
+ if (d->dc && d->glFormat.doubleBuffer() && !deviceIsPixmap()) {
+ if (d->glFormat.plane())
+ wglSwapLayerBuffers(d->dc, WGL_SWAP_OVERLAY1);
+ else {
+ if (d->glFormat.hasOverlay())
+ wglSwapLayerBuffers(d->dc, WGL_SWAP_MAIN_PLANE);
+ else
+ SwapBuffers(d->dc);
+ }
+ }
+}
+
+
+QColor QGLContext::overlayTransparentColor() const
+{
+ return d_func()->transpColor;
+}
+
+
+uint QGLContext::colorIndex(const QColor& c) const
+{
+ Q_D(const QGLContext);
+ if (!isValid())
+ return 0;
+ if (d->cmap) {
+ int idx = d->cmap->find(c.rgb());
+ if (idx >= 0)
+ return idx;
+ if (d->dc && d->glFormat.plane()) {
+ idx = d->cmap->allocate(c.rgb());
+ if (idx >= 0) {
+ COLORREF r = RGB(qRed(c.rgb()),qGreen(c.rgb()),qBlue(c.rgb()));
+ wglSetLayerPaletteEntries(d->dc, d->glFormat.plane(), idx, 1, &r);
+ wglRealizeLayerPalette(d->dc, d->glFormat.plane(), TRUE);
+ return idx;
+ }
+ }
+ return d->cmap->findNearest(c.rgb());
+ }
+ QColormap cmap = QColormap::instance();
+ return cmap.pixel(c) & 0x00ffffff; // Assumes standard palette
+}
+
+void QGLContext::generateFontDisplayLists(const QFont & fnt, int listBase)
+{
+ if (!isValid())
+ return;
+
+ HDC display_dc = GetDC(0);
+ HDC tmp_dc = CreateCompatibleDC(display_dc);
+ HGDIOBJ old_font = SelectObject(tmp_dc, fnt.handle());
+
+ ReleaseDC(0, display_dc);
+
+ if (!wglUseFontBitmaps(tmp_dc, 0, 256, listBase))
+ qWarning("QGLContext::generateFontDisplayLists: Could not generate display lists for font '%s'", fnt.family().toLatin1().data());
+
+ SelectObject(tmp_dc, old_font);
+ DeleteDC(tmp_dc);
+}
+
+void *QGLContext::getProcAddress(const QString &proc) const
+{
+ return (void *)wglGetProcAddress(proc.toLatin1());
+}
+
+/*****************************************************************************
+ QGLWidget Win32/WGL-specific code
+ *****************************************************************************/
+
+void QGLWidgetPrivate::init(QGLContext *ctx, const QGLWidget* shareWidget)
+{
+ Q_Q(QGLWidget);
+ olcx = 0;
+ initContext(ctx, shareWidget);
+
+ if (q->isValid() && q->context()->format().hasOverlay()) {
+ olcx = new QGLContext(QGLFormat::defaultOverlayFormat(), q);
+ if (!olcx->create(shareWidget ? shareWidget->overlayContext() : 0)) {
+ delete olcx;
+ olcx = 0;
+ glcx->d_func()->glFormat.setOverlay(false);
+ }
+ } else {
+ olcx = 0;
+ }
+}
+
+/*\internal
+ Store color values in the given colormap.
+*/
+static void qStoreColors(HPALETTE cmap, const QGLColormap & cols)
+{
+ QRgb color;
+ PALETTEENTRY pe;
+
+ for (int i = 0; i < cols.size(); i++) {
+ color = cols.entryRgb(i);
+ pe.peRed = qRed(color);
+ pe.peGreen = qGreen(color);
+ pe.peBlue = qBlue(color);
+ pe.peFlags = 0;
+
+ SetPaletteEntries(cmap, i, 1, &pe);
+ }
+}
+
+void QGLWidgetPrivate::updateColormap()
+{
+ Q_Q(QGLWidget);
+ if (!cmap.handle())
+ return;
+ HDC hdc = GetDC(q->winId());
+ SelectPalette(hdc, (HPALETTE) cmap.handle(), TRUE);
+ qStoreColors((HPALETTE) cmap.handle(), cmap);
+ RealizePalette(hdc);
+ ReleaseDC(q->winId(), hdc);
+}
+
+void QGLWidget::setMouseTracking(bool enable)
+{
+ QWidget::setMouseTracking(enable);
+}
+
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ resizeGL(width(), height());
+ if (d->olcx) {
+ makeOverlayCurrent();
+ resizeOverlayGL(width(), height());
+ }
+}
+
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return d_func()->olcx;
+}
+
+
+void QGLWidget::makeOverlayCurrent()
+{
+ Q_D(QGLWidget);
+ if (d->olcx) {
+ d->olcx->makeCurrent();
+ if (!d->olcx->initialized()) {
+ initializeOverlayGL();
+ d->olcx->setInitialized(true);
+ }
+ }
+}
+
+
+void QGLWidget::updateOverlayGL()
+{
+ Q_D(QGLWidget);
+ if (d->olcx) {
+ makeOverlayCurrent();
+ paintOverlayGL();
+ if (d->olcx->format().doubleBuffer()) {
+ if (d->autoSwap)
+ d->olcx->swapBuffers();
+ }
+ else {
+ glFlush();
+ }
+ }
+}
+
+
+void QGLWidget::setContext(QGLContext *context,
+ const QGLContext* shareContext,
+ bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+ if (!context->deviceIsPixmap() && context->device() != this) {
+ qWarning("QGLWidget::setContext: Context must refer to this widget");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+
+ bool doShow = false;
+ if (oldcx && oldcx->d_func()->win == winId() && !d->glcx->deviceIsPixmap()) {
+ // We already have a context and must therefore create a new
+ // window since Windows does not permit setting a new OpenGL
+ // context for a window that already has one set.
+ doShow = isVisible();
+ QWidget *pW = static_cast<QWidget *>(parent());
+ QPoint pos = geometry().topLeft();
+ setParent(pW, windowFlags());
+ move(pos);
+ }
+
+ if (!d->glcx->isValid()) {
+ bool wasSharing = shareContext || (oldcx && oldcx->isSharing());
+ d->glcx->create(shareContext ? shareContext : oldcx);
+ // the above is a trick to keep disp lists etc when a
+ // QGLWidget has been reparented, so remove the sharing
+ // flag if we don't actually have a sharing context.
+ if (!wasSharing)
+ d->glcx->d_ptr->sharing = false;
+ }
+
+ if (deleteOldContext)
+ delete oldcx;
+
+ if (doShow)
+ show();
+}
+
+
+bool QGLWidgetPrivate::renderCxPm(QPixmap*)
+{
+ return false;
+}
+
+void QGLWidgetPrivate::cleanupColormaps()
+{
+ Q_Q(QGLWidget);
+ if (cmap.handle()) {
+ HDC hdc = GetDC(q->winId());
+ SelectPalette(hdc, (HPALETTE) GetStockObject(DEFAULT_PALETTE), FALSE);
+ DeleteObject((HPALETTE) cmap.handle());
+ ReleaseDC(q->winId(), hdc);
+ cmap.setHandle(0);
+ }
+ return;
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ return d_func()->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap & c)
+{
+ Q_D(QGLWidget);
+ d->cmap = c;
+
+ if (d->cmap.handle()) { // already have an allocated cmap
+ d->updateColormap();
+ } else {
+ LOGPALETTE *lpal = (LOGPALETTE *) malloc(sizeof(LOGPALETTE)
+ +c.size()*sizeof(PALETTEENTRY));
+ lpal->palVersion = 0x300;
+ lpal->palNumEntries = c.size();
+ d->cmap.setHandle(CreatePalette(lpal));
+ free(lpal);
+ d->updateColormap();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_wince.cpp b/src/opengl/qgl_wince.cpp
new file mode 100644
index 0000000000..e1989f9f2f
--- /dev/null
+++ b/src/opengl/qgl_wince.cpp
@@ -0,0 +1,636 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <qgl.h>
+#include <qlist.h>
+#include <qmap.h>
+#include <qpixmap.h>
+#include <qevent.h>
+#include <private/qgl_p.h>
+#include <qcolormap.h>
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+#include <qapplication.h>
+#include <qdesktopwidget>
+
+#include <windows.h>
+
+#include <private/qeglproperties_p.h>
+#include <private/qeglcontext_p.h>
+#include <private/qgl_egl_p.h>
+
+
+QT_BEGIN_NAMESPACE
+
+
+
+class QGLCmapPrivate
+{
+public:
+ QGLCmapPrivate() : count(1) { }
+ void ref() { ++count; }
+ bool deref() { return !--count; }
+ uint count;
+
+ enum AllocState{ UnAllocated = 0, Allocated = 0x01, Reserved = 0x02 };
+
+ int maxSize;
+ QVector<uint> colorArray;
+ QVector<quint8> allocArray;
+ QVector<quint8> contextArray;
+ QMap<uint,int> colorMap;
+};
+
+/*****************************************************************************
+ QColorMap class - temporarily here, until it is ready for prime time
+ *****************************************************************************/
+
+/****************************************************************************
+**
+** Definition of QColorMap class
+**
+****************************************************************************/
+
+#ifndef QGLCMAP_H
+#define QGLCMAP_H
+
+#include <qcolor.h>
+
+/*
+ QGLTemporaryContext implementation
+*/
+
+class QGLTemporaryContextPrivate
+{
+public:
+ QGLWidget *widget;
+};
+
+QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
+ : d(new QGLTemporaryContextPrivate)
+{
+ d->widget = new QGLWidget;
+ d->widget->makeCurrent();
+}
+
+QGLTemporaryContext::~QGLTemporaryContext()
+{
+ delete d->widget;
+}
+
+/*****************************************************************************
+ QGLFormat Win32/WGL-specific code
+ *****************************************************************************/
+
+static bool opengl32dll = false;
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ return false; // ###
+}
+
+
+bool QGLContext::chooseContext(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+
+ // Validate the device.
+ if (!device())
+ return false;
+ int devType = device()->devType();
+ if (devType != QInternal::Pixmap && devType != QInternal::Image && devType != QInternal::Widget) {
+ qWarning("QGLContext::chooseContext(): Cannot create QGLContext's for paint device type %d", devType);
+ return false;
+ }
+
+ // Get the display and initialize it.
+ d->eglContext = new QEglContext();
+ d->ownsEglContext = true;
+ d->eglContext->setApi(QEgl::OpenGL);
+
+ // Construct the configuration we need for this surface.
+ QEglProperties configProps;
+ qt_eglproperties_set_glformat(configProps, d->glFormat);
+ configProps.setDeviceType(devType);
+ configProps.setPaintDeviceFormat(device());
+ configProps.setRenderableType(QEgl::OpenGL);
+
+ // Search for a matching configuration, reducing the complexity
+ // each time until we get something that matches.
+ if (!d->eglContext->chooseConfig(configProps)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ // Inform the higher layers about the actual format properties.
+ qt_glformat_from_eglconfig(d->glFormat, d->eglContext->config());
+
+ // Create a new context for the configuration.
+ if (!d->eglContext->createContext
+ (shareContext ? shareContext->d_func()->eglContext : 0)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+ d->sharing = d->eglContext->isSharing();
+ if (d->sharing && shareContext)
+ const_cast<QGLContext *>(shareContext)->d_func()->sharing = true;
+
+#if defined(EGL_VERSION_1_1)
+ if (d->glFormat.swapInterval() != -1 && devType == QInternal::Widget)
+ eglSwapInterval(d->eglContext->display(), d->glFormat.swapInterval());
+#endif
+
+ // Create the EGL surface to draw into.
+ d->eglSurface = d->eglContext->createSurface(device());
+ if (d->eglSurface == EGL_NO_SURFACE) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ return true;
+
+}
+
+
+
+static bool qLogEq(bool a, bool b)
+{
+ return (((!a) && (!b)) || (a && b));
+}
+
+int QGLContext::choosePixelFormat(void* , HDC )
+{
+
+ return 0;
+}
+
+class QGLCmapPrivate;
+
+class /*Q_EXPORT*/ QGLCmap
+{
+public:
+ enum Flags { Reserved = 0x01 };
+
+ QGLCmap(int maxSize = 256);
+ QGLCmap(const QGLCmap& map);
+ ~QGLCmap();
+
+ QGLCmap& operator=(const QGLCmap& map);
+
+ // isEmpty and/or isNull ?
+ int size() const;
+ int maxSize() const;
+
+ void resize(int newSize);
+
+ int find(QRgb color) const;
+ int findNearest(QRgb color) const;
+ int allocate(QRgb color, uint flags = 0, quint8 context = 0);
+
+ void setEntry(int idx, QRgb color, uint flags = 0, quint8 context = 0);
+
+ const QRgb* colors() const;
+
+private:
+ void detach();
+ QGLCmapPrivate* d;
+};
+
+#endif
+
+
+QGLCmap::QGLCmap(int maxSize) // add a bool prealloc?
+{
+ d = new QGLCmapPrivate;
+ d->maxSize = maxSize;
+}
+
+QGLCmap::QGLCmap(const QGLCmap& map)
+{
+ d = map.d;
+ d->ref();
+}
+
+QGLCmap::~QGLCmap()
+{
+ if (d && d->deref())
+ delete d;
+ d = 0;
+}
+
+QGLCmap& QGLCmap::operator=(const QGLCmap& map)
+{
+ map.d->ref();
+ if (d->deref())
+ delete d;
+ d = map.d;
+ return *this;
+}
+
+int QGLCmap::size() const
+{
+ return d->colorArray.size();
+}
+
+int QGLCmap::maxSize() const
+{
+ return d->maxSize;
+}
+
+void QGLCmap::detach()
+{
+ if (d->count != 1) {
+ d->deref();
+ QGLCmapPrivate* newd = new QGLCmapPrivate;
+ newd->maxSize = d->maxSize;
+ newd->colorArray = d->colorArray;
+ newd->allocArray = d->allocArray;
+ newd->contextArray = d->contextArray;
+ newd->colorArray.detach();
+ newd->allocArray.detach();
+ newd->contextArray.detach();
+ newd->colorMap = d->colorMap;
+ d = newd;
+ }
+}
+
+
+void QGLCmap::resize(int newSize)
+{
+ if (newSize < 0 || newSize > d->maxSize) {
+ qWarning("QGLCmap::resize(): size out of range");
+ return;
+ }
+ int oldSize = size();
+ detach();
+ //if shrinking; remove the lost elems from colorMap
+ d->colorArray.resize(newSize);
+ d->allocArray.resize(newSize);
+ d->contextArray.resize(newSize);
+ if (newSize > oldSize) {
+ memset(d->allocArray.data() + oldSize, 0, newSize - oldSize);
+ memset(d->contextArray.data() + oldSize, 0, newSize - oldSize);
+ }
+}
+
+
+int QGLCmap::find(QRgb color) const
+{
+ QMap<uint,int>::ConstIterator it = d->colorMap.find(color);
+ if (it != d->colorMap.end())
+ return *it;
+ return -1;
+}
+
+
+int QGLCmap::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++) {
+ if (!(d->allocArray[i] & QGLCmapPrivate::Allocated))
+ continue;
+ QRgb ci = d->colorArray[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;
+}
+
+
+// Does not always allocate; returns existing c idx if found
+
+int QGLCmap::allocate(QRgb color, uint flags, quint8 context)
+{
+ int idx = find(color);
+ if (idx >= 0)
+ return idx;
+
+ int mapSize = d->colorArray.size();
+ int newIdx = d->allocArray.indexOf(QGLCmapPrivate::UnAllocated);
+
+ if (newIdx < 0) { // Must allocate more room
+ if (mapSize < d->maxSize) {
+ newIdx = mapSize;
+ mapSize++;
+ resize(mapSize);
+ }
+ else {
+ //# add a bool param that says what to do in case no more room -
+ // fail (-1) or return nearest?
+ return -1;
+ }
+ }
+
+ d->colorArray[newIdx] = color;
+ if (flags & QGLCmap::Reserved) {
+ d->allocArray[newIdx] = QGLCmapPrivate::Reserved;
+ }
+ else {
+ d->allocArray[newIdx] = QGLCmapPrivate::Allocated;
+ d->colorMap.insert(color, newIdx);
+ }
+ d->contextArray[newIdx] = context;
+ return newIdx;
+}
+
+
+void QGLCmap::setEntry(int idx, QRgb color, uint flags, quint8 context)
+{
+ if (idx < 0 || idx >= d->maxSize) {
+ qWarning("QGLCmap::set(): Index out of range");
+ return;
+ }
+ detach();
+ int mapSize = size();
+ if (idx >= mapSize) {
+ mapSize = idx + 1;
+ resize(mapSize);
+ }
+ d->colorArray[idx] = color;
+ if (flags & QGLCmap::Reserved) {
+ d->allocArray[idx] = QGLCmapPrivate::Reserved;
+ }
+ else {
+ d->allocArray[idx] = QGLCmapPrivate::Allocated;
+ d->colorMap.insert(color, idx);
+ }
+ d->contextArray[idx] = context;
+}
+
+
+const QRgb* QGLCmap::colors() const
+{
+ return d->colorArray.data();
+}
+
+
+/*****************************************************************************
+ QGLWidget Win32/WGL-specific code
+ *****************************************************************************/
+
+void QGLWidgetPrivate::init(QGLContext *ctx, const QGLWidget* shareWidget)
+{
+ Q_Q(QGLWidget);
+ olcx = 0;
+ initContext(ctx, shareWidget);
+
+ if (q->isValid() && q->context()->format().hasOverlay()) {
+ olcx = new QGLContext(QGLFormat::defaultOverlayFormat(), q);
+ if (!olcx->create(shareWidget ? shareWidget->overlayContext() : 0)) {
+ delete olcx;
+ olcx = 0;
+ glcx->d_func()->glFormat.setOverlay(false);
+ }
+ } else {
+ olcx = 0;
+ }
+}
+
+/*\internal
+ Store color values in the given colormap.
+*/
+static void qStoreColors(HPALETTE cmap, const QGLColormap & cols)
+{
+ QRgb color;
+ PALETTEENTRY pe;
+
+ for (int i = 0; i < cols.size(); i++) {
+ color = cols.entryRgb(i);
+ pe.peRed = qRed(color);
+ pe.peGreen = qGreen(color);
+ pe.peBlue = qBlue(color);
+ pe.peFlags = 0;
+
+ SetPaletteEntries(cmap, i, 1, &pe);
+ }
+}
+
+void QGLWidgetPrivate::updateColormap()
+{
+ Q_Q(QGLWidget);
+ if (!cmap.handle())
+ return;
+ HDC hdc = GetDC(q->winId());
+ SelectPalette(hdc, (HPALETTE) cmap.handle(), TRUE);
+ qStoreColors((HPALETTE) cmap.handle(), cmap);
+ RealizePalette(hdc);
+ ReleaseDC(q->winId(), hdc);
+}
+
+bool QGLWidget::event(QEvent *e)
+{
+ Q_D(QGLWidget);
+ if (e->type() == QEvent::ParentChange) {
+ setContext(new QGLContext(d->glcx->requestedFormat(), this));
+ // the overlay needs to be recreated as well
+ delete d->olcx;
+ if (isValid() && context()->format().hasOverlay()) {
+ d->olcx = new QGLContext(QGLFormat::defaultOverlayFormat(), this);
+ if (!d->olcx->create(isSharing() ? d->glcx : 0)) {
+ delete d->olcx;
+ d->olcx = 0;
+ d->glcx->d_func()->glFormat.setOverlay(false);
+ }
+ } else {
+ d->olcx = 0;
+ }
+ } else if (e->type() == QEvent::Show && !format().rgba()) {
+ d->updateColormap();
+ }
+
+ return QWidget::event(e);
+}
+
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ resizeGL(width(), height());
+ if (d->olcx) {
+ makeOverlayCurrent();
+ resizeOverlayGL(width(), height());
+ }
+}
+
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return d_func()->olcx;
+}
+
+
+void QGLWidget::makeOverlayCurrent()
+{
+ Q_D(QGLWidget);
+ if (d->olcx) {
+ d->olcx->makeCurrent();
+ if (!d->olcx->initialized()) {
+ initializeOverlayGL();
+ d->olcx->setInitialized(true);
+ }
+ }
+}
+
+
+void QGLWidget::updateOverlayGL()
+{
+ Q_D(QGLWidget);
+ if (d->olcx) {
+ makeOverlayCurrent();
+ paintOverlayGL();
+ if (d->olcx->format().doubleBuffer()) {
+ if (d->autoSwap)
+ d->olcx->swapBuffers();
+ }
+ else {
+ glFlush();
+ }
+ }
+}
+
+void QGLWidget::setContext(QGLContext *context,
+ const QGLContext* shareContext,
+ bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+ if (!context->deviceIsPixmap() && context->device() != this) {
+ qWarning("QGLWidget::setContext: Context must refer to this widget");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+
+ bool doShow = false;
+ if (oldcx && oldcx->d_func()->win == winId() && !d->glcx->deviceIsPixmap()) {
+ // We already have a context and must therefore create a new
+ // window since Windows does not permit setting a new OpenGL
+ // context for a window that already has one set.
+ doShow = isVisible();
+ QWidget *pW = static_cast<QWidget *>(parent());
+ QPoint pos = geometry().topLeft();
+ setParent(pW, windowFlags());
+ move(pos);
+ }
+
+ if (!d->glcx->isValid()) {
+ d->glcx->create(shareContext ? shareContext : oldcx);
+ // the above is a trick to keep disp lists etc when a
+ // QGLWidget has been reparented, so remove the sharing
+ // flag if we don't actually have a sharing context.
+ if (!shareContext)
+ d->glcx->d_ptr->sharing = false;
+ }
+
+ if (deleteOldContext)
+ delete oldcx;
+
+ if (doShow)
+ show();
+}
+
+
+void QGLWidgetPrivate::cleanupColormaps()
+{
+ Q_Q(QGLWidget);
+ if (cmap.handle()) {
+ HDC hdc = GetDC(q->winId());
+ SelectPalette(hdc, (HPALETTE) GetStockObject(DEFAULT_PALETTE), FALSE);
+ DeleteObject((HPALETTE) cmap.handle());
+ ReleaseDC(q->winId(), hdc);
+ cmap.setHandle(0);
+ }
+ return;
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ return d_func()->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap & c)
+{
+ Q_D(QGLWidget);
+ d->cmap = c;
+
+ if (d->cmap.handle()) { // already have an allocated cmap
+ d->updateColormap();
+ } else {
+ LOGPALETTE *lpal = (LOGPALETTE *) malloc(sizeof(LOGPALETTE)
+ +c.size()*sizeof(PALETTEENTRY));
+ lpal->palVersion = 0x300;
+ lpal->palNumEntries = c.size();
+ d->cmap.setHandle(CreatePalette(lpal));
+ free(lpal);
+ d->updateColormap();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_x11.cpp b/src/opengl/qgl_x11.cpp
new file mode 100644
index 0000000000..9b520034f8
--- /dev/null
+++ b/src/opengl/qgl_x11.cpp
@@ -0,0 +1,1908 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgl.h"
+#include "qgl_p.h"
+
+#include "qmap.h"
+#include "qapplication.h"
+#include "qcolormap.h"
+#include "qdesktopwidget.h"
+#include "qpixmap.h"
+#include "qhash.h"
+#include "qlibrary.h"
+#include "qdebug.h"
+#include <private/qfontengine_ft_p.h>
+#include <private/qt_x11_p.h>
+#include <private/qpixmap_x11_p.h>
+#include <private/qimagepixmapcleanuphooks_p.h>
+#include <private/qunicodetables_p.h>
+#ifdef Q_OS_HPUX
+// for GLXPBuffer
+#include <private/qglpixelbuffer_p.h>
+#endif
+
+// We always define GLX_EXT_texture_from_pixmap ourselves because
+// we can't trust system headers to do it properly
+#define GLX_EXT_texture_from_pixmap 1
+
+#define INT8 dummy_INT8
+#define INT32 dummy_INT32
+#include <GL/glx.h>
+#undef INT8
+#undef INT32
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#ifdef Q_OS_VXWORS
+# ifdef open
+# undef open
+# endif
+# ifdef getpid
+# undef getpid
+# endif
+#endif // Q_OS_VXWORKS
+#include <X11/Xatom.h>
+
+#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
+#include <dlfcn.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern Drawable qt_x11Handle(const QPaintDevice *pd);
+extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
+
+#ifndef GLX_ARB_multisample
+#define GLX_SAMPLE_BUFFERS_ARB 100000
+#define GLX_SAMPLES_ARB 100001
+#endif
+
+#ifndef GLX_TEXTURE_2D_BIT_EXT
+#define GLX_TEXTURE_2D_BIT_EXT 0x00000002
+#define GLX_TEXTURE_RECTANGLE_BIT_EXT 0x00000004
+#define GLX_BIND_TO_TEXTURE_RGB_EXT 0x20D0
+#define GLX_BIND_TO_TEXTURE_RGBA_EXT 0x20D1
+#define GLX_BIND_TO_MIPMAP_TEXTURE_EXT 0x20D2
+#define GLX_BIND_TO_TEXTURE_TARGETS_EXT 0x20D3
+#define GLX_Y_INVERTED_EXT 0x20D4
+#define GLX_TEXTURE_FORMAT_EXT 0x20D5
+#define GLX_TEXTURE_TARGET_EXT 0x20D6
+#define GLX_MIPMAP_TEXTURE_EXT 0x20D7
+#define GLX_TEXTURE_FORMAT_NONE_EXT 0x20D8
+#define GLX_TEXTURE_FORMAT_RGB_EXT 0x20D9
+#define GLX_TEXTURE_FORMAT_RGBA_EXT 0x20DA
+#define GLX_TEXTURE_2D_EXT 0x20DC
+#define GLX_TEXTURE_RECTANGLE_EXT 0x20DD
+#define GLX_FRONT_LEFT_EXT 0x20DE
+#endif
+
+#ifndef GLX_ARB_create_context
+#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001
+#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
+#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define GLX_CONTEXT_FLAGS_ARB 0x2094
+#endif
+
+#ifndef GLX_ARB_create_context_profile
+#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
+#endif
+
+/*
+ The qt_gl_choose_cmap function is internal and used by QGLWidget::setContext()
+ and GLX (not Windows). If the application can't find any sharable
+ colormaps, it must at least create as few colormaps as possible. The
+ dictionary solution below ensures only one colormap is created per visual.
+ Colormaps are also deleted when the application terminates.
+*/
+
+struct QCMapEntry {
+ QCMapEntry();
+ ~QCMapEntry();
+
+ Colormap cmap;
+ bool alloc;
+ XStandardColormap scmap;
+};
+
+QCMapEntry::QCMapEntry()
+{
+ cmap = 0;
+ alloc = false;
+ scmap.colormap = 0;
+}
+
+QCMapEntry::~QCMapEntry()
+{
+ if (alloc)
+ XFreeColormap(X11->display, cmap);
+}
+typedef QHash<int, QCMapEntry *> CMapEntryHash;
+typedef QHash<int, QMap<int, QRgb> > GLCMapHash;
+static bool mesa_gl = false;
+static bool first_time = true;
+
+static void cleanup_cmaps();
+
+struct QGLCMapCleanupHandler {
+ QGLCMapCleanupHandler() {
+ cmap_hash = new CMapEntryHash;
+ qglcmap_hash = new GLCMapHash;
+ }
+ ~QGLCMapCleanupHandler() {
+ delete cmap_hash;
+ delete qglcmap_hash;
+ }
+ CMapEntryHash *cmap_hash;
+ GLCMapHash *qglcmap_hash;
+};
+Q_GLOBAL_STATIC(QGLCMapCleanupHandler, cmap_handler)
+
+static void cleanup_cmaps()
+{
+ CMapEntryHash *hash = cmap_handler()->cmap_hash;
+ QHash<int, QCMapEntry *>::ConstIterator it = hash->constBegin();
+ while (it != hash->constEnd()) {
+ delete it.value();
+ ++it;
+ }
+
+ hash->clear();
+ cmap_handler()->qglcmap_hash->clear();
+}
+
+Colormap qt_gl_choose_cmap(Display *dpy, XVisualInfo *vi)
+{
+ if (first_time) {
+ const char *v = glXQueryServerString(dpy, vi->screen, GLX_VERSION);
+ if (v)
+ mesa_gl = (strstr(v, "Mesa") != 0);
+ first_time = false;
+ }
+
+ CMapEntryHash *hash = cmap_handler()->cmap_hash;
+ CMapEntryHash::ConstIterator it = hash->constFind((long) vi->visualid + (vi->screen * 256));
+ if (it != hash->constEnd())
+ return it.value()->cmap; // found colormap for visual
+
+ if (vi->visualid ==
+ XVisualIDFromVisual((Visual *) QX11Info::appVisual(vi->screen))) {
+ // qDebug("Using x11AppColormap");
+ return QX11Info::appColormap(vi->screen);
+ }
+
+ QCMapEntry *x = new QCMapEntry();
+
+ XStandardColormap *c;
+ int n, i;
+
+ // qDebug("Choosing cmap for vID %0x", vi->visualid);
+
+ if (mesa_gl) { // we're using MesaGL
+ Atom hp_cmaps = XInternAtom(dpy, "_HP_RGB_SMOOTH_MAP_LIST", true);
+ if (hp_cmaps && vi->visual->c_class == TrueColor && vi->depth == 8) {
+ if (XGetRGBColormaps(dpy,RootWindow(dpy,vi->screen),&c,&n,
+ hp_cmaps)) {
+ i = 0;
+ while (i < n && x->cmap == 0) {
+ if (c[i].visualid == vi->visual->visualid) {
+ x->cmap = c[i].colormap;
+ x->scmap = c[i];
+ //qDebug("Using HP_RGB scmap");
+
+ }
+ i++;
+ }
+ XFree((char *)c);
+ }
+ }
+ }
+ if (!x->cmap) {
+ if (XGetRGBColormaps(dpy,RootWindow(dpy,vi->screen),&c,&n,
+ XA_RGB_DEFAULT_MAP)) {
+ for (int i = 0; i < n && x->cmap == 0; ++i) {
+ if (!c[i].red_max ||
+ !c[i].green_max ||
+ !c[i].blue_max ||
+ !c[i].red_mult ||
+ !c[i].green_mult ||
+ !c[i].blue_mult)
+ continue; // invalid stdcmap
+ if (c[i].visualid == vi->visualid) {
+ x->cmap = c[i].colormap;
+ x->scmap = c[i];
+ //qDebug("Using RGB_DEFAULT scmap");
+ }
+ }
+ XFree((char *)c);
+ }
+ }
+ if (!x->cmap) { // no shared cmap found
+ x->cmap = XCreateColormap(dpy, RootWindow(dpy,vi->screen), vi->visual,
+ AllocNone);
+ x->alloc = true;
+ // qDebug("Allocating cmap");
+ }
+
+ // colormap hash should be cleanup only when the QApplication dtor is called
+ if (hash->isEmpty())
+ qAddPostRoutine(cleanup_cmaps);
+
+ // associate cmap with visualid
+ hash->insert((long) vi->visualid + (vi->screen * 256), x);
+ return x->cmap;
+}
+
+struct QTransColor
+{
+ VisualID vis;
+ int screen;
+ long color;
+};
+
+static QVector<QTransColor> trans_colors;
+static int trans_colors_init = false;
+
+static void find_trans_colors()
+{
+ struct OverlayProp {
+ long visual;
+ long type;
+ long value;
+ long layer;
+ };
+
+ trans_colors_init = true;
+
+ Display* appDisplay = X11->display;
+
+ int scr;
+ int lastsize = 0;
+ for (scr = 0; scr < ScreenCount(appDisplay); scr++) {
+ QWidget* rootWin = QApplication::desktop()->screen(scr);
+ if (!rootWin)
+ return; // Should not happen
+ Atom overlayVisualsAtom = XInternAtom(appDisplay,
+ "SERVER_OVERLAY_VISUALS", True);
+ if (overlayVisualsAtom == XNone)
+ return; // Server has no overlays
+
+ Atom actualType;
+ int actualFormat;
+ ulong nItems;
+ ulong bytesAfter;
+ unsigned char *retval = 0;
+ int res = XGetWindowProperty(appDisplay, rootWin->winId(),
+ overlayVisualsAtom, 0, 10000, False,
+ overlayVisualsAtom, &actualType,
+ &actualFormat, &nItems, &bytesAfter,
+ &retval);
+
+ if (res != Success || actualType != overlayVisualsAtom
+ || actualFormat != 32 || nItems < 4 || !retval)
+ return; // Error reading property
+
+ OverlayProp *overlayProps = (OverlayProp *)retval;
+
+ int numProps = nItems / 4;
+ trans_colors.resize(lastsize + numProps);
+ int j = lastsize;
+ for (int i = 0; i < numProps; i++) {
+ if (overlayProps[i].type == 1) {
+ trans_colors[j].vis = (VisualID)overlayProps[i].visual;
+ trans_colors[j].screen = scr;
+ trans_colors[j].color = (int)overlayProps[i].value;
+ j++;
+ }
+ }
+ XFree(overlayProps);
+ lastsize = j;
+ trans_colors.resize(lastsize);
+ }
+}
+
+/*****************************************************************************
+ QGLFormat UNIX/GLX-specific code
+ *****************************************************************************/
+
+void* qglx_getProcAddress(const char* procName)
+{
+ // On systems where the GL driver is pluggable (like Mesa), we have to use
+ // the glXGetProcAddressARB extension to resolve other function pointers as
+ // the symbols wont be in the GL library, but rather in a plugin loaded by
+ // the GL library.
+ typedef void* (*qt_glXGetProcAddressARB)(const char *);
+ static qt_glXGetProcAddressARB glXGetProcAddressARB = 0;
+ static bool triedResolvingGlxGetProcAddress = false;
+ if (!triedResolvingGlxGetProcAddress) {
+ triedResolvingGlxGetProcAddress = true;
+ QGLExtensionMatcher extensions(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS));
+ if (extensions.match("GLX_ARB_get_proc_address")) {
+#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
+ void *handle = dlopen(NULL, RTLD_LAZY);
+ if (handle) {
+ glXGetProcAddressARB = (qt_glXGetProcAddressARB) dlsym(handle, "glXGetProcAddressARB");
+ dlclose(handle);
+ }
+ if (!glXGetProcAddressARB)
+#endif
+ {
+#if !defined(QT_NO_LIBRARY)
+ extern const QString qt_gl_library_name();
+ QLibrary lib(qt_gl_library_name());
+ glXGetProcAddressARB = (qt_glXGetProcAddressARB) lib.resolve("glXGetProcAddressARB");
+#endif
+ }
+ }
+ }
+
+ void *procAddress = 0;
+ if (glXGetProcAddressARB)
+ procAddress = glXGetProcAddressARB(procName);
+
+ // If glXGetProcAddress didn't work, try looking the symbol up in the GL library
+#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
+ if (!procAddress) {
+ void *handle = dlopen(NULL, RTLD_LAZY);
+ if (handle) {
+ procAddress = dlsym(handle, procName);
+ dlclose(handle);
+ }
+ }
+#endif
+#if !defined(QT_NO_LIBRARY)
+ if (!procAddress) {
+ extern const QString qt_gl_library_name();
+ QLibrary lib(qt_gl_library_name());
+ procAddress = lib.resolve(procName);
+ }
+#endif
+
+ return procAddress;
+}
+
+bool QGLFormat::hasOpenGL()
+{
+ return glXQueryExtension(X11->display, 0, 0) != 0;
+}
+
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ if (!trans_colors_init)
+ find_trans_colors();
+ return trans_colors.size() > 0;
+}
+
+static bool buildSpec(int* spec, const QGLFormat& f, QPaintDevice* paintDevice,
+ int bufDepth, bool onlyFBConfig = false)
+{
+ int i = 0;
+ spec[i++] = GLX_LEVEL;
+ spec[i++] = f.plane();
+ const QX11Info *xinfo = qt_x11Info(paintDevice);
+ bool useFBConfig = onlyFBConfig;
+
+#if defined(GLX_VERSION_1_3) && !defined(QT_NO_XRENDER) && !defined(Q_OS_HPUX)
+ /*
+ HPUX defines GLX_VERSION_1_3 but does not implement the corresponding functions.
+ Specifically glXChooseFBConfig and glXGetVisualFromFBConfig are not implemented.
+ */
+ QWidget* widget = 0;
+ if (paintDevice->devType() == QInternal::Widget)
+ widget = static_cast<QWidget*>(paintDevice);
+
+ // Only use glXChooseFBConfig for widgets if we're trying to get an ARGB visual
+ if (widget && widget->testAttribute(Qt::WA_TranslucentBackground) && X11->use_xrender)
+ useFBConfig = true;
+#endif
+
+#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_info)
+ static bool useTranspExt = false;
+ static bool useTranspExtChecked = false;
+ if (f.plane() && !useTranspExtChecked && paintDevice) {
+ QGLExtensionMatcher extensions(glXQueryExtensionsString(xinfo->display(), xinfo->screen()));
+ useTranspExt = extensions.match("GLX_EXT_visual_info");
+ //# (A bit simplistic; that could theoretically be a substring)
+ if (useTranspExt) {
+ QByteArray cstr(glXGetClientString(xinfo->display(), GLX_VENDOR));
+ useTranspExt = !cstr.contains("Xi Graphics"); // bug workaround
+ if (useTranspExt) {
+ // bug workaround - some systems (eg. FireGL) refuses to return an overlay
+ // visual if the GLX_TRANSPARENT_TYPE_EXT attribute is specified, even if
+ // the implementation supports transparent overlays
+ int tmpSpec[] = { GLX_LEVEL, f.plane(), GLX_TRANSPARENT_TYPE_EXT,
+ f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT,
+ XNone };
+ XVisualInfo * vinf = glXChooseVisual(xinfo->display(), xinfo->screen(), tmpSpec);
+ if (!vinf) {
+ useTranspExt = false;
+ }
+ }
+ }
+
+ useTranspExtChecked = true;
+ }
+ if (f.plane() && useTranspExt && !useFBConfig) {
+ // Required to avoid non-transparent overlay visual(!) on some systems
+ spec[i++] = GLX_TRANSPARENT_TYPE_EXT;
+ spec[i++] = f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT;
+ }
+#endif
+
+#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
+ // GLX_RENDER_TYPE is only in glx >=1.3
+ if (useFBConfig) {
+ spec[i++] = GLX_RENDER_TYPE;
+ spec[i++] = f.rgba() ? GLX_RGBA_BIT : GLX_COLOR_INDEX_BIT;
+ }
+#endif
+
+ if (f.doubleBuffer())
+ spec[i++] = GLX_DOUBLEBUFFER;
+ if (useFBConfig)
+ spec[i++] = True;
+ if (f.depth()) {
+ spec[i++] = GLX_DEPTH_SIZE;
+ spec[i++] = f.depthBufferSize() == -1 ? 1 : f.depthBufferSize();
+ }
+ if (f.stereo()) {
+ spec[i++] = GLX_STEREO;
+ if (useFBConfig)
+ spec[i++] = True;
+ }
+ if (f.stencil()) {
+ spec[i++] = GLX_STENCIL_SIZE;
+ spec[i++] = f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize();
+ }
+ if (f.rgba()) {
+ if (!useFBConfig)
+ spec[i++] = GLX_RGBA;
+ spec[i++] = GLX_RED_SIZE;
+ spec[i++] = f.redBufferSize() == -1 ? 1 : f.redBufferSize();
+ spec[i++] = GLX_GREEN_SIZE;
+ spec[i++] = f.greenBufferSize() == -1 ? 1 : f.greenBufferSize();
+ spec[i++] = GLX_BLUE_SIZE;
+ spec[i++] = f.blueBufferSize() == -1 ? 1 : f.blueBufferSize();
+ if (f.alpha()) {
+ spec[i++] = GLX_ALPHA_SIZE;
+ spec[i++] = f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize();
+ }
+ if (f.accum()) {
+ spec[i++] = GLX_ACCUM_RED_SIZE;
+ spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ spec[i++] = GLX_ACCUM_GREEN_SIZE;
+ spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ spec[i++] = GLX_ACCUM_BLUE_SIZE;
+ spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ if (f.alpha()) {
+ spec[i++] = GLX_ACCUM_ALPHA_SIZE;
+ spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ }
+ }
+ } else {
+ spec[i++] = GLX_BUFFER_SIZE;
+ spec[i++] = bufDepth;
+ }
+
+ if (f.sampleBuffers()) {
+ spec[i++] = GLX_SAMPLE_BUFFERS_ARB;
+ spec[i++] = 1;
+ spec[i++] = GLX_SAMPLES_ARB;
+ spec[i++] = f.samples() == -1 ? 4 : f.samples();
+ }
+
+#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
+ if (useFBConfig) {
+ spec[i++] = GLX_DRAWABLE_TYPE;
+ switch(paintDevice->devType()) {
+ case QInternal::Pixmap:
+ spec[i++] = GLX_PIXMAP_BIT;
+ break;
+ case QInternal::Pbuffer:
+ spec[i++] = GLX_PBUFFER_BIT;
+ break;
+ default:
+ qWarning("QGLContext: Unknown paint device type %d", paintDevice->devType());
+ // Fall-through & assume it's a window
+ case QInternal::Widget:
+ spec[i++] = GLX_WINDOW_BIT;
+ break;
+ };
+ }
+#endif
+
+ spec[i] = XNone;
+ return useFBConfig;
+}
+
+/*****************************************************************************
+ QGLContext UNIX/GLX-specific code
+ *****************************************************************************/
+
+bool QGLContext::chooseContext(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+ const QX11Info *xinfo = qt_x11Info(d->paintDevice);
+
+ Display* disp = xinfo->display();
+ d->vi = chooseVisual();
+ if (!d->vi)
+ return false;
+
+ if (deviceIsPixmap() &&
+ (((XVisualInfo*)d->vi)->depth != xinfo->depth() ||
+ ((XVisualInfo*)d->vi)->screen != xinfo->screen()))
+ {
+ XFree(d->vi);
+ XVisualInfo appVisInfo;
+ memset(&appVisInfo, 0, sizeof(XVisualInfo));
+ appVisInfo.visualid = XVisualIDFromVisual((Visual *) xinfo->visual());
+ appVisInfo.screen = xinfo->screen();
+ int nvis;
+ d->vi = XGetVisualInfo(disp, VisualIDMask | VisualScreenMask, &appVisInfo, &nvis);
+ if (!d->vi)
+ return false;
+
+ int useGL;
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_USE_GL, &useGL);
+ if (!useGL)
+ return false; //# Chickening out already...
+ }
+ int res;
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_LEVEL, &res);
+ d->glFormat.setPlane(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_DOUBLEBUFFER, &res);
+ d->glFormat.setDoubleBuffer(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_DEPTH_SIZE, &res);
+ d->glFormat.setDepth(res);
+ if (d->glFormat.depth())
+ d->glFormat.setDepthBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_RGBA, &res);
+ d->glFormat.setRgba(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_RED_SIZE, &res);
+ d->glFormat.setRedBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_GREEN_SIZE, &res);
+ d->glFormat.setGreenBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_BLUE_SIZE, &res);
+ d->glFormat.setBlueBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_ALPHA_SIZE, &res);
+ d->glFormat.setAlpha(res);
+ if (d->glFormat.alpha())
+ d->glFormat.setAlphaBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_ACCUM_RED_SIZE, &res);
+ d->glFormat.setAccum(res);
+ if (d->glFormat.accum())
+ d->glFormat.setAccumBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_STENCIL_SIZE, &res);
+ d->glFormat.setStencil(res);
+ if (d->glFormat.stencil())
+ d->glFormat.setStencilBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_STEREO, &res);
+ d->glFormat.setStereo(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_SAMPLE_BUFFERS_ARB, &res);
+ d->glFormat.setSampleBuffers(res);
+ if (d->glFormat.sampleBuffers()) {
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_SAMPLES_ARB, &res);
+ d->glFormat.setSamples(res);
+ }
+
+ Bool direct = format().directRendering() ? True : False;
+
+ if (shareContext &&
+ (!shareContext->isValid() || !shareContext->d_func()->cx)) {
+ qWarning("QGLContext::chooseContext(): Cannot share with invalid context");
+ shareContext = 0;
+ }
+
+ // 1. Sharing between rgba and color-index will give wrong colors.
+ // 2. Contexts cannot be shared btw. direct/non-direct renderers.
+ // 3. Pixmaps cannot share contexts that are set up for direct rendering.
+ // 4. If the contexts are not created on the same screen, they can't be shared
+
+ if (shareContext
+ && (format().rgba() != shareContext->format().rgba()
+ || (deviceIsPixmap() && glXIsDirect(disp, (GLXContext)shareContext->d_func()->cx))
+ || (shareContext->d_func()->screen != xinfo->screen())))
+ {
+ shareContext = 0;
+ }
+
+ const int major = d->reqFormat.majorVersion();
+ const int minor = d->reqFormat.minorVersion();
+ const int profile = d->reqFormat.profile() == QGLFormat::CompatibilityProfile
+ ? GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB
+ : GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
+
+ d->cx = 0;
+
+#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
+ /*
+ HPUX defines GLX_VERSION_1_3 but does not implement the corresponding functions.
+ Specifically glXChooseFBConfig and glXGetVisualFromFBConfig are not implemented.
+ */
+ if ((major == 3 && minor >= 2) || major > 3) {
+ QGLTemporaryContext *tmpContext = 0;
+ if (!QGLContext::currentContext())
+ tmpContext = new QGLTemporaryContext;
+
+ int attributes[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, major,
+ GLX_CONTEXT_MINOR_VERSION_ARB, minor,
+ GLX_CONTEXT_PROFILE_MASK_ARB, profile,
+ 0 };
+
+ typedef GLXContext ( * Q_PFNGLXCREATECONTEXTATTRIBSARBPROC)
+ (Display* dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list);
+
+
+ Q_PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs =
+ (Q_PFNGLXCREATECONTEXTATTRIBSARBPROC) qglx_getProcAddress("glXCreateContextAttribsARB");
+
+ if (glXCreateContextAttribs) {
+ int spec[45];
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_BUFFER_SIZE, &res);
+ buildSpec(spec, format(), d->paintDevice, res, true);
+
+ GLXFBConfig *configs;
+ int configCount = 0;
+ configs = glXChooseFBConfig(disp, xinfo->screen(), spec, &configCount);
+
+ if (configs && configCount > 0) {
+ d->cx = glXCreateContextAttribs(disp, configs[0],
+ shareContext ? (GLXContext)shareContext->d_func()->cx : 0, direct, attributes);
+ if (!d->cx && shareContext) {
+ shareContext = 0;
+ d->cx = glXCreateContextAttribs(disp, configs[0], 0, direct, attributes);
+ }
+ d->screen = ((XVisualInfo*)d->vi)->screen;
+ }
+ XFree(configs);
+ } else {
+ qWarning("QGLContext::chooseContext(): OpenGL %d.%d is not supported", major, minor);
+ }
+
+ if (tmpContext)
+ delete tmpContext;
+ }
+#else
+ Q_UNUSED(major);
+ Q_UNUSED(minor);
+ Q_UNUSED(profile);
+#endif
+
+ if (!d->cx && shareContext) {
+ d->cx = glXCreateContext(disp, (XVisualInfo *)d->vi,
+ (GLXContext)shareContext->d_func()->cx, direct);
+ d->screen = ((XVisualInfo*)d->vi)->screen;
+ }
+ if (!d->cx) {
+ d->cx = glXCreateContext(disp, (XVisualInfo *)d->vi, NULL, direct);
+ d->screen = ((XVisualInfo*)d->vi)->screen;
+ shareContext = 0;
+ }
+
+ if (shareContext && d->cx) {
+ QGLContext *share = const_cast<QGLContext *>(shareContext);
+ d->sharing = true;
+ share->d_func()->sharing = true;
+ }
+
+ if (!d->cx)
+ return false;
+ d->glFormat.setDirectRendering(glXIsDirect(disp, (GLXContext)d->cx));
+ if (deviceIsPixmap()) {
+#if defined(GLX_MESA_pixmap_colormap) && defined(QGL_USE_MESA_EXT)
+ d->gpm = glXCreateGLXPixmapMESA(disp, (XVisualInfo *)d->vi,
+ qt_x11Handle(d->paintDevice),
+ qt_gl_choose_cmap(disp, (XVisualInfo *)d->vi));
+#else
+ d->gpm = (quint32)glXCreateGLXPixmap(disp, (XVisualInfo *)d->vi,
+ qt_x11Handle(d->paintDevice));
+#endif
+ if (!d->gpm)
+ return false;
+ }
+ QGLExtensionMatcher extensions(glXQueryExtensionsString(xinfo->display(), xinfo->screen()));
+ if (extensions.match("GLX_SGI_video_sync")) {
+ if (d->glFormat.swapInterval() == -1)
+ d->glFormat.setSwapInterval(0);
+ } else {
+ d->glFormat.setSwapInterval(-1);
+ }
+ return true;
+}
+
+/*
+ See qgl.cpp for qdoc comment.
+ */
+void *QGLContext::chooseVisual()
+{
+ Q_D(QGLContext);
+ static const int bufDepths[] = { 8, 4, 2, 1 }; // Try 16, 12 also?
+ //todo: if pixmap, also make sure that vi->depth == pixmap->depth
+ void* vis = 0;
+ int i = 0;
+ bool fail = false;
+ QGLFormat fmt = format();
+ bool tryDouble = !fmt.doubleBuffer(); // Some GL impl's only have double
+ bool triedDouble = false;
+ bool triedSample = false;
+ if (fmt.sampleBuffers())
+ fmt.setSampleBuffers(QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers);
+ while(!fail && !(vis = tryVisual(fmt, bufDepths[i]))) {
+ if (!fmt.rgba() && bufDepths[i] > 1) {
+ i++;
+ continue;
+ }
+ if (tryDouble) {
+ fmt.setDoubleBuffer(true);
+ tryDouble = false;
+ triedDouble = true;
+ continue;
+ } else if (triedDouble) {
+ fmt.setDoubleBuffer(false);
+ triedDouble = false;
+ }
+ if (!triedSample && fmt.sampleBuffers()) {
+ fmt.setSampleBuffers(false);
+ triedSample = true;
+ continue;
+ }
+ if (fmt.stereo()) {
+ fmt.setStereo(false);
+ continue;
+ }
+ if (fmt.accum()) {
+ fmt.setAccum(false);
+ continue;
+ }
+ if (fmt.stencil()) {
+ fmt.setStencil(false);
+ continue;
+ }
+ if (fmt.alpha()) {
+ fmt.setAlpha(false);
+ continue;
+ }
+ if (fmt.depth()) {
+ fmt.setDepth(false);
+ continue;
+ }
+ if (fmt.doubleBuffer()) {
+ fmt.setDoubleBuffer(false);
+ continue;
+ }
+ fail = true;
+ }
+ d->glFormat = fmt;
+ return vis;
+}
+
+/*
+ See qgl.cpp for qdoc comment.
+ */
+void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth)
+{
+ Q_D(QGLContext);
+ int spec[45];
+ const QX11Info *xinfo = qt_x11Info(d->paintDevice);
+ bool useFBConfig = buildSpec(spec, f, d->paintDevice, bufDepth, false);
+
+ XVisualInfo* chosenVisualInfo = 0;
+
+#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
+ while (useFBConfig) {
+ GLXFBConfig *configs;
+ int configCount = 0;
+ configs = glXChooseFBConfig(xinfo->display(), xinfo->screen(), spec, &configCount);
+
+ if (!configs)
+ break; // fallback to trying glXChooseVisual
+
+ for (int i = 0; i < configCount; ++i) {
+ XVisualInfo* vi;
+ vi = glXGetVisualFromFBConfig(xinfo->display(), configs[i]);
+ if (!vi)
+ continue;
+
+#if !defined(QT_NO_XRENDER)
+ QWidget* w = 0;
+ if (d->paintDevice->devType() == QInternal::Widget)
+ w = static_cast<QWidget*>(d->paintDevice);
+
+ if (w && w->testAttribute(Qt::WA_TranslucentBackground) && f.alpha()) {
+ // Attempt to find a config who's visual has a proper alpha channel
+ XRenderPictFormat *pictFormat;
+ pictFormat = XRenderFindVisualFormat(xinfo->display(), vi->visual);
+
+ if (pictFormat && (pictFormat->type == PictTypeDirect) && pictFormat->direct.alphaMask) {
+ // The pict format for the visual matching the FBConfig indicates ARGB
+ if (chosenVisualInfo)
+ XFree(chosenVisualInfo);
+ chosenVisualInfo = vi;
+ break;
+ }
+ } else
+#endif //QT_NO_XRENDER
+ if (chosenVisualInfo) {
+ // If we've got a visual we can use and we're not trying to find one with a
+ // real alpha channel, we might as well just use the one we've got
+ break;
+ }
+
+ if (!chosenVisualInfo)
+ chosenVisualInfo = vi; // Have something to fall back to
+ else
+ XFree(vi);
+ }
+
+ XFree(configs);
+ break;
+ }
+#endif // defined(GLX_VERSION_1_3)
+
+ if (!chosenVisualInfo)
+ chosenVisualInfo = glXChooseVisual(xinfo->display(), xinfo->screen(), spec);
+
+ return chosenVisualInfo;
+}
+
+
+void QGLContext::reset()
+{
+ Q_D(QGLContext);
+ if (!d->valid)
+ return;
+ d->cleanup();
+ const QX11Info *xinfo = qt_x11Info(d->paintDevice);
+ doneCurrent();
+ if (d->gpm)
+ glXDestroyGLXPixmap(xinfo->display(), (GLXPixmap)d->gpm);
+ d->gpm = 0;
+ glXDestroyContext(xinfo->display(), (GLXContext)d->cx);
+ if (d->vi)
+ XFree(d->vi);
+ d->vi = 0;
+ d->cx = 0;
+ d->crWin = false;
+ d->sharing = false;
+ d->valid = false;
+ d->transpColor = QColor();
+ d->initDone = false;
+ QGLContextGroup::removeShare(this);
+}
+
+
+void QGLContext::makeCurrent()
+{
+ Q_D(QGLContext);
+ if (!d->valid) {
+ qWarning("QGLContext::makeCurrent(): Cannot make invalid context current.");
+ return;
+ }
+ const QX11Info *xinfo = qt_x11Info(d->paintDevice);
+ bool ok = true;
+ if (d->paintDevice->devType() == QInternal::Pixmap) {
+ ok = glXMakeCurrent(xinfo->display(), (GLXPixmap)d->gpm, (GLXContext)d->cx);
+ } else if (d->paintDevice->devType() == QInternal::Pbuffer) {
+ ok = glXMakeCurrent(xinfo->display(), (GLXPbuffer)d->pbuf, (GLXContext)d->cx);
+ } else if (d->paintDevice->devType() == QInternal::Widget) {
+ ok = glXMakeCurrent(xinfo->display(), ((QWidget *)d->paintDevice)->internalWinId(), (GLXContext)d->cx);
+ }
+ if (!ok)
+ qWarning("QGLContext::makeCurrent(): Failed.");
+
+ if (ok)
+ QGLContextPrivate::setCurrentContext(this);
+}
+
+void QGLContext::doneCurrent()
+{
+ Q_D(QGLContext);
+ glXMakeCurrent(qt_x11Info(d->paintDevice)->display(), 0, 0);
+ QGLContextPrivate::setCurrentContext(0);
+}
+
+
+void QGLContext::swapBuffers() const
+{
+ Q_D(const QGLContext);
+ if (!d->valid)
+ return;
+ if (!deviceIsPixmap()) {
+ int interval = d->glFormat.swapInterval();
+ if (interval > 0) {
+ typedef int (*qt_glXGetVideoSyncSGI)(uint *);
+ typedef int (*qt_glXWaitVideoSyncSGI)(int, int, uint *);
+ static qt_glXGetVideoSyncSGI glXGetVideoSyncSGI = 0;
+ static qt_glXWaitVideoSyncSGI glXWaitVideoSyncSGI = 0;
+ static bool resolved = false;
+ if (!resolved) {
+ const QX11Info *xinfo = qt_x11Info(d->paintDevice);
+ QGLExtensionMatcher extensions(glXQueryExtensionsString(xinfo->display(), xinfo->screen()));
+ if (extensions.match("GLX_SGI_video_sync")) {
+ glXGetVideoSyncSGI = (qt_glXGetVideoSyncSGI)qglx_getProcAddress("glXGetVideoSyncSGI");
+ glXWaitVideoSyncSGI = (qt_glXWaitVideoSyncSGI)qglx_getProcAddress("glXWaitVideoSyncSGI");
+ }
+ resolved = true;
+ }
+ if (glXGetVideoSyncSGI && glXWaitVideoSyncSGI) {
+ uint counter;
+ if (!glXGetVideoSyncSGI(&counter))
+ glXWaitVideoSyncSGI(interval + 1, (counter + interval) % (interval + 1), &counter);
+ }
+ }
+ glXSwapBuffers(qt_x11Info(d->paintDevice)->display(),
+ static_cast<QWidget *>(d->paintDevice)->winId());
+ }
+}
+
+QColor QGLContext::overlayTransparentColor() const
+{
+ if (isValid())
+ return Qt::transparent;
+ return QColor(); // Invalid color
+}
+
+static uint qt_transparent_pixel(VisualID id, int screen)
+{
+ for (int i = 0; i < trans_colors.size(); i++) {
+ if (trans_colors[i].vis == id && trans_colors[i].screen == screen)
+ return trans_colors[i].color;
+ }
+ return 0;
+}
+
+uint QGLContext::colorIndex(const QColor& c) const
+{
+ Q_D(const QGLContext);
+ int screen = ((XVisualInfo *)d->vi)->screen;
+ QColormap colmap = QColormap::instance(screen);
+ if (isValid()) {
+ if (format().plane() && c == Qt::transparent) {
+ return qt_transparent_pixel(((XVisualInfo *)d->vi)->visualid,
+ ((XVisualInfo *)d->vi)->screen);
+ }
+ if (((XVisualInfo*)d->vi)->visualid ==
+ XVisualIDFromVisual((Visual *) QX11Info::appVisual(screen)))
+ return colmap.pixel(c); // We're using QColor's cmap
+
+ XVisualInfo *info = (XVisualInfo *) d->vi;
+ CMapEntryHash *hash = cmap_handler()->cmap_hash;
+ CMapEntryHash::ConstIterator it = hash->constFind(long(info->visualid)
+ + (info->screen * 256));
+ QCMapEntry *x = 0;
+ if (it != hash->constEnd())
+ x = it.value();
+ if (x && !x->alloc) { // It's a standard colormap
+ int rf = (int)(((float)c.red() * (x->scmap.red_max+1))/256.0);
+ int gf = (int)(((float)c.green() * (x->scmap.green_max+1))/256.0);
+ int bf = (int)(((float)c.blue() * (x->scmap.blue_max+1))/256.0);
+ uint p = x->scmap.base_pixel
+ + (rf * x->scmap.red_mult)
+ + (gf * x->scmap.green_mult)
+ + (bf * x->scmap.blue_mult);
+ return p;
+ } else {
+ QMap<int, QRgb> &cmap = (*cmap_handler()->qglcmap_hash)[(long)info->visualid];
+
+ // already in the map?
+ QRgb target = c.rgb();
+ QMap<int, QRgb>::Iterator it = cmap.begin();
+ for (; it != cmap.end(); ++it) {
+ if ((*it) == target)
+ return it.key();
+ }
+
+ // need to alloc color
+ unsigned long plane_mask[2];
+ unsigned long color_map_entry;
+ if (!XAllocColorCells (QX11Info::display(), x->cmap, true, plane_mask, 0,
+ &color_map_entry, 1))
+ return colmap.pixel(c);
+
+ XColor col;
+ col.flags = DoRed | DoGreen | DoBlue;
+ col.pixel = color_map_entry;
+ col.red = (ushort)((qRed(c.rgb()) / 255.0) * 65535.0 + 0.5);
+ col.green = (ushort)((qGreen(c.rgb()) / 255.0) * 65535.0 + 0.5);
+ col.blue = (ushort)((qBlue(c.rgb()) / 255.0) * 65535.0 + 0.5);
+ XStoreColor(QX11Info::display(), x->cmap, &col);
+
+ cmap.insert(color_map_entry, target);
+ return color_map_entry;
+ }
+ }
+ return 0;
+}
+
+#ifndef QT_NO_FONTCONFIG
+/*! \internal
+ This is basically a substitute for glxUseXFont() which can only
+ handle XLFD fonts. This version relies on freetype to render the
+ glyphs, but it works with all fonts that fontconfig provides - both
+ antialiased and aliased bitmap and outline fonts.
+*/
+static void qgl_use_font(QFontEngineFT *engine, int first, int count, int listBase)
+{
+ GLfloat color[4];
+ glGetFloatv(GL_CURRENT_COLOR, color);
+
+ // save the pixel unpack state
+ GLint gl_swapbytes, gl_lsbfirst, gl_rowlength, gl_skiprows, gl_skippixels, gl_alignment;
+ glGetIntegerv (GL_UNPACK_SWAP_BYTES, &gl_swapbytes);
+ glGetIntegerv (GL_UNPACK_LSB_FIRST, &gl_lsbfirst);
+ glGetIntegerv (GL_UNPACK_ROW_LENGTH, &gl_rowlength);
+ glGetIntegerv (GL_UNPACK_SKIP_ROWS, &gl_skiprows);
+ glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &gl_skippixels);
+ glGetIntegerv (GL_UNPACK_ALIGNMENT, &gl_alignment);
+
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ const bool antialiased = engine->drawAntialiased();
+ FT_Face face = engine->lockFace();
+
+ // start generating font glyphs
+ for (int i = first; i < count; ++i) {
+ int list = listBase + i;
+ GLfloat x0, y0, dx, dy;
+
+ FT_Error err;
+
+ err = FT_Load_Glyph(face, FT_Get_Char_Index(face, i), FT_LOAD_DEFAULT);
+ if (err) {
+ qDebug("failed loading glyph %d from font", i);
+ Q_ASSERT(!err);
+ }
+ err = FT_Render_Glyph(face->glyph, (antialiased ? FT_RENDER_MODE_NORMAL
+ : FT_RENDER_MODE_MONO));
+ if (err) {
+ qDebug("failed rendering glyph %d from font", i);
+ Q_ASSERT(!err);
+ }
+
+ FT_Bitmap bm = face->glyph->bitmap;
+ x0 = face->glyph->metrics.horiBearingX >> 6;
+ y0 = (face->glyph->metrics.height - face->glyph->metrics.horiBearingY) >> 6;
+ dx = face->glyph->metrics.horiAdvance >> 6;
+ dy = 0;
+ int sz = bm.pitch * bm.rows;
+ uint *aa_glyph = 0;
+ uchar *ua_glyph = 0;
+
+ if (antialiased)
+ aa_glyph = new uint[sz];
+ else
+ ua_glyph = new uchar[sz];
+
+ // convert to GL format
+ for (int y = 0; y < bm.rows; ++y) {
+ for (int x = 0; x < bm.pitch; ++x) {
+ int c1 = y*bm.pitch + x;
+ int c2 = (bm.rows - y - 1) > 0 ? (bm.rows-y-1)*bm.pitch + x : x;
+ if (antialiased) {
+ aa_glyph[c1] = (int(color[0]*255) << 24)
+ | (int(color[1]*255) << 16)
+ | (int(color[2]*255) << 8) | bm.buffer[c2];
+ } else {
+ ua_glyph[c1] = bm.buffer[c2];
+ }
+ }
+ }
+
+ glNewList(list, GL_COMPILE);
+ if (antialiased) {
+ // calling glBitmap() is just a trick to move the current
+ // raster pos, since glGet*() won't work in display lists
+ glBitmap(0, 0, 0, 0, x0, -y0, 0);
+ glDrawPixels(bm.pitch, bm.rows, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, aa_glyph);
+ glBitmap(0, 0, 0, 0, dx-x0, y0, 0);
+ } else {
+ glBitmap(bm.pitch*8, bm.rows, -x0, y0, dx, dy, ua_glyph);
+ }
+ glEndList();
+ antialiased ? delete[] aa_glyph : delete[] ua_glyph;
+ }
+
+ engine->unlockFace();
+
+ // restore pixel unpack settings
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, gl_swapbytes);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, gl_lsbfirst);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, gl_rowlength);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS, gl_skiprows);
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, gl_skippixels);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, gl_alignment);
+}
+#endif
+
+#undef d
+void QGLContext::generateFontDisplayLists(const QFont & fnt, int listBase)
+{
+ QFont f(fnt);
+ QFontEngine *engine = f.d->engineForScript(QUnicodeTables::Common);
+
+ if (engine->type() == QFontEngine::Multi)
+ engine = static_cast<QFontEngineMulti *>(engine)->engine(0);
+#ifndef QT_NO_FONTCONFIG
+ if(engine->type() == QFontEngine::Freetype) {
+ qgl_use_font(static_cast<QFontEngineFT *>(engine), 0, 256, listBase);
+ return;
+ }
+#endif
+ // glXUseXFont() only works with XLFD font structures and a few GL
+ // drivers crash if 0 is passed as the font handle
+ f.setStyleStrategy(QFont::OpenGLCompatible);
+ if (f.handle() && engine->type() == QFontEngine::XLFD)
+ glXUseXFont(static_cast<Font>(f.handle()), 0, 256, listBase);
+}
+
+void *QGLContext::getProcAddress(const QString &proc) const
+{
+ typedef void *(*qt_glXGetProcAddressARB)(const GLubyte *);
+ static qt_glXGetProcAddressARB glXGetProcAddressARB = 0;
+ static bool resolved = false;
+
+ if (resolved && !glXGetProcAddressARB)
+ return 0;
+ if (!glXGetProcAddressARB) {
+ QGLExtensionMatcher extensions(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS));
+ if (extensions.match("GLX_ARB_get_proc_address")) {
+#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
+ void *handle = dlopen(NULL, RTLD_LAZY);
+ if (handle) {
+ glXGetProcAddressARB = (qt_glXGetProcAddressARB) dlsym(handle, "glXGetProcAddressARB");
+ dlclose(handle);
+ }
+ if (!glXGetProcAddressARB)
+#endif
+ {
+#if !defined(QT_NO_LIBRARY)
+ extern const QString qt_gl_library_name();
+ QLibrary lib(qt_gl_library_name());
+ glXGetProcAddressARB = (qt_glXGetProcAddressARB) lib.resolve("glXGetProcAddressARB");
+#endif
+ }
+ }
+ resolved = true;
+ }
+ if (!glXGetProcAddressARB)
+ return 0;
+ return glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(proc.toLatin1().data()));
+}
+
+/*
+ QGLTemporaryContext implementation
+*/
+
+class QGLTemporaryContextPrivate {
+public:
+ bool initialized;
+ Window drawable;
+ GLXContext context;
+ GLXDrawable oldDrawable;
+ GLXContext oldContext;
+};
+
+QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
+ : d(new QGLTemporaryContextPrivate)
+{
+ d->initialized = false;
+ d->oldDrawable = 0;
+ d->oldContext = 0;
+ int screen = 0;
+
+ int attribs[] = {GLX_RGBA, XNone};
+ XVisualInfo *vi = glXChooseVisual(X11->display, screen, attribs);
+ if (!vi) {
+ qWarning("QGLTempContext: No GL capable X visuals available.");
+ return;
+ }
+
+ int useGL;
+ glXGetConfig(X11->display, vi, GLX_USE_GL, &useGL);
+ if (!useGL) {
+ XFree(vi);
+ return;
+ }
+
+ d->oldDrawable = glXGetCurrentDrawable();
+ d->oldContext = glXGetCurrentContext();
+
+ XSetWindowAttributes a;
+ a.colormap = qt_gl_choose_cmap(X11->display, vi);
+ d->drawable = XCreateWindow(X11->display, RootWindow(X11->display, screen),
+ 0, 0, 1, 1, 0,
+ vi->depth, InputOutput, vi->visual,
+ CWColormap, &a);
+ d->context = glXCreateContext(X11->display, vi, 0, True);
+ if (d->context && glXMakeCurrent(X11->display, d->drawable, d->context)) {
+ d->initialized = true;
+ } else {
+ qWarning("QGLTempContext: Unable to create GL context.");
+ XDestroyWindow(X11->display, d->drawable);
+ }
+ XFree(vi);
+}
+
+QGLTemporaryContext::~QGLTemporaryContext()
+{
+ if (d->initialized) {
+ glXMakeCurrent(X11->display, 0, 0);
+ glXDestroyContext(X11->display, d->context);
+ XDestroyWindow(X11->display, d->drawable);
+ }
+ if (d->oldDrawable && d->oldContext)
+ glXMakeCurrent(X11->display, d->oldDrawable, d->oldContext);
+}
+
+/*****************************************************************************
+ QGLOverlayWidget (Internal overlay class for X11)
+ *****************************************************************************/
+
+class QGLOverlayWidget : public QGLWidget
+{
+ Q_OBJECT
+public:
+ QGLOverlayWidget(const QGLFormat& format, QGLWidget* parent, const QGLWidget* shareWidget=0);
+
+protected:
+ void initializeGL();
+ void paintGL();
+ void resizeGL(int w, int h);
+ bool x11Event(XEvent *e) { return realWidget->x11Event(e); }
+
+private:
+ QGLWidget* realWidget;
+
+private:
+ Q_DISABLE_COPY(QGLOverlayWidget)
+};
+
+
+QGLOverlayWidget::QGLOverlayWidget(const QGLFormat& format, QGLWidget* parent,
+ const QGLWidget* shareWidget)
+ : QGLWidget(format, parent, shareWidget ? shareWidget->d_func()->olw : 0)
+{
+ setAttribute(Qt::WA_X11OpenGLOverlay);
+ realWidget = parent;
+}
+
+
+
+void QGLOverlayWidget::initializeGL()
+{
+ QColor transparentColor = context()->overlayTransparentColor();
+ if (transparentColor.isValid())
+ qglClearColor(transparentColor);
+ else
+ qWarning("QGLOverlayWidget::initializeGL(): Could not get transparent color");
+ realWidget->initializeOverlayGL();
+}
+
+
+void QGLOverlayWidget::resizeGL(int w, int h)
+{
+ glViewport(0, 0, w, h);
+ realWidget->resizeOverlayGL(w, h);
+}
+
+
+void QGLOverlayWidget::paintGL()
+{
+ realWidget->paintOverlayGL();
+}
+
+#undef Bool
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qgl_x11.moc"
+QT_END_INCLUDE_NAMESPACE
+
+/*****************************************************************************
+ QGLWidget UNIX/GLX-specific code
+ *****************************************************************************/
+void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget *shareWidget)
+{
+ Q_Q(QGLWidget);
+ initContext(context, shareWidget);
+ olw = 0;
+
+ if (q->isValid() && context->format().hasOverlay()) {
+ QString olwName = q->objectName();
+ olwName += QLatin1String("-QGL_internal_overlay_widget");
+ olw = new QGLOverlayWidget(QGLFormat::defaultOverlayFormat(), q, shareWidget);
+ olw->setObjectName(olwName);
+ if (olw->isValid()) {
+ olw->setAutoBufferSwap(false);
+ olw->setFocusProxy(q);
+ }
+ else {
+ delete olw;
+ olw = 0;
+ glcx->d_func()->glFormat.setOverlay(false);
+ }
+ }
+}
+
+bool QGLWidgetPrivate::renderCxPm(QPixmap* pm)
+{
+ Q_Q(QGLWidget);
+ if (((XVisualInfo*)glcx->d_func()->vi)->depth != pm->depth())
+ return false;
+
+ GLXPixmap glPm;
+#if defined(GLX_MESA_pixmap_colormap) && defined(QGL_USE_MESA_EXT)
+ glPm = glXCreateGLXPixmapMESA(X11->display,
+ (XVisualInfo*)glcx->vi,
+ (Pixmap)pm->handle(),
+ qt_gl_choose_cmap(pm->X11->display,
+ (XVisualInfo*)glcx->vi));
+#else
+ glPm = (quint32)glXCreateGLXPixmap(X11->display,
+ (XVisualInfo*)glcx->d_func()->vi,
+ (Pixmap)pm->handle());
+#endif
+
+ if (!glXMakeCurrent(X11->display, glPm, (GLXContext)glcx->d_func()->cx)) {
+ glXDestroyGLXPixmap(X11->display, glPm);
+ return false;
+ }
+
+ glDrawBuffer(GL_FRONT);
+ if (!glcx->initialized())
+ q->glInit();
+ q->resizeGL(pm->width(), pm->height());
+ q->paintGL();
+ glFlush();
+ q->makeCurrent();
+ glXDestroyGLXPixmap(X11->display, glPm);
+ q->resizeGL(q->width(), q->height());
+ return true;
+}
+
+/*! \internal
+ Free up any allocated colormaps. This fn is only called for
+ top-level widgets.
+*/
+void QGLWidgetPrivate::cleanupColormaps()
+{
+ if (!cmap.handle()) {
+ return;
+ } else {
+ XFreeColormap(X11->display, (Colormap) cmap.handle());
+ cmap.setHandle(0);
+ }
+}
+
+void QGLWidget::setMouseTracking(bool enable)
+{
+ Q_D(QGLWidget);
+ if (d->olw)
+ d->olw->setMouseTracking(enable);
+ QWidget::setMouseTracking(enable);
+}
+
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ glXWaitX();
+ resizeGL(width(), height());
+ if (d->olw)
+ d->olw->setGeometry(rect());
+}
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ Q_D(const QGLWidget);
+ if (d->olw)
+ return d->olw->context();
+ else
+ return 0;
+}
+
+
+void QGLWidget::makeOverlayCurrent()
+{
+ Q_D(QGLWidget);
+ if (d->olw)
+ d->olw->makeCurrent();
+}
+
+
+void QGLWidget::updateOverlayGL()
+{
+ Q_D(QGLWidget);
+ if (d->olw)
+ d->olw->updateGL();
+}
+
+/*!
+ \internal
+
+ Sets a new QGLContext, \a context, for this QGLWidget, using the
+ shared context, \a shareContext. If \a deleteOldContext is true,
+ the original context is deleted; otherwise it is overridden.
+*/
+void QGLWidget::setContext(QGLContext *context,
+ const QGLContext* shareContext,
+ bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+ if (!context->deviceIsPixmap() && context->device() != this) {
+ qWarning("QGLWidget::setContext: Context must refer to this widget");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+
+ if (parentWidget()) {
+ // force creation of delay-created widgets
+ parentWidget()->winId();
+ if (parentWidget()->x11Info().screen() != x11Info().screen())
+ d_func()->xinfo = parentWidget()->d_func()->xinfo;
+ }
+
+ // If the application has set WA_TranslucentBackground and not explicitly set
+ // the alpha buffer size to zero, modify the format so it have an alpha channel
+ QGLFormat& fmt = d->glcx->d_func()->glFormat;
+ if (testAttribute(Qt::WA_TranslucentBackground) && fmt.alphaBufferSize() == -1)
+ fmt.setAlphaBufferSize(1);
+
+ bool createFailed = false;
+ if (!d->glcx->isValid()) {
+ if (!d->glcx->create(shareContext ? shareContext : oldcx))
+ createFailed = true;
+ }
+ if (createFailed) {
+ if (deleteOldContext)
+ delete oldcx;
+ return;
+ }
+
+ if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) {
+ if (deleteOldContext)
+ delete oldcx;
+ return;
+ }
+
+ bool visible = isVisible();
+ if (visible)
+ hide();
+
+ XVisualInfo *vi = (XVisualInfo*)d->glcx->d_func()->vi;
+ XSetWindowAttributes a;
+
+ QColormap colmap = QColormap::instance(vi->screen);
+ a.colormap = qt_gl_choose_cmap(QX11Info::display(), vi); // find best colormap
+ a.background_pixel = colmap.pixel(palette().color(backgroundRole()));
+ a.border_pixel = colmap.pixel(Qt::black);
+ Window p = RootWindow(X11->display, vi->screen);
+ if (parentWidget())
+ p = parentWidget()->winId();
+
+ Window w = XCreateWindow(X11->display, p, x(), y(), width(), height(),
+ 0, vi->depth, InputOutput, vi->visual,
+ CWBackPixel|CWBorderPixel|CWColormap, &a);
+ Window *cmw;
+ Window *cmwret;
+ int count;
+ if (XGetWMColormapWindows(X11->display, window()->winId(),
+ &cmwret, &count)) {
+ cmw = new Window[count+1];
+ memcpy((char *)cmw, (char *)cmwret, sizeof(Window)*count);
+ XFree((char *)cmwret);
+ int i;
+ for (i=0; i<count; i++) {
+ if (cmw[i] == winId()) { // replace old window
+ cmw[i] = w;
+ break;
+ }
+ }
+ if (i >= count) // append new window
+ cmw[count++] = w;
+ } else {
+ count = 1;
+ cmw = new Window[count];
+ cmw[0] = w;
+ }
+
+#if defined(GLX_MESA_release_buffers) && defined(QGL_USE_MESA_EXT)
+ if (oldcx && oldcx->windowCreated())
+ glXReleaseBuffersMESA(X11->display, winId());
+#endif
+ if (deleteOldContext)
+ delete oldcx;
+ oldcx = 0;
+
+ if (testAttribute(Qt::WA_WState_Created))
+ create(w);
+ else
+ d->createWinId(w);
+ XSetWMColormapWindows(X11->display, window()->winId(), cmw, count);
+ delete [] cmw;
+
+ // calling QWidget::create() will always result in a new paint
+ // engine being created - get rid of it and replace it with our
+ // own
+
+ if (visible)
+ show();
+ XFlush(X11->display);
+ d->glcx->setWindowCreated(true);
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ Q_D(const QGLWidget);
+ return d->cmap;
+}
+
+/*\internal
+ Store color values in the given colormap.
+*/
+static void qStoreColors(QWidget * tlw, Colormap cmap,
+ const QGLColormap & cols)
+{
+ Q_UNUSED(tlw);
+ XColor c;
+ QRgb color;
+
+ for (int i = 0; i < cols.size(); i++) {
+ color = cols.entryRgb(i);
+ c.pixel = i;
+ c.red = (ushort)((qRed(color) / 255.0) * 65535.0 + 0.5);
+ c.green = (ushort)((qGreen(color) / 255.0) * 65535.0 + 0.5);
+ c.blue = (ushort)((qBlue(color) / 255.0) * 65535.0 + 0.5);
+ c.flags = DoRed | DoGreen | DoBlue;
+ XStoreColor(X11->display, cmap, &c);
+ }
+}
+
+/*\internal
+ Check whether the given visual supports dynamic colormaps or not.
+*/
+static bool qCanAllocColors(QWidget * w)
+{
+ bool validVisual = false;
+ int numVisuals;
+ long mask;
+ XVisualInfo templ;
+ XVisualInfo * visuals;
+ VisualID id = XVisualIDFromVisual((Visual *) w->window()->x11Info().visual());
+
+ mask = VisualScreenMask;
+ templ.screen = w->x11Info().screen();
+ visuals = XGetVisualInfo(X11->display, mask, &templ, &numVisuals);
+
+ for (int i = 0; i < numVisuals; i++) {
+ if (visuals[i].visualid == id) {
+ switch (visuals[i].c_class) {
+ case TrueColor:
+ case StaticColor:
+ case StaticGray:
+ case XGrayScale:
+ validVisual = false;
+ break;
+ case DirectColor:
+ case PseudoColor:
+ validVisual = true;
+ break;
+ }
+ break;
+ }
+ }
+ XFree(visuals);
+
+ if (!validVisual)
+ return false;
+ return true;
+}
+
+
+void QGLWidget::setColormap(const QGLColormap & c)
+{
+ Q_D(QGLWidget);
+ QWidget * tlw = window(); // must return a valid widget
+
+ d->cmap = c;
+ if (!d->cmap.handle())
+ return;
+
+ if (!qCanAllocColors(this)) {
+ qWarning("QGLWidget::setColormap: Cannot create a read/write "
+ "colormap for this visual");
+ return;
+ }
+
+ // If the child GL widget is not of the same visual class as the
+ // toplevel widget we will get in trouble..
+ Window wid = tlw->winId();
+ Visual * vis = (Visual *) tlw->x11Info().visual();;
+ VisualID cvId = XVisualIDFromVisual((Visual *) x11Info().visual());
+ VisualID tvId = XVisualIDFromVisual((Visual *) tlw->x11Info().visual());
+ if (cvId != tvId) {
+ wid = winId();
+ vis = (Visual *) x11Info().visual();
+ }
+
+ if (!d->cmap.handle()) // allocate a cmap if necessary
+ d->cmap.setHandle(XCreateColormap(X11->display, wid, vis, AllocAll));
+
+ qStoreColors(this, (Colormap) d->cmap.handle(), c);
+ XSetWindowColormap(X11->display, wid, (Colormap) d->cmap.handle());
+
+ // tell the wm that this window has a special colormap
+ Window * cmw;
+ Window * cmwret;
+ int count;
+ if (XGetWMColormapWindows(X11->display, tlw->winId(), &cmwret, &count))
+ {
+ cmw = new Window[count+1];
+ memcpy((char *) cmw, (char *) cmwret, sizeof(Window) * count);
+ XFree((char *) cmwret);
+ int i;
+ for (i = 0; i < count; i++) {
+ if (cmw[i] == winId()) {
+ break;
+ }
+ }
+ if (i >= count) // append new window only if not in the list
+ cmw[count++] = winId();
+ } else {
+ count = 1;
+ cmw = new Window[count];
+ cmw[0] = winId();
+ }
+ XSetWMColormapWindows(X11->display, tlw->winId(), cmw, count);
+ delete [] cmw;
+}
+
+// Solaris defines glXBindTexImageEXT as part of the GL library
+#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
+typedef void (*qt_glXBindTexImageEXT)(Display*, GLXDrawable, int, const int*);
+typedef void (*qt_glXReleaseTexImageEXT)(Display*, GLXDrawable, int);
+static qt_glXBindTexImageEXT glXBindTexImageEXT = 0;
+static qt_glXReleaseTexImageEXT glXReleaseTexImageEXT = 0;
+
+static bool qt_resolveTextureFromPixmap(QPaintDevice *paintDevice)
+{
+ static bool resolvedTextureFromPixmap = false;
+
+ if (!resolvedTextureFromPixmap) {
+ resolvedTextureFromPixmap = true;
+
+ // Check to see if we have NPOT texture support
+ if ( !(QGLExtensions::glExtensions() & QGLExtensions::NPOTTextures) &&
+ !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0))
+ {
+ return false; // Can't use TFP without NPOT
+ }
+
+ const QX11Info *xinfo = qt_x11Info(paintDevice);
+ Display *display = xinfo ? xinfo->display() : X11->display;
+ int screen = xinfo ? xinfo->screen() : X11->defaultScreen;
+
+ QGLExtensionMatcher serverExtensions(glXQueryExtensionsString(display, screen));
+ QGLExtensionMatcher clientExtensions(glXGetClientString(display, GLX_EXTENSIONS));
+ if (serverExtensions.match("GLX_EXT_texture_from_pixmap")
+ && clientExtensions.match("GLX_EXT_texture_from_pixmap"))
+ {
+ glXBindTexImageEXT = (qt_glXBindTexImageEXT) qglx_getProcAddress("glXBindTexImageEXT");
+ glXReleaseTexImageEXT = (qt_glXReleaseTexImageEXT) qglx_getProcAddress("glXReleaseTexImageEXT");
+ }
+ }
+
+ return glXBindTexImageEXT && glXReleaseTexImageEXT;
+}
+#endif //defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
+
+
+QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pixmap, const qint64 key,
+ QGLContext::BindOptions options)
+{
+#if !defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX)
+ return 0;
+#else
+
+ // Check we have GLX 1.3, as it is needed for glXCreatePixmap & glXDestroyPixmap
+ int majorVersion = 0;
+ int minorVersion = 0;
+ glXQueryVersion(X11->display, &majorVersion, &minorVersion);
+ if (majorVersion < 1 || (majorVersion == 1 && minorVersion < 3))
+ return 0;
+
+ Q_Q(QGLContext);
+
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pixmap->data_ptr().data());
+ Q_ASSERT(pixmapData->classId() == QPixmapData::X11Class);
+
+ // We can't use TFP if the pixmap has a separate X11 mask
+ if (pixmapData->x11_mask)
+ return 0;
+
+ if (!qt_resolveTextureFromPixmap(paintDevice))
+ return 0;
+
+ const QX11Info &x11Info = pixmapData->xinfo;
+
+ // Store the configs (Can be static because configs aren't dependent on current context)
+ static GLXFBConfig glxRGBPixmapConfig = 0;
+ static bool RGBConfigInverted = false;
+ static GLXFBConfig glxRGBAPixmapConfig = 0;
+ static bool RGBAConfigInverted = false;
+
+ bool hasAlpha = pixmapData->hasAlphaChannel();
+
+ // Check to see if we need a config
+ if ( (hasAlpha && !glxRGBAPixmapConfig) || (!hasAlpha && !glxRGBPixmapConfig) ) {
+ GLXFBConfig *configList = 0;
+ int configCount = 0;
+
+ int configAttribs[] = {
+ hasAlpha ? GLX_BIND_TO_TEXTURE_RGBA_EXT : GLX_BIND_TO_TEXTURE_RGB_EXT, True,
+ GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
+ GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
+ // QGLContext::bindTexture() can't return an inverted texture, but QPainter::drawPixmap() can:
+ GLX_Y_INVERTED_EXT, options & QGLContext::CanFlipNativePixmapBindOption ? GLX_DONT_CARE : False,
+ XNone
+ };
+ configList = glXChooseFBConfig(x11Info.display(), x11Info.screen(), configAttribs, &configCount);
+ if (!configList)
+ return 0;
+
+ int yInv;
+ glXGetFBConfigAttrib(x11Info.display(), configList[0], GLX_Y_INVERTED_EXT, &yInv);
+
+ if (hasAlpha) {
+ glxRGBAPixmapConfig = configList[0];
+ RGBAConfigInverted = yInv;
+ }
+ else {
+ glxRGBPixmapConfig = configList[0];
+ RGBConfigInverted = yInv;
+ }
+
+ XFree(configList);
+ }
+
+ // Check to see if the surface is still valid
+ if (pixmapData->gl_surface &&
+ hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
+ {
+ // Surface is invalid!
+ destroyGlSurfaceForPixmap(pixmapData);
+ }
+
+ // Check to see if we need a surface
+ if (!pixmapData->gl_surface) {
+ GLXPixmap glxPixmap;
+ int pixmapAttribs[] = {
+ GLX_TEXTURE_FORMAT_EXT, hasAlpha ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT,
+ GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
+ GLX_MIPMAP_TEXTURE_EXT, False, // Maybe needs to be don't care
+ XNone
+ };
+
+ // Wrap the X Pixmap into a GLXPixmap:
+ glxPixmap = glXCreatePixmap(x11Info.display(),
+ hasAlpha ? glxRGBAPixmapConfig : glxRGBPixmapConfig,
+ pixmapData->handle(), pixmapAttribs);
+
+ if (!glxPixmap)
+ return 0;
+
+ pixmapData->gl_surface = (void*)glxPixmap;
+
+ // Make sure the cleanup hook gets called so we can delete the glx pixmap
+ QImagePixmapCleanupHooks::enableCleanupHooks(pixmapData);
+ }
+
+ GLuint textureId;
+ glGenTextures(1, &textureId);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+ glXBindTexImageEXT(x11Info.display(), (GLXPixmap)pixmapData->gl_surface, GLX_FRONT_LEFT_EXT, 0);
+
+ glBindTexture(GL_TEXTURE_2D, textureId);
+ GLuint filtering = (options & QGLContext::LinearFilteringBindOption) ? GL_LINEAR : GL_NEAREST;
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+
+ if (!((hasAlpha && RGBAConfigInverted) || (!hasAlpha && RGBConfigInverted)))
+ options &= ~QGLContext::InvertedYBindOption;
+
+ QGLTexture *texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options);
+ if (texture->options & QGLContext::InvertedYBindOption)
+ pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture;
+
+ // We assume the cost of bound pixmaps is zero
+ QGLTextureCache::instance()->insert(q, key, texture, 0);
+
+ return texture;
+#endif //!defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX)
+}
+
+
+void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd)
+{
+#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
+ Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
+ if (pixmapData->gl_surface) {
+ glXDestroyPixmap(QX11Info::display(), (GLXPixmap)pixmapData->gl_surface);
+ pixmapData->gl_surface = 0;
+ }
+#endif
+}
+
+void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd)
+{
+#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
+ Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
+ Q_ASSERT(QGLContext::currentContext());
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
+ if (pixmapData->gl_surface)
+ glXReleaseTexImageEXT(QX11Info::display(), (GLXPixmap)pixmapData->gl_surface, GLX_FRONT_LEFT_EXT);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp
new file mode 100644
index 0000000000..2ddfd35f8d
--- /dev/null
+++ b/src/opengl/qgl_x11egl.cpp
@@ -0,0 +1,548 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgl.h"
+#include <private/qt_x11_p.h>
+#include <private/qpixmap_x11_p.h>
+#include <private/qgl_p.h>
+#include <private/qpaintengine_opengl_p.h>
+#include "qgl_egl_p.h"
+#include "qcolormap.h"
+#include <QDebug>
+#include <QPixmap>
+
+
+QT_BEGIN_NAMESPACE
+
+
+/*
+ QGLTemporaryContext implementation
+*/
+
+class QGLTemporaryContextPrivate
+{
+public:
+ bool initialized;
+ Window window;
+ EGLContext context;
+ EGLSurface surface;
+ EGLDisplay display;
+};
+
+QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
+ : d(new QGLTemporaryContextPrivate)
+{
+ d->initialized = false;
+ d->window = 0;
+ d->context = 0;
+ d->surface = 0;
+ int screen = 0;
+
+ d->display = QEgl::display();
+
+ EGLConfig config;
+ int numConfigs = 0;
+ EGLint attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+#ifdef QT_OPENGL_ES_2
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+#endif
+ EGL_NONE
+ };
+
+ eglChooseConfig(d->display, attribs, &config, 1, &numConfigs);
+ if (!numConfigs) {
+ qWarning("QGLTemporaryContext: No EGL configurations available.");
+ return;
+ }
+
+ XVisualInfo visualInfo;
+ XVisualInfo *vi;
+ int numVisuals;
+
+ visualInfo.visualid = QEgl::getCompatibleVisualId(config);
+ vi = XGetVisualInfo(X11->display, VisualIDMask, &visualInfo, &numVisuals);
+ if (!vi || numVisuals < 1) {
+ qWarning("QGLTemporaryContext: Unable to get X11 visual info id.");
+ return;
+ }
+
+ XSetWindowAttributes attr;
+ unsigned long mask;
+ attr.background_pixel = 0;
+ attr.border_pixel = 0;
+ attr.colormap = XCreateColormap(X11->display, DefaultRootWindow(X11->display), vi->visual, AllocNone);
+ attr.event_mask = StructureNotifyMask | ExposureMask;
+ mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
+
+ d->window = XCreateWindow(X11->display, RootWindow(X11->display, screen),
+ 0, 0, 1, 1, 0,
+ vi->depth, InputOutput, vi->visual,
+ mask, &attr);
+
+ d->surface = eglCreateWindowSurface(d->display, config, (EGLNativeWindowType) d->window, NULL);
+
+ if (d->surface == EGL_NO_SURFACE) {
+ qWarning("QGLTemporaryContext: Error creating EGL surface.");
+ XFree(vi);
+ XDestroyWindow(X11->display, d->window);
+ return;
+ }
+
+ EGLint contextAttribs[] = {
+#ifdef QT_OPENGL_ES_2
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+#endif
+ EGL_NONE
+ };
+ d->context = eglCreateContext(d->display, config, 0, contextAttribs);
+ if (d->context != EGL_NO_CONTEXT
+ && eglMakeCurrent(d->display, d->surface, d->surface, d->context))
+ {
+ d->initialized = true;
+ } else {
+ qWarning("QGLTemporaryContext: Error creating EGL context.");
+ eglDestroySurface(d->display, d->surface);
+ XDestroyWindow(X11->display, d->window);
+ }
+ XFree(vi);
+}
+
+QGLTemporaryContext::~QGLTemporaryContext()
+{
+ if (d->initialized) {
+ eglMakeCurrent(d->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(d->display, d->context);
+ eglDestroySurface(d->display, d->surface);
+ XDestroyWindow(X11->display, d->window);
+ }
+}
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ return false;
+}
+
+// Chooses the EGL config and creates the EGL context
+bool QGLContext::chooseContext(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+
+ if (!device())
+ return false;
+
+ int devType = device()->devType();
+
+ QX11PixmapData *x11PixmapData = 0;
+ if (devType == QInternal::Pixmap) {
+ QPixmapData *pmd = static_cast<QPixmap*>(device())->data_ptr().data();
+ if (pmd->classId() == QPixmapData::X11Class)
+ x11PixmapData = static_cast<QX11PixmapData*>(pmd);
+ else {
+ // TODO: Replace the pixmap's data with a new QX11PixmapData
+ qWarning("WARNING: Creating a QGLContext on a QPixmap is only supported for X11 pixmap backend");
+ return false;
+ }
+ } else if ((devType != QInternal::Widget) && (devType != QInternal::Pbuffer)) {
+ qWarning("WARNING: Creating a QGLContext not supported on device type %d", devType);
+ return false;
+ }
+
+ // Only create the eglContext if we don't already have one:
+ if (d->eglContext == 0) {
+ d->eglContext = new QEglContext();
+ d->ownsEglContext = true;
+ d->eglContext->setApi(QEgl::OpenGL);
+
+ // If the device is a widget with WA_TranslucentBackground set, make sure the glFormat
+ // has the alpha channel option set:
+ if (devType == QInternal::Widget) {
+ QWidget* widget = static_cast<QWidget*>(device());
+ if (widget->testAttribute(Qt::WA_TranslucentBackground))
+ d->glFormat.setAlpha(true);
+ }
+
+ // Construct the configuration we need for this surface.
+ QEglProperties configProps;
+ configProps.setDeviceType(devType);
+ configProps.setRenderableType(QEgl::OpenGL);
+ qt_eglproperties_set_glformat(configProps, d->glFormat);
+
+ // Set buffer preserved for regular QWidgets, QGLWidgets are ok with either preserved or destroyed:
+ if ((devType == QInternal::Widget) && qobject_cast<QGLWidget*>(static_cast<QWidget*>(device())) == 0)
+ configProps.setValue(EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
+
+ if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ // Create a new context for the configuration.
+ QEglContext* eglSharedContext = shareContext ? shareContext->d_func()->eglContext : 0;
+ if (!d->eglContext->createContext(eglSharedContext)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+ d->sharing = d->eglContext->isSharing();
+ if (d->sharing && shareContext)
+ const_cast<QGLContext *>(shareContext)->d_func()->sharing = true;
+ }
+
+ // Inform the higher layers about the actual format properties
+ qt_glformat_from_eglconfig(d->glFormat, d->eglContext->config());
+
+ // Do don't create the EGLSurface for everything.
+ // QWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface
+ // QGLWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface
+ // QPixmap - yes, create the EGLSurface but store it in QX11PixmapData::gl_surface
+ // QGLPixelBuffer - no, it creates the surface itself and stores it in QGLPixelBufferPrivate::pbuf
+
+ if (devType == QInternal::Widget) {
+ if (d->eglSurface != EGL_NO_SURFACE)
+ eglDestroySurface(d->eglContext->display(), d->eglSurface);
+ // extraWindowSurfaceCreationProps default to NULL unless were specifically set before
+ d->eglSurface = QEgl::createSurface(device(), d->eglContext->config(), d->extraWindowSurfaceCreationProps);
+ XFlush(X11->display);
+ setWindowCreated(true);
+ }
+
+ if (x11PixmapData) {
+ // TODO: Actually check to see if the existing surface can be re-used
+ if (x11PixmapData->gl_surface)
+ eglDestroySurface(d->eglContext->display(), (EGLSurface)x11PixmapData->gl_surface);
+
+ x11PixmapData->gl_surface = (void*)QEgl::createSurface(device(), d->eglContext->config());
+ }
+
+ return true;
+}
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ resizeGL(width(), height());
+ //handle overlay
+}
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return 0;
+}
+
+void QGLWidget::makeOverlayCurrent()
+{
+ //handle overlay
+}
+
+void QGLWidget::updateOverlayGL()
+{
+ //handle overlay
+}
+
+void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+ if (!context->deviceIsPixmap() && context->device() != this) {
+ qWarning("QGLWidget::setContext: Context must refer to this widget");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+
+ bool createFailed = false;
+ if (!d->glcx->isValid()) {
+ // Create the QGLContext here, which in turn chooses the EGL config
+ // and creates the EGL context:
+ if (!d->glcx->create(shareContext ? shareContext : oldcx))
+ createFailed = true;
+ }
+ if (createFailed) {
+ if (deleteOldContext)
+ delete oldcx;
+ return;
+ }
+
+
+ d->eglSurfaceWindowId = winId(); // Remember the window id we created the surface for
+}
+
+void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget* shareWidget)
+{
+ Q_Q(QGLWidget);
+
+ initContext(context, shareWidget);
+
+ if (q->isValid() && glcx->format().hasOverlay()) {
+ //no overlay
+ qWarning("QtOpenGL ES doesn't currently support overlays");
+ }
+}
+
+void QGLWidgetPrivate::cleanupColormaps()
+{
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ return d_func()->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap &)
+{
+}
+
+// Re-creates the EGL surface if the window ID has changed or if there isn't a surface
+void QGLWidgetPrivate::recreateEglSurface()
+{
+ Q_Q(QGLWidget);
+
+ Window currentId = q->winId();
+
+ // If the window ID has changed since the surface was created, we need to delete the
+ // old surface before re-creating a new one. Note: This should not be the case as the
+ // surface should be deleted before the old window id.
+ if (glcx->d_func()->eglSurface != EGL_NO_SURFACE && (currentId != eglSurfaceWindowId)) {
+ qWarning("EGL surface for deleted window %lx was not destroyed", uint(eglSurfaceWindowId));
+ glcx->d_func()->destroyEglSurfaceForDevice();
+ }
+
+ if (glcx->d_func()->eglSurface == EGL_NO_SURFACE) {
+ glcx->d_func()->eglSurface = glcx->d_func()->eglContext->createSurface(q);
+ eglSurfaceWindowId = currentId;
+ }
+}
+
+
+QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pixmap, const qint64 key,
+ QGLContext::BindOptions options)
+{
+ Q_Q(QGLContext);
+
+ // The EGL texture_from_pixmap has no facility to invert the y coordinate
+ if (!(options & QGLContext::CanFlipNativePixmapBindOption))
+ return 0;
+
+
+ static bool checkedForTFP = false;
+ static bool haveTFP = false;
+ static bool checkedForEglImageTFP = false;
+ static bool haveEglImageTFP = false;
+
+
+ if (!checkedForEglImageTFP) {
+ checkedForEglImageTFP = true;
+
+ // We need to be able to create an EGLImage from a native pixmap, which was split
+ // into a separate EGL extension, EGL_KHR_image_pixmap. It is possible to have
+ // eglCreateImageKHR & eglDestroyImageKHR without support for pixmaps, so we must
+ // check we have the EGLImage from pixmap functionality.
+ if (QEgl::hasExtension("EGL_KHR_image") || QEgl::hasExtension("EGL_KHR_image_pixmap")) {
+
+ // Being able to create an EGLImage from a native pixmap is also pretty useless
+ // without the ability to bind that EGLImage as a texture, which is provided by
+ // the GL_OES_EGL_image extension, which we try to resolve here:
+ haveEglImageTFP = qt_resolve_eglimage_gl_extensions(q);
+
+ if (haveEglImageTFP)
+ qDebug("Found EGL_KHR_image_pixmap & GL_OES_EGL_image extensions (preferred method)!");
+ }
+ }
+
+ if (!checkedForTFP) {
+ // Check for texture_from_pixmap egl extension
+ checkedForTFP = true;
+ if (QEgl::hasExtension("EGL_NOKIA_texture_from_pixmap") ||
+ QEgl::hasExtension("EGL_EXT_texture_from_pixmap"))
+ {
+ qDebug("Found texture_from_pixmap EGL extension!");
+ haveTFP = true;
+ }
+ }
+
+ if (!haveTFP && !haveEglImageTFP)
+ return 0;
+
+
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pixmap->data_ptr().data());
+ Q_ASSERT(pixmapData->classId() == QPixmapData::X11Class);
+ bool hasAlpha = pixmapData->hasAlphaChannel();
+ bool pixmapHasValidSurface = false;
+ bool textureIsBound = false;
+ GLuint textureId;
+ glGenTextures(1, &textureId);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+
+ if (haveTFP && pixmapData->gl_surface &&
+ hasAlpha == (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
+ {
+ pixmapHasValidSurface = true;
+ }
+
+ // If we already have a valid EGL surface for the pixmap, we should use it
+ if (pixmapHasValidSurface) {
+ EGLBoolean success;
+ success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER);
+ if (success == EGL_FALSE) {
+ qWarning() << "eglBindTexImage() failed:" << QEgl::errorString();
+ eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
+ pixmapData->gl_surface = (void*)EGL_NO_SURFACE;
+ } else
+ textureIsBound = true;
+ }
+
+ // If the pixmap doesn't already have a valid surface, try binding it via EGLImage
+ // first, as going through EGLImage should be faster and better supported:
+ if (!textureIsBound && haveEglImageTFP) {
+ EGLImageKHR eglImage;
+
+ EGLint attribs[] = {
+ EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
+ EGL_NONE
+ };
+ eglImage = QEgl::eglCreateImageKHR(QEgl::display(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
+ (EGLClientBuffer)QEgl::nativePixmap(pixmap), attribs);
+
+ QGLContext* ctx = q;
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage);
+
+ GLint err = glGetError();
+ if (err == GL_NO_ERROR)
+ textureIsBound = true;
+
+ // Once the egl image is bound, the texture becomes a new sibling image and we can safely
+ // destroy the EGLImage we created for the pixmap:
+ if (eglImage != EGL_NO_IMAGE_KHR)
+ QEgl::eglDestroyImageKHR(QEgl::display(), eglImage);
+ }
+
+ if (!textureIsBound && haveTFP) {
+ // Check to see if the surface is still valid
+ if (pixmapData->gl_surface &&
+ hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
+ {
+ // Surface is invalid!
+ destroyGlSurfaceForPixmap(pixmapData);
+ }
+
+ if (pixmapData->gl_surface == 0) {
+ EGLConfig config = QEgl::defaultConfig(QInternal::Pixmap,
+ QEgl::OpenGL,
+ hasAlpha ? QEgl::Translucent : QEgl::NoOptions);
+
+ pixmapData->gl_surface = (void*)QEgl::createSurface(pixmap, config);
+ if (pixmapData->gl_surface == (void*)EGL_NO_SURFACE)
+ return false;
+ }
+
+ EGLBoolean success;
+ success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER);
+ if (success == EGL_FALSE) {
+ qWarning() << "eglBindTexImage() failed:" << QEgl::errorString();
+ eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
+ pixmapData->gl_surface = (void*)EGL_NO_SURFACE;
+ haveTFP = false; // If TFP isn't working, disable it's use
+ } else
+ textureIsBound = true;
+ }
+
+ QGLTexture *texture = 0;
+
+ if (textureIsBound) {
+ texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options);
+ pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture;
+
+ // We assume the cost of bound pixmaps is zero
+ QGLTextureCache::instance()->insert(q, key, texture, 0);
+
+ glBindTexture(GL_TEXTURE_2D, textureId);
+ } else
+ glDeleteTextures(1, &textureId);
+
+ return texture;
+}
+
+
+void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd)
+{
+ Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
+ if (pixmapData->gl_surface) {
+ EGLBoolean success;
+ success = eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
+ if (success == EGL_FALSE) {
+ qWarning() << "destroyGlSurfaceForPixmap() - Error deleting surface: "
+ << QEgl::errorString();
+ }
+ pixmapData->gl_surface = 0;
+ }
+}
+
+void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd)
+{
+ Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
+ if (pixmapData->gl_surface) {
+ EGLBoolean success;
+ success = eglReleaseTexImage(QEgl::display(),
+ (EGLSurface)pixmapData->gl_surface,
+ EGL_BACK_BUFFER);
+ if (success == EGL_FALSE) {
+ qWarning() << "unbindPixmapFromTexture() - Unable to release bound texture: "
+ << QEgl::errorString();
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglbuffer.cpp b/src/opengl/qglbuffer.cpp
new file mode 100644
index 0000000000..d96f75bae2
--- /dev/null
+++ b/src/opengl/qglbuffer.cpp
@@ -0,0 +1,576 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtOpenGL/qgl.h>
+#include <QtOpenGL/private/qgl_p.h>
+#include <QtOpenGL/private/qglextensions_p.h>
+#include <QtCore/qatomic.h>
+#include "qglbuffer.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLBuffer
+ \brief The QGLBuffer 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.
+
+ QGLBuffer objects can be copied around as a reference to the
+ underlying GL buffer object:
+
+ \code
+ QGLBuffer buffer1(QGLBuffer::IndexBuffer);
+ buffer1.create();
+
+ QGLBuffer buffer2 = buffer1;
+ \endcode
+
+ QGLBuffer 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 QGLBuffer::Type
+ This enum defines the type of GL buffer object to create with QGLBuffer.
+
+ \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 QGLBuffer::UsagePattern
+ This enum defines the usage pattern of a QGLBuffer 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 QGLBuffer::Access
+ This enum defines the access mode for QGLBuffer::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 QGLBufferPrivate
+{
+public:
+ QGLBufferPrivate(QGLBuffer::Type t)
+ : ref(1),
+ type(t),
+ guard(0),
+ usagePattern(QGLBuffer::StaticDraw),
+ actualUsagePattern(QGLBuffer::StaticDraw)
+ {
+ }
+
+ QAtomicInt ref;
+ QGLBuffer::Type type;
+ QGLSharedResourceGuard guard;
+ QGLBuffer::UsagePattern usagePattern;
+ QGLBuffer::UsagePattern actualUsagePattern;
+};
+
+/*!
+ Constructs a new buffer object of type QGLBuffer::VertexBuffer.
+
+ Note: this constructor just creates the QGLBuffer instance. The actual
+ buffer object in the GL server is not created until create() is called.
+
+ \sa create()
+*/
+QGLBuffer::QGLBuffer()
+ : d_ptr(new QGLBufferPrivate(QGLBuffer::VertexBuffer))
+{
+}
+
+/*!
+ Constructs a new buffer object of \a type.
+
+ Note: this constructor just creates the QGLBuffer instance. The actual
+ buffer object in the GL server is not created until create() is called.
+
+ \sa create()
+*/
+QGLBuffer::QGLBuffer(QGLBuffer::Type type)
+ : d_ptr(new QGLBufferPrivate(type))
+{
+}
+
+/*!
+ Constructs a shallow copy of \a other.
+
+ Note: QGLBuffer does not implement copy-on-write semantics,
+ so \a other will be affected whenever the copy is modified.
+*/
+QGLBuffer::QGLBuffer(const QGLBuffer &other)
+ : d_ptr(other.d_ptr)
+{
+ d_ptr->ref.ref();
+}
+
+#define ctx d->guard.context()
+
+/*!
+ Destroys this buffer object, including the storage being
+ used in the GL server.
+*/
+QGLBuffer::~QGLBuffer()
+{
+ if (!d_ptr->ref.deref()) {
+ destroy();
+ delete d_ptr;
+ }
+}
+
+/*!
+ Assigns a shallow copy of \a other to this object.
+
+ Note: QGLBuffer does not implement copy-on-write semantics,
+ so \a other will be affected whenever the copy is modified.
+*/
+QGLBuffer &QGLBuffer::operator=(const QGLBuffer &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.
+*/
+QGLBuffer::Type QGLBuffer::type() const
+{
+ Q_D(const QGLBuffer);
+ return d->type;
+}
+
+/*!
+ Returns the usage pattern for this buffer object.
+ The default value is StaticDraw.
+
+ \sa setUsagePattern()
+*/
+QGLBuffer::UsagePattern QGLBuffer::usagePattern() const
+{
+ Q_D(const QGLBuffer);
+ 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 QGLBuffer::setUsagePattern(QGLBuffer::UsagePattern value)
+{
+ Q_D(QGLBuffer);
+#if defined(QT_OPENGL_ES_1)
+ // OpenGL/ES 1.1 does not support GL_STREAM_DRAW, so use GL_STATIC_DRAW.
+ // OpenGL/ES 2.0 does support GL_STREAM_DRAW.
+ d->usagePattern = value;
+ if (value == StreamDraw)
+ d->actualUsagePattern = StaticDraw;
+ else
+ d->actualUsagePattern = value;
+#else
+ d->usagePattern = d->actualUsagePattern = value;
+#endif
+}
+
+#undef ctx
+
+/*!
+ 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 QGLContext.
+ 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 QGLContext.
+
+ \sa isCreated(), allocate(), write(), destroy()
+*/
+bool QGLBuffer::create()
+{
+ Q_D(QGLBuffer);
+ if (d->guard.id())
+ return true;
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (ctx) {
+ if (!qt_resolve_buffer_extensions(const_cast<QGLContext *>(ctx)))
+ return false;
+ GLuint bufferId = 0;
+ glGenBuffers(1, &bufferId);
+ if (bufferId) {
+ d->guard.setContext(ctx);
+ d->guard.setId(bufferId);
+ return true;
+ }
+ }
+ return false;
+}
+
+#define ctx d->guard.context()
+
+/*!
+ Returns true if this buffer has been created; false otherwise.
+
+ \sa create(), destroy()
+*/
+bool QGLBuffer::isCreated() const
+{
+ Q_D(const QGLBuffer);
+ return d->guard.id() != 0;
+}
+
+/*!
+ Destroys this buffer object, including the storage being
+ used in the GL server. All references to the buffer will
+ become invalid.
+*/
+void QGLBuffer::destroy()
+{
+ Q_D(QGLBuffer);
+ GLuint bufferId = d->guard.id();
+ if (bufferId) {
+ // Switch to the original creating context to destroy it.
+ QGLShareContextScope scope(d->guard.context());
+ glDeleteBuffers(1, &bufferId);
+ }
+ d->guard.setId(0);
+ d->guard.setContext(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 QGLBuffer::read(int offset, void *data, int count)
+{
+#if !defined(QT_OPENGL_ES)
+ Q_D(QGLBuffer);
+ if (!glGetBufferSubData || !d->guard.id())
+ return false;
+ while (glGetError() != GL_NO_ERROR) ; // Clear error state.
+ 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 QGLBuffer::write(int offset, const void *data, int count)
+{
+#ifndef QT_NO_DEBUG
+ if (!isCreated())
+ qWarning("QGLBuffer::allocate(): buffer not created");
+#endif
+ Q_D(QGLBuffer);
+ if (d->guard.id())
+ 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 QGLBuffer::allocate(const void *data, int count)
+{
+#ifndef QT_NO_DEBUG
+ if (!isCreated())
+ qWarning("QGLBuffer::allocate(): buffer not created");
+#endif
+ Q_D(QGLBuffer);
+ if (d->guard.id())
+ glBufferData(d->type, count, data, d->actualUsagePattern);
+}
+
+/*!
+ \fn void QGLBuffer::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 QGLContext current when create()
+ was called, or to another QGLContext that is sharing with it.
+ Otherwise, false will be returned from this function.
+
+ \sa release(), create()
+*/
+bool QGLBuffer::bind()
+{
+#ifndef QT_NO_DEBUG
+ if (!isCreated())
+ qWarning("QGLBuffer::bind(): buffer not created");
+#endif
+ Q_D(const QGLBuffer);
+ GLuint bufferId = d->guard.id();
+ if (bufferId) {
+ if (!QGLContext::areSharing(QGLContext::currentContext(),
+ d->guard.context())) {
+#ifndef QT_NO_DEBUG
+ qWarning("QGLBuffer::bind: buffer is not valid in the current context");
+#endif
+ return false;
+ }
+ 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 QGLContext current
+ as when bind() was called on the buffer.
+
+ \sa bind()
+*/
+void QGLBuffer::release()
+{
+#ifndef QT_NO_DEBUG
+ if (!isCreated())
+ qWarning("QGLBuffer::release(): buffer not created");
+#endif
+ Q_D(const QGLBuffer);
+ if (d->guard.id())
+ glBindBuffer(d->type, 0);
+}
+
+#undef ctx
+
+/*!
+ Releases the buffer associated with \a type in the current
+ QGLContext.
+
+ This function is a direct call to \c{glBindBuffer(type, 0)}
+ for use when the caller does not know which QGLBuffer has
+ been bound to the context but wants to make sure that it
+ is released.
+
+ \code
+ QGLBuffer::release(QGLBuffer::VertexBuffer);
+ \endcode
+*/
+void QGLBuffer::release(QGLBuffer::Type type)
+{
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (ctx && qt_resolve_buffer_extensions(const_cast<QGLContext *>(ctx)))
+ glBindBuffer(GLenum(type), 0);
+}
+
+#define ctx d->guard.context()
+
+/*!
+ Returns the GL identifier associated with this buffer; zero if
+ the buffer has not been created.
+
+ \sa isCreated()
+*/
+GLuint QGLBuffer::bufferId() const
+{
+ Q_D(const QGLBuffer);
+ return d->guard.id();
+}
+
+#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 QGLBuffer::size() const
+{
+ Q_D(const QGLBuffer);
+ if (!d->guard.id())
+ return -1;
+ GLint value = -1;
+ 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 *QGLBuffer::map(QGLBuffer::Access access)
+{
+ Q_D(QGLBuffer);
+#ifndef QT_NO_DEBUG
+ if (!isCreated())
+ qWarning("QGLBuffer::map(): buffer not created");
+#endif
+ if (!d->guard.id())
+ return 0;
+ if (!glMapBufferARB)
+ return 0;
+ return glMapBufferARB(d->type, access);
+}
+
+/*!
+ 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 QGLBuffer::unmap()
+{
+ Q_D(QGLBuffer);
+#ifndef QT_NO_DEBUG
+ if (!isCreated())
+ qWarning("QGLBuffer::unmap(): buffer not created");
+#endif
+ if (!d->guard.id())
+ return false;
+ if (!glUnmapBufferARB)
+ return false;
+ return glUnmapBufferARB(d->type) == GL_TRUE;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglbuffer.h b/src/opengl/qglbuffer.h
new file mode 100644
index 0000000000..97ea589053
--- /dev/null
+++ b/src/opengl/qglbuffer.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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLBUFFER_H
+#define QGLBUFFER_H
+
+#include <QtCore/qscopedpointer.h>
+#include <QtOpenGL/qgl.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLBufferPrivate;
+
+class Q_OPENGL_EXPORT QGLBuffer
+{
+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
+ };
+
+ QGLBuffer();
+ explicit QGLBuffer(QGLBuffer::Type type);
+ QGLBuffer(const QGLBuffer &other);
+ ~QGLBuffer();
+
+ QGLBuffer &operator=(const QGLBuffer &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
+ };
+
+ QGLBuffer::Type type() const;
+
+ QGLBuffer::UsagePattern usagePattern() const;
+ void setUsagePattern(QGLBuffer::UsagePattern value);
+
+ bool create();
+ bool isCreated() const;
+
+ void destroy();
+
+ bool bind();
+ void release();
+
+ static void release(QGLBuffer::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(QGLBuffer::Access access);
+ bool unmap();
+
+private:
+ QGLBufferPrivate *d_ptr;
+
+ Q_DECLARE_PRIVATE(QGLBuffer)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/opengl/qglcolormap.cpp b/src/opengl/qglcolormap.cpp
new file mode 100644
index 0000000000..facc5ba938
--- /dev/null
+++ b/src/opengl/qglcolormap.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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGLColormap
+ \brief The QGLColormap class is used for installing custom colormaps into
+ a QGLWidget.
+
+ \module OpenGL
+ \ingroup painting-3D
+ \ingroup shared
+
+ QGLColormap provides a platform independent way of specifying and
+ installing indexed colormaps for a QGLWidget. QGLColormap 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_qglcolormap.cpp 0
+
+ \sa QGLWidget::setColormap(), QGLWidget::colormap()
+*/
+
+/*!
+ \fn Qt::HANDLE QGLColormap::handle()
+
+ \internal
+
+ Returns the handle for this color map.
+*/
+
+/*!
+ \fn void QGLColormap::setHandle(Qt::HANDLE handle)
+
+ \internal
+
+ Sets the handle for this color map to \a handle.
+*/
+
+#include "qglcolormap.h"
+
+QT_BEGIN_NAMESPACE
+
+QGLColormap::QGLColormapData QGLColormap::shared_null = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0 };
+
+/*!
+ Construct a QGLColormap.
+*/
+QGLColormap::QGLColormap()
+ : d(&shared_null)
+{
+ d->ref.ref();
+}
+
+
+/*!
+ Construct a shallow copy of \a map.
+*/
+QGLColormap::QGLColormap(const QGLColormap &map)
+ : d(map.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ Dereferences the QGLColormap and deletes it if this was the last
+ reference to it.
+*/
+QGLColormap::~QGLColormap()
+{
+ if (!d->ref.deref())
+ cleanup(d);
+}
+
+void QGLColormap::cleanup(QGLColormap::QGLColormapData *x)
+{
+ delete x->cells;
+ x->cells = 0;
+ delete x;
+}
+
+/*!
+ Assign a shallow copy of \a map to this QGLColormap.
+*/
+QGLColormap & QGLColormap::operator=(const QGLColormap &map)
+{
+ map.d->ref.ref();
+ if (!d->ref.deref())
+ cleanup(d);
+ d = map.d;
+ return *this;
+}
+
+/*!
+ \fn void QGLColormap::detach()
+ \internal
+
+ Detaches this QGLColormap from the shared block.
+*/
+
+void QGLColormap::detach_helper()
+{
+ QGLColormapData *x = new QGLColormapData;
+ 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 QGLColormap::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 QGLColormap::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(), "QGLColormap::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 QGLColormap::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 QGLColormap::setEntry(int idx, const QColor &color)
+{
+ setEntry(idx, color.rgb());
+}
+
+/*!
+ Returns the QRgb value in the colorcell with index \a idx.
+*/
+QColor QGLColormap::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 QGLWidget; 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 QGLWidget is also considered empty.
+
+ Compare size() with zero to determine if the colormap is empty
+ regardless of whether it is in use by a QGLWidget or not.
+
+ \sa size()
+*/
+bool QGLColormap::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 QGLColormap::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 QGLColormap::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 QGLColormap::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/opengl/qglcolormap.h b/src/opengl/qglcolormap.h
new file mode 100644
index 0000000000..9a4038ef1a
--- /dev/null
+++ b/src/opengl/qglcolormap.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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLCOLORMAP_H
+#define QGLCOLORMAP_H
+
+#include <QtGui/qcolor.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class Q_OPENGL_EXPORT QGLColormap
+{
+public:
+ QGLColormap();
+ QGLColormap(const QGLColormap &);
+ ~QGLColormap();
+
+ QGLColormap &operator=(const QGLColormap &);
+
+ 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 QGLColormapData {
+ QBasicAtomicInt ref;
+ QVector<QRgb> *cells;
+ Qt::HANDLE cmapHandle;
+ };
+
+ QGLColormapData *d;
+ static struct QGLColormapData shared_null;
+ static void cleanup(QGLColormapData *x);
+ void detach_helper();
+
+ friend class QGLWidget;
+ friend class QGLWidgetPrivate;
+};
+
+inline void QGLColormap::detach()
+{
+ if (d->ref != 1)
+ detach_helper();
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGLCOLORMAP_H
diff --git a/src/opengl/qglextensions.cpp b/src/opengl/qglextensions.cpp
new file mode 100644
index 0000000000..731896b8bc
--- /dev/null
+++ b/src/opengl/qglextensions.cpp
@@ -0,0 +1,430 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static void *qt_gl_getProcAddress_search
+ (QGLContext *ctx, const char *name1, const char *name2,
+ const char *name3, const char *name4)
+{
+ void *addr;
+
+ addr = ctx->getProcAddress(QLatin1String(name1));
+ if (addr)
+ return addr;
+
+ addr = ctx->getProcAddress(QLatin1String(name2));
+ if (addr)
+ return addr;
+
+ addr = ctx->getProcAddress(QLatin1String(name3));
+ if (addr)
+ return addr;
+
+ if (name4)
+ return ctx->getProcAddress(QLatin1String(name4));
+
+ return 0;
+}
+
+// Search for an extension function starting with the most likely
+// function suffix first, and then trying the other variations.
+#if defined(QT_OPENGL_ES)
+#define qt_gl_getProcAddress(ctx,name) \
+ qt_gl_getProcAddress_search((ctx), name, name "OES", name "EXT", name "ARB")
+#define qt_gl_getProcAddressEXT(ctx,name) \
+ qt_gl_getProcAddress_search((ctx), name "OES", name, name "EXT", name "ARB")
+#define qt_gl_getProcAddressARB(ctx,name) \
+ qt_gl_getProcAddress_search((ctx), name "OES", name, name "ARB", name "EXT")
+#define qt_gl_getProcAddressOES(ctx,name) \
+ qt_gl_getProcAddress_search((ctx), name "OES", name, name "EXT", name "ARB")
+#else
+#define qt_gl_getProcAddress(ctx,name) \
+ qt_gl_getProcAddress_search((ctx), name, name "ARB", name "EXT", 0)
+#define qt_gl_getProcAddressEXT(ctx,name) \
+ qt_gl_getProcAddress_search((ctx), name "EXT", name, name "ARB", 0)
+#define qt_gl_getProcAddressARB(ctx,name) \
+ qt_gl_getProcAddress_search((ctx), name "ARB", name, name "EXT", 0)
+#define qt_gl_getProcAddressOES(ctx,name) \
+ qt_gl_getProcAddress_search((ctx), name "OES", name, name "EXT", name "ARB")
+#endif
+
+bool qt_resolve_framebufferobject_extensions(QGLContext *ctx)
+{
+#if defined(QT_OPENGL_ES_2)
+ static bool have_resolved = false;
+ if (have_resolved)
+ return true;
+ have_resolved = true;
+#else
+ if (glIsRenderbuffer != 0)
+ return true;
+#endif
+
+ if (ctx == 0) {
+ qWarning("QGLFramebufferObject: Unable to resolve framebuffer object extensions -"
+ " make sure there is a current context when creating the framebuffer object.");
+ return false;
+ }
+
+
+ glBlitFramebufferEXT = (_glBlitFramebufferEXT) qt_gl_getProcAddressEXT(ctx, "glBlitFramebuffer");
+ glRenderbufferStorageMultisampleEXT =
+ (_glRenderbufferStorageMultisampleEXT) qt_gl_getProcAddressEXT(ctx, "glRenderbufferStorageMultisample");
+
+#if !defined(QT_OPENGL_ES_2)
+ glIsRenderbuffer = (_glIsRenderbuffer) qt_gl_getProcAddressEXT(ctx, "glIsRenderbuffer");
+ if (!glIsRenderbuffer)
+ return false; // Not much point searching for anything else.
+ glBindRenderbuffer = (_glBindRenderbuffer) qt_gl_getProcAddressEXT(ctx, "glBindRenderbuffer");
+ glDeleteRenderbuffers = (_glDeleteRenderbuffers) qt_gl_getProcAddressEXT(ctx, "glDeleteRenderbuffers");
+ glGenRenderbuffers = (_glGenRenderbuffers) qt_gl_getProcAddressEXT(ctx, "glGenRenderbuffers");
+ glRenderbufferStorage = (_glRenderbufferStorage) qt_gl_getProcAddressEXT(ctx, "glRenderbufferStorage");
+ glGetRenderbufferParameteriv =
+ (_glGetRenderbufferParameteriv) qt_gl_getProcAddressEXT(ctx, "glGetRenderbufferParameteriv");
+ glIsFramebuffer = (_glIsFramebuffer) qt_gl_getProcAddressEXT(ctx, "glIsFramebuffer");
+ glBindFramebuffer = (_glBindFramebuffer) qt_gl_getProcAddressEXT(ctx, "glBindFramebuffer");
+ glDeleteFramebuffers = (_glDeleteFramebuffers) qt_gl_getProcAddressEXT(ctx, "glDeleteFramebuffers");
+ glGenFramebuffers = (_glGenFramebuffers) qt_gl_getProcAddressEXT(ctx, "glGenFramebuffers");
+ glCheckFramebufferStatus = (_glCheckFramebufferStatus) qt_gl_getProcAddressEXT(ctx, "glCheckFramebufferStatus");
+ glFramebufferTexture2D = (_glFramebufferTexture2D) qt_gl_getProcAddressEXT(ctx, "glFramebufferTexture2D");
+ glFramebufferRenderbuffer = (_glFramebufferRenderbuffer) qt_gl_getProcAddressEXT(ctx, "glFramebufferRenderbuffer");
+ glGetFramebufferAttachmentParameteriv =
+ (_glGetFramebufferAttachmentParameteriv) qt_gl_getProcAddressEXT(ctx, "glGetFramebufferAttachmentParameteriv");
+ glGenerateMipmap = (_glGenerateMipmap) qt_gl_getProcAddressEXT(ctx, "glGenerateMipmap");
+
+ return glIsRenderbuffer != 0;
+#else
+ return true;
+#endif
+}
+
+#if !defined(QT_OPENGL_ES_2)
+bool qt_resolve_version_1_3_functions(QGLContext *ctx)
+{
+ if (glMultiTexCoord4f != 0)
+ return true;
+
+ QGLContext cx(QGLFormat::defaultFormat());
+ glMultiTexCoord4f = (_glMultiTexCoord4f) ctx->getProcAddress(QLatin1String("glMultiTexCoord4f"));
+
+ glActiveTexture = (_glActiveTexture) ctx->getProcAddress(QLatin1String("glActiveTexture"));
+ return glMultiTexCoord4f && glActiveTexture;
+}
+#endif
+
+#if !defined(QT_OPENGL_ES_2)
+bool qt_resolve_stencil_face_extension(QGLContext *ctx)
+{
+ if (glActiveStencilFaceEXT != 0)
+ return true;
+
+ QGLContext cx(QGLFormat::defaultFormat());
+ glActiveStencilFaceEXT = (_glActiveStencilFaceEXT) ctx->getProcAddress(QLatin1String("glActiveStencilFaceEXT"));
+
+ return glActiveStencilFaceEXT;
+}
+#endif
+
+
+#if !defined(QT_OPENGL_ES_2)
+bool qt_resolve_frag_program_extensions(QGLContext *ctx)
+{
+ if (glProgramStringARB != 0)
+ return true;
+
+ // ARB_fragment_program
+ glProgramStringARB = (_glProgramStringARB) ctx->getProcAddress(QLatin1String("glProgramStringARB"));
+ glBindProgramARB = (_glBindProgramARB) ctx->getProcAddress(QLatin1String("glBindProgramARB"));
+ glDeleteProgramsARB = (_glDeleteProgramsARB) ctx->getProcAddress(QLatin1String("glDeleteProgramsARB"));
+ glGenProgramsARB = (_glGenProgramsARB) ctx->getProcAddress(QLatin1String("glGenProgramsARB"));
+ glProgramLocalParameter4fvARB = (_glProgramLocalParameter4fvARB) ctx->getProcAddress(QLatin1String("glProgramLocalParameter4fvARB"));
+
+ return glProgramStringARB
+ && glBindProgramARB
+ && glDeleteProgramsARB
+ && glGenProgramsARB
+ && glProgramLocalParameter4fvARB;
+}
+#endif
+
+
+bool qt_resolve_buffer_extensions(QGLContext *ctx)
+{
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
+ if (glBindBuffer && glDeleteBuffers && glGenBuffers && glBufferData
+ && glBufferSubData && glGetBufferParameteriv)
+ return true;
+#endif
+
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
+ glBindBuffer = (_glBindBuffer) qt_gl_getProcAddressARB(ctx, "glBindBuffer");
+ glDeleteBuffers = (_glDeleteBuffers) qt_gl_getProcAddressARB(ctx, "glDeleteBuffers");
+ glGenBuffers = (_glGenBuffers) qt_gl_getProcAddressARB(ctx, "glGenBuffers");
+ glBufferData = (_glBufferData) qt_gl_getProcAddressARB(ctx, "glBufferData");
+ glBufferSubData = (_glBufferSubData) qt_gl_getProcAddressARB(ctx, "glBufferSubData");
+ glGetBufferSubData = (_glGetBufferSubData) qt_gl_getProcAddressARB(ctx, "glGetBufferSubData");
+ glGetBufferParameteriv = (_glGetBufferParameteriv) qt_gl_getProcAddressARB(ctx, "glGetBufferParameteriv");
+#endif
+ glMapBufferARB = (_glMapBufferARB) qt_gl_getProcAddressARB(ctx, "glMapBuffer");
+ glUnmapBufferARB = (_glUnmapBufferARB) qt_gl_getProcAddressARB(ctx, "glUnmapBuffer");
+
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
+ return glBindBuffer
+ && glDeleteBuffers
+ && glGenBuffers
+ && glBufferData
+ && glBufferSubData
+ && glGetBufferParameteriv;
+ // glGetBufferSubData() is optional
+#else
+ return true;
+#endif
+}
+
+#ifndef QT_NO_EGL
+bool qt_resolve_eglimage_gl_extensions(QGLContext *ctx)
+{
+ if (glEGLImageTargetTexture2DOES || glEGLImageTargetRenderbufferStorageOES)
+ return true;
+ glEGLImageTargetTexture2DOES = (_glEGLImageTargetTexture2DOES) ctx->getProcAddress(QLatin1String("glEGLImageTargetTexture2DOES"));
+ glEGLImageTargetRenderbufferStorageOES = (_glEGLImageTargetRenderbufferStorageOES) ctx->getProcAddress(QLatin1String("glEGLImageTargetRenderbufferStorageOES"));
+ return glEGLImageTargetTexture2DOES && glEGLImageTargetRenderbufferStorageOES;
+}
+#endif
+
+bool qt_resolve_glsl_extensions(QGLContext *ctx)
+{
+
+#if defined(QT_OPENGL_ES_2)
+ // The GLSL shader functions are always present in OpenGL/ES 2.0.
+ // The only exceptions are glGetProgramBinaryOES and glProgramBinaryOES.
+ if (!QGLContextPrivate::extensionFuncs(ctx).qt_glslResolved) {
+ glGetProgramBinaryOES = (_glGetProgramBinaryOES) ctx->getProcAddress(QLatin1String("glGetProgramBinaryOES"));
+ glProgramBinaryOES = (_glProgramBinaryOES) ctx->getProcAddress(QLatin1String("glProgramBinaryOES"));
+ QGLContextPrivate::extensionFuncs(ctx).qt_glslResolved = true;
+ }
+ return true;
+#else
+ if (glCreateShader)
+ return true;
+
+ // Geometry shaders are optional...
+ glProgramParameteriEXT = (_glProgramParameteriEXT) ctx->getProcAddress(QLatin1String("glProgramParameteriEXT"));
+ glFramebufferTextureEXT = (_glFramebufferTextureEXT) ctx->getProcAddress(QLatin1String("glFramebufferTextureEXT"));
+ glFramebufferTextureLayerEXT = (_glFramebufferTextureLayerEXT) ctx->getProcAddress(QLatin1String("glFramebufferTextureLayerEXT"));
+ glFramebufferTextureFaceEXT = (_glFramebufferTextureFaceEXT) ctx->getProcAddress(QLatin1String("glFramebufferTextureFaceEXT"));
+
+ // Must at least have the FragmentShader extension to continue.
+ if (!(QGLExtensions::glExtensions() & QGLExtensions::FragmentShader))
+ return false;
+
+ glCreateShader = (_glCreateShader) ctx->getProcAddress(QLatin1String("glCreateShader"));
+ if (glCreateShader) {
+ glShaderSource = (_glShaderSource) ctx->getProcAddress(QLatin1String("glShaderSource"));
+ glShaderBinary = (_glShaderBinary) ctx->getProcAddress(QLatin1String("glShaderBinary"));
+ glCompileShader = (_glCompileShader) ctx->getProcAddress(QLatin1String("glCompileShader"));
+ glDeleteShader = (_glDeleteShader) ctx->getProcAddress(QLatin1String("glDeleteShader"));
+ glIsShader = (_glIsShader) ctx->getProcAddress(QLatin1String("glIsShader"));
+
+ glCreateProgram = (_glCreateProgram) ctx->getProcAddress(QLatin1String("glCreateProgram"));
+ glAttachShader = (_glAttachShader) ctx->getProcAddress(QLatin1String("glAttachShader"));
+ glDetachShader = (_glDetachShader) ctx->getProcAddress(QLatin1String("glDetachShader"));
+ glLinkProgram = (_glLinkProgram) ctx->getProcAddress(QLatin1String("glLinkProgram"));
+ glUseProgram = (_glUseProgram) ctx->getProcAddress(QLatin1String("glUseProgram"));
+ glDeleteProgram = (_glDeleteProgram) ctx->getProcAddress(QLatin1String("glDeleteProgram"));
+ glIsProgram = (_glIsProgram) ctx->getProcAddress(QLatin1String("glIsProgram"));
+
+ glGetShaderInfoLog = (_glGetShaderInfoLog) ctx->getProcAddress(QLatin1String("glGetShaderInfoLog"));
+ glGetShaderiv = (_glGetShaderiv) ctx->getProcAddress(QLatin1String("glGetShaderiv"));
+ glGetShaderSource = (_glGetShaderSource) ctx->getProcAddress(QLatin1String("glGetShaderSource"));
+ glGetProgramiv = (_glGetProgramiv) ctx->getProcAddress(QLatin1String("glGetProgramiv"));
+ glGetProgramInfoLog = (_glGetProgramInfoLog) ctx->getProcAddress(QLatin1String("glGetProgramInfoLog"));
+
+ glGetUniformLocation = (_glGetUniformLocation) ctx->getProcAddress(QLatin1String("glGetUniformLocation"));
+ glUniform4fv = (_glUniform4fv) ctx->getProcAddress(QLatin1String("glUniform4fv"));
+ glUniform3fv = (_glUniform3fv) ctx->getProcAddress(QLatin1String("glUniform3fv"));
+ glUniform2fv = (_glUniform2fv) ctx->getProcAddress(QLatin1String("glUniform2fv"));
+ glUniform1fv = (_glUniform1fv) ctx->getProcAddress(QLatin1String("glUniform1fv"));
+ glUniform1i = (_glUniform1i) ctx->getProcAddress(QLatin1String("glUniform1i"));
+ glUniform1iv = (_glUniform1iv) ctx->getProcAddress(QLatin1String("glUniform1iv"));
+ glUniformMatrix2fv = (_glUniformMatrix2fv) ctx->getProcAddress(QLatin1String("glUniformMatrix2fv"));
+ glUniformMatrix3fv = (_glUniformMatrix3fv) ctx->getProcAddress(QLatin1String("glUniformMatrix3fv"));
+ glUniformMatrix4fv = (_glUniformMatrix4fv) ctx->getProcAddress(QLatin1String("glUniformMatrix4fv"));
+ glUniformMatrix2x3fv = (_glUniformMatrix2x3fv) ctx->getProcAddress(QLatin1String("glUniformMatrix2x3fv"));
+ glUniformMatrix2x4fv = (_glUniformMatrix2x4fv) ctx->getProcAddress(QLatin1String("glUniformMatrix2x4fv"));
+ glUniformMatrix3x2fv = (_glUniformMatrix3x2fv) ctx->getProcAddress(QLatin1String("glUniformMatrix3x2fv"));
+ glUniformMatrix3x4fv = (_glUniformMatrix3x4fv) ctx->getProcAddress(QLatin1String("glUniformMatrix3x4fv"));
+ glUniformMatrix4x2fv = (_glUniformMatrix4x2fv) ctx->getProcAddress(QLatin1String("glUniformMatrix4x2fv"));
+ glUniformMatrix4x3fv = (_glUniformMatrix4x3fv) ctx->getProcAddress(QLatin1String("glUniformMatrix4x3fv"));
+
+ glBindAttribLocation = (_glBindAttribLocation) ctx->getProcAddress(QLatin1String("glBindAttribLocation"));
+ glGetAttribLocation = (_glGetAttribLocation) ctx->getProcAddress(QLatin1String("glGetAttribLocation"));
+ glVertexAttrib1fv = (_glVertexAttrib1fv) ctx->getProcAddress(QLatin1String("glVertexAttrib1fv"));
+ glVertexAttrib2fv = (_glVertexAttrib2fv) ctx->getProcAddress(QLatin1String("glVertexAttrib2fv"));
+ glVertexAttrib3fv = (_glVertexAttrib3fv) ctx->getProcAddress(QLatin1String("glVertexAttrib3fv"));
+ glVertexAttrib4fv = (_glVertexAttrib4fv) ctx->getProcAddress(QLatin1String("glVertexAttrib4fv"));
+ glVertexAttribPointer = (_glVertexAttribPointer) ctx->getProcAddress(QLatin1String("glVertexAttribPointer"));
+ glDisableVertexAttribArray = (_glDisableVertexAttribArray) ctx->getProcAddress(QLatin1String("glDisableVertexAttribArray"));
+ glEnableVertexAttribArray = (_glEnableVertexAttribArray) ctx->getProcAddress(QLatin1String("glEnableVertexAttribArray"));
+
+ } else {
+ // We may not have the standard shader functions, but we might
+ // have the older ARB functions instead.
+ glCreateShader = (_glCreateShader) ctx->getProcAddress(QLatin1String("glCreateShaderObjectARB"));
+ glShaderSource = (_glShaderSource) ctx->getProcAddress(QLatin1String("glShaderSourceARB"));
+ glShaderBinary = (_glShaderBinary) ctx->getProcAddress(QLatin1String("glShaderBinaryARB"));
+ glCompileShader = (_glCompileShader) ctx->getProcAddress(QLatin1String("glCompileShaderARB"));
+ glDeleteShader = (_glDeleteShader) ctx->getProcAddress(QLatin1String("glDeleteObjectARB"));
+ glIsShader = 0;
+
+ glCreateProgram = (_glCreateProgram) ctx->getProcAddress(QLatin1String("glCreateProgramObjectARB"));
+ glAttachShader = (_glAttachShader) ctx->getProcAddress(QLatin1String("glAttachObjectARB"));
+ glDetachShader = (_glDetachShader) ctx->getProcAddress(QLatin1String("glDetachObjectARB"));
+ glLinkProgram = (_glLinkProgram) ctx->getProcAddress(QLatin1String("glLinkProgramARB"));
+ glUseProgram = (_glUseProgram) ctx->getProcAddress(QLatin1String("glUseProgramObjectARB"));
+ glDeleteProgram = (_glDeleteProgram) ctx->getProcAddress(QLatin1String("glDeleteObjectARB"));
+ glIsProgram = 0;
+
+ glGetShaderInfoLog = (_glGetShaderInfoLog) ctx->getProcAddress(QLatin1String("glGetInfoLogARB"));
+ glGetShaderiv = (_glGetShaderiv) ctx->getProcAddress(QLatin1String("glGetObjectParameterivARB"));
+ glGetShaderSource = (_glGetShaderSource) ctx->getProcAddress(QLatin1String("glGetShaderSourceARB"));
+ glGetProgramiv = (_glGetProgramiv) ctx->getProcAddress(QLatin1String("glGetObjectParameterivARB"));
+ glGetProgramInfoLog = (_glGetProgramInfoLog) ctx->getProcAddress(QLatin1String("glGetInfoLogARB"));
+
+ glGetUniformLocation = (_glGetUniformLocation) ctx->getProcAddress(QLatin1String("glGetUniformLocationARB"));
+ glUniform4fv = (_glUniform4fv) ctx->getProcAddress(QLatin1String("glUniform4fvARB"));
+ glUniform3fv = (_glUniform3fv) ctx->getProcAddress(QLatin1String("glUniform3fvARB"));
+ glUniform2fv = (_glUniform2fv) ctx->getProcAddress(QLatin1String("glUniform2fvARB"));
+ glUniform1fv = (_glUniform1fv) ctx->getProcAddress(QLatin1String("glUniform1fvARB"));
+ glUniform1i = (_glUniform1i) ctx->getProcAddress(QLatin1String("glUniform1iARB"));
+ glUniform1iv = (_glUniform1iv) ctx->getProcAddress(QLatin1String("glUniform1ivARB"));
+ glUniformMatrix2fv = (_glUniformMatrix2fv) ctx->getProcAddress(QLatin1String("glUniformMatrix2fvARB"));
+ glUniformMatrix3fv = (_glUniformMatrix3fv) ctx->getProcAddress(QLatin1String("glUniformMatrix3fvARB"));
+ glUniformMatrix4fv = (_glUniformMatrix4fv) ctx->getProcAddress(QLatin1String("glUniformMatrix4fvARB"));
+ glUniformMatrix2x3fv = (_glUniformMatrix2x3fv) ctx->getProcAddress(QLatin1String("glUniformMatrix2x3fvARB"));
+ glUniformMatrix2x4fv = (_glUniformMatrix2x4fv) ctx->getProcAddress(QLatin1String("glUniformMatrix2x4fvARB"));
+ glUniformMatrix3x2fv = (_glUniformMatrix3x2fv) ctx->getProcAddress(QLatin1String("glUniformMatrix3x2fvARB"));
+ glUniformMatrix3x4fv = (_glUniformMatrix3x4fv) ctx->getProcAddress(QLatin1String("glUniformMatrix3x4fvARB"));
+ glUniformMatrix4x2fv = (_glUniformMatrix4x2fv) ctx->getProcAddress(QLatin1String("glUniformMatrix4x2fvARB"));
+ glUniformMatrix4x3fv = (_glUniformMatrix4x3fv) ctx->getProcAddress(QLatin1String("glUniformMatrix4x3fvARB"));
+
+ glBindAttribLocation = (_glBindAttribLocation) ctx->getProcAddress(QLatin1String("glBindAttribLocationARB"));
+ glGetAttribLocation = (_glGetAttribLocation) ctx->getProcAddress(QLatin1String("glGetAttribLocationARB"));
+ glVertexAttrib1fv = (_glVertexAttrib1fv) ctx->getProcAddress(QLatin1String("glVertexAttrib1fvARB"));
+ glVertexAttrib2fv = (_glVertexAttrib2fv) ctx->getProcAddress(QLatin1String("glVertexAttrib2fvARB"));
+ glVertexAttrib3fv = (_glVertexAttrib3fv) ctx->getProcAddress(QLatin1String("glVertexAttrib3fvARB"));
+ glVertexAttrib4fv = (_glVertexAttrib4fv) ctx->getProcAddress(QLatin1String("glVertexAttrib4fvARB"));
+ glVertexAttribPointer = (_glVertexAttribPointer) ctx->getProcAddress(QLatin1String("glVertexAttribPointerARB"));
+ glDisableVertexAttribArray = (_glDisableVertexAttribArray) ctx->getProcAddress(QLatin1String("glDisableVertexAttribArrayARB"));
+ glEnableVertexAttribArray = (_glEnableVertexAttribArray) ctx->getProcAddress(QLatin1String("glEnableVertexAttribArrayARB"));
+ }
+
+ // Note: glShaderBinary(), glIsShader(), glIsProgram(), and
+ // glUniformMatrixNxMfv() are optional, but all other functions
+ // are required.
+
+ return glCreateShader &&
+ glShaderSource &&
+ glCompileShader &&
+ glDeleteProgram &&
+ glCreateProgram &&
+ glAttachShader &&
+ glDetachShader &&
+ glLinkProgram &&
+ glUseProgram &&
+ glDeleteProgram &&
+ glGetShaderInfoLog &&
+ glGetShaderiv &&
+ glGetShaderSource &&
+ glGetProgramiv &&
+ glGetProgramInfoLog &&
+ glGetUniformLocation &&
+ glUniform1fv &&
+ glUniform2fv &&
+ glUniform3fv &&
+ glUniform4fv &&
+ glUniform1i &&
+ glUniform1iv &&
+ glUniformMatrix2fv &&
+ glUniformMatrix3fv &&
+ glUniformMatrix4fv &&
+ glBindAttribLocation &&
+ glGetAttribLocation &&
+ glVertexAttrib1fv &&
+ glVertexAttrib2fv &&
+ glVertexAttrib3fv &&
+ glVertexAttrib4fv &&
+ glVertexAttribPointer &&
+ glDisableVertexAttribArray &&
+ glEnableVertexAttribArray;
+#endif
+}
+
+#if !defined(QT_OPENGL_ES_2)
+bool qt_resolve_version_2_0_functions(QGLContext *ctx)
+{
+ bool gl2supported = true;
+ if (!qt_resolve_glsl_extensions(ctx))
+ gl2supported = false;
+
+ if (!qt_resolve_version_1_3_functions(ctx))
+ gl2supported = false;
+
+ if (!qt_resolve_framebufferobject_extensions(ctx))
+ gl2supported = false;
+
+ if (glStencilOpSeparate)
+ return gl2supported;
+
+ glBlendColor = (_glBlendColor) ctx->getProcAddress(QLatin1String("glBlendColor"));
+ glStencilOpSeparate = (_glStencilOpSeparate) ctx->getProcAddress(QLatin1String("glStencilOpSeparate"));
+ if (!glBlendColor || !glStencilOpSeparate)
+ gl2supported = false;
+
+ return gl2supported;
+}
+#endif
+
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglextensions_p.h b/src/opengl/qglextensions_p.h
new file mode 100644
index 0000000000..529c7a1b4e
--- /dev/null
+++ b/src/opengl/qglextensions_p.h
@@ -0,0 +1,897 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGL_EXTENSIONS_P_H
+#define QGL_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.
+//
+
+// extension prototypes
+#ifndef Q_WS_MAC
+# ifndef APIENTRYP
+# ifdef APIENTRY
+# define APIENTRYP APIENTRY *
+# else
+# define APIENTRY
+# define APIENTRYP *
+# endif
+# endif
+#else
+# define APIENTRY
+# define APIENTRYP *
+#endif
+
+#ifndef QT_NO_EGL
+// Needed for EGLImageKHR definition:
+#include <QtGui/private/qegl_p.h>
+#endif
+
+#include <QtCore/qglobal.h>
+
+#ifndef GL_ARB_vertex_buffer_object
+typedef ptrdiff_t GLintptrARB;
+typedef ptrdiff_t GLsizeiptrARB;
+#endif
+
+#ifndef GL_VERSION_2_0
+typedef char GLchar;
+#endif
+
+// ARB_vertex_buffer_object
+typedef void (APIENTRY *_glBindBuffer) (GLenum, GLuint);
+typedef void (APIENTRY *_glDeleteBuffers) (GLsizei, const GLuint *);
+typedef void (APIENTRY *_glGenBuffers) (GLsizei, GLuint *);
+typedef void (APIENTRY *_glBufferData) (GLenum, GLsizeiptrARB, const GLvoid *, GLenum);
+typedef void (APIENTRY *_glBufferSubData) (GLenum, GLintptrARB, GLsizeiptrARB, const GLvoid *);
+typedef void (APIENTRY *_glGetBufferSubData) (GLenum, GLintptrARB, GLsizeiptrARB, GLvoid *);
+typedef void (APIENTRY *_glGetBufferParameteriv) (GLenum, GLenum, GLint *);
+typedef GLvoid* (APIENTRY *_glMapBufferARB) (GLenum, GLenum);
+typedef GLboolean (APIENTRY *_glUnmapBufferARB) (GLenum);
+// We can call the buffer functions directly in OpenGL/ES 1.1 or higher,
+// but all other platforms need to resolve the extensions.
+#if defined(QT_OPENGL_ES)
+#if defined(GL_OES_VERSION_1_0) && !defined(GL_OES_VERSION_1_1)
+#define QGL_RESOLVE_BUFFER_FUNCS 1
+#endif
+#else
+#define QGL_RESOLVE_BUFFER_FUNCS 1
+#endif
+
+// ARB_fragment_program
+typedef void (APIENTRY *_glProgramStringARB) (GLenum, GLenum, GLsizei, const GLvoid *);
+typedef void (APIENTRY *_glBindProgramARB) (GLenum, GLuint);
+typedef void (APIENTRY *_glDeleteProgramsARB) (GLsizei, const GLuint *);
+typedef void (APIENTRY *_glGenProgramsARB) (GLsizei, GLuint *);
+typedef void (APIENTRY *_glProgramLocalParameter4fvARB) (GLenum, GLuint, const GLfloat *);
+
+// GLSL
+typedef GLuint (APIENTRY *_glCreateShader) (GLenum);
+typedef void (APIENTRY *_glShaderSource) (GLuint, GLsizei, const char **, const GLint *);
+typedef void (APIENTRY *_glShaderBinary) (GLint, const GLuint*, GLenum, const void*, GLint);
+typedef void (APIENTRY *_glCompileShader) (GLuint);
+typedef void (APIENTRY *_glDeleteShader) (GLuint);
+typedef GLboolean (APIENTRY *_glIsShader) (GLuint);
+
+typedef GLuint (APIENTRY *_glCreateProgram) ();
+typedef void (APIENTRY *_glAttachShader) (GLuint, GLuint);
+typedef void (APIENTRY *_glDetachShader) (GLuint, GLuint);
+typedef void (APIENTRY *_glLinkProgram) (GLuint);
+typedef void (APIENTRY *_glUseProgram) (GLuint);
+typedef void (APIENTRY *_glDeleteProgram) (GLuint);
+typedef GLboolean (APIENTRY *_glIsProgram) (GLuint);
+
+typedef void (APIENTRY *_glGetShaderInfoLog) (GLuint, GLsizei, GLsizei *, char *);
+typedef void (APIENTRY *_glGetShaderiv) (GLuint, GLenum, GLint *);
+typedef void (APIENTRY *_glGetShaderSource) (GLuint, GLsizei, GLsizei *, char *);
+typedef void (APIENTRY *_glGetProgramiv) (GLuint, GLenum, GLint *);
+typedef void (APIENTRY *_glGetProgramInfoLog) (GLuint, GLsizei, GLsizei *, char *);
+
+typedef GLuint (APIENTRY *_glGetUniformLocation) (GLuint, const char*);
+typedef void (APIENTRY *_glUniform4fv) (GLint, GLsizei, const GLfloat *);
+typedef void (APIENTRY *_glUniform3fv) (GLint, GLsizei, const GLfloat *);
+typedef void (APIENTRY *_glUniform2fv) (GLint, GLsizei, const GLfloat *);
+typedef void (APIENTRY *_glUniform1fv) (GLint, GLsizei, const GLfloat *);
+typedef void (APIENTRY *_glUniform1i) (GLint, GLint);
+typedef void (APIENTRY *_glUniform1iv) (GLint, GLsizei, const GLint *);
+typedef void (APIENTRY *_glUniformMatrix2fv) (GLint, GLsizei, GLboolean, const GLfloat *);
+typedef void (APIENTRY *_glUniformMatrix3fv) (GLint, GLsizei, GLboolean, const GLfloat *);
+typedef void (APIENTRY *_glUniformMatrix4fv) (GLint, GLsizei, GLboolean, const GLfloat *);
+typedef void (APIENTRY *_glUniformMatrix2x3fv) (GLint, GLsizei, GLboolean, const GLfloat *);
+typedef void (APIENTRY *_glUniformMatrix2x4fv) (GLint, GLsizei, GLboolean, const GLfloat *);
+typedef void (APIENTRY *_glUniformMatrix3x2fv) (GLint, GLsizei, GLboolean, const GLfloat *);
+typedef void (APIENTRY *_glUniformMatrix3x4fv) (GLint, GLsizei, GLboolean, const GLfloat *);
+typedef void (APIENTRY *_glUniformMatrix4x2fv) (GLint, GLsizei, GLboolean, const GLfloat *);
+typedef void (APIENTRY *_glUniformMatrix4x3fv) (GLint, GLsizei, GLboolean, const GLfloat *);
+
+typedef void (APIENTRY *_glBindAttribLocation) (GLuint, GLuint, const char *);
+typedef GLint (APIENTRY *_glGetAttribLocation) (GLuint, const char *);
+typedef void (APIENTRY *_glVertexAttrib1fv) (GLuint, const GLfloat *);
+typedef void (APIENTRY *_glVertexAttrib2fv) (GLuint, const GLfloat *);
+typedef void (APIENTRY *_glVertexAttrib3fv) (GLuint, const GLfloat *);
+typedef void (APIENTRY *_glVertexAttrib4fv) (GLuint, const GLfloat *);
+typedef void (APIENTRY *_glVertexAttribPointer) (GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid *);
+typedef void (APIENTRY *_glDisableVertexAttribArray) (GLuint);
+typedef void (APIENTRY *_glEnableVertexAttribArray) (GLuint);
+
+typedef void (APIENTRY *_glGetProgramBinaryOES) (GLuint, GLsizei, GLsizei *, GLenum *, void *);
+typedef void (APIENTRY *_glProgramBinaryOES) (GLuint, GLenum, const void *, GLint);
+
+
+typedef void (APIENTRY *_glMultiTexCoord4f) (GLenum, GLfloat, GLfloat, GLfloat, GLfloat);
+typedef void (APIENTRY *_glActiveStencilFaceEXT) (GLenum );
+
+// Needed for GL2 engine:
+typedef void (APIENTRY *_glStencilOpSeparate) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
+typedef void (APIENTRY *_glActiveTexture) (GLenum);
+typedef void (APIENTRY *_glBlendColor) (GLclampf, GLclampf, GLclampf, GLclampf);
+
+
+// EXT_GL_framebuffer_object
+typedef GLboolean (APIENTRY *_glIsRenderbuffer) (GLuint renderbuffer);
+typedef void (APIENTRY *_glBindRenderbuffer) (GLenum target, GLuint renderbuffer);
+typedef void (APIENTRY *_glDeleteRenderbuffers) (GLsizei n, const GLuint *renderbuffers);
+typedef void (APIENTRY *_glGenRenderbuffers) (GLsizei n, GLuint *renderbuffers);
+typedef void (APIENTRY *_glRenderbufferStorage) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (APIENTRY *_glGetRenderbufferParameteriv) (GLenum target, GLenum pname, GLint *params);
+typedef GLboolean (APIENTRY *_glIsFramebuffer) (GLuint framebuffer);
+typedef void (APIENTRY *_glBindFramebuffer) (GLenum target, GLuint framebuffer);
+typedef void (APIENTRY *_glDeleteFramebuffers) (GLsizei n, const GLuint *framebuffers);
+typedef void (APIENTRY *_glGenFramebuffers) (GLsizei n, GLuint *framebuffers);
+typedef GLenum (APIENTRY *_glCheckFramebufferStatus) (GLenum target);
+typedef void (APIENTRY *_glFramebufferTexture2D) (GLenum target, GLenum attachment, GLenum textarget,
+ GLuint texture, GLint level);
+typedef void (APIENTRY *_glFramebufferRenderbuffer) (GLenum target, GLenum attachment, GLenum renderbuffertarget,
+ GLuint renderbuffer);
+typedef void (APIENTRY *_glGetFramebufferAttachmentParameteriv) (GLenum target, GLenum attachment, GLenum pname,
+ GLint *params);
+typedef void (APIENTRY *_glGenerateMipmap) (GLenum target);
+
+// EXT_GL_framebuffer_blit
+typedef void (APIENTRY *_glBlitFramebufferEXT) (int srcX0, int srcY0, int srcX1, int srcY1,
+ int dstX0, int dstY0, int dstX1, int dstY1,
+ GLbitfield mask, GLenum filter);
+
+// EXT_GL_framebuffer_multisample
+typedef void (APIENTRY *_glRenderbufferStorageMultisampleEXT) (GLenum target, GLsizei samples,
+ GLenum internalformat, GLsizei width, GLsizei height);
+
+// GL_EXT_geometry_shader4
+typedef void (APIENTRY *_glProgramParameteriEXT)(GLuint program, GLenum pname, GLint value);
+typedef void (APIENTRY *_glFramebufferTextureEXT)(GLenum target, GLenum attachment,
+ GLuint texture, GLint level);
+typedef void (APIENTRY *_glFramebufferTextureLayerEXT)(GLenum target, GLenum attachment,
+ GLuint texture, GLint level, GLint layer);
+typedef void (APIENTRY *_glFramebufferTextureFaceEXT)(GLenum target, GLenum attachment,
+ GLuint texture, GLint level, GLenum face);
+
+// ARB_texture_compression
+typedef void (APIENTRY *_glCompressedTexImage2DARB) (GLenum, GLint, GLenum, GLsizei,
+ GLsizei, GLint, GLsizei, const GLvoid *);
+
+#ifndef QT_NO_EGL
+// OES_EGL_image
+// Note: We define these to take EGLImage whereas spec says they take a new GLeglImageOES
+// type, which the EGL image should be cast to.
+typedef void (APIENTRY *_glEGLImageTargetTexture2DOES) (GLenum, EGLImageKHR);
+typedef void (APIENTRY *_glEGLImageTargetRenderbufferStorageOES) (GLenum, EGLImageKHR);
+#endif
+
+QT_BEGIN_NAMESPACE
+
+struct QGLExtensionFuncs
+{
+ QGLExtensionFuncs() {
+#if !defined(QT_OPENGL_ES_2)
+ qt_glProgramStringARB = 0;
+ qt_glBindProgramARB = 0;
+ qt_glDeleteProgramsARB = 0;
+ qt_glGenProgramsARB = 0;
+ qt_glProgramLocalParameter4fvARB = 0;
+
+ // GLSL
+ qt_glCreateShader = 0;
+ qt_glShaderSource = 0;
+ qt_glShaderBinary = 0;
+ qt_glCompileShader = 0;
+ qt_glDeleteShader = 0;
+ qt_glIsShader = 0;
+
+ qt_glCreateProgram = 0;
+ qt_glAttachShader = 0;
+ qt_glDetachShader = 0;
+ qt_glLinkProgram = 0;
+ qt_glUseProgram = 0;
+ qt_glDeleteProgram = 0;
+ qt_glIsProgram = 0;
+
+ qt_glGetShaderInfoLog = 0;
+ qt_glGetShaderiv = 0;
+ qt_glGetShaderSource = 0;
+ qt_glGetProgramiv = 0;
+ qt_glGetProgramInfoLog = 0;
+
+ qt_glGetUniformLocation = 0;
+ qt_glUniform4fv = 0;
+ qt_glUniform3fv = 0;
+ qt_glUniform2fv = 0;
+ qt_glUniform1fv = 0;
+ qt_glUniform1i = 0;
+ qt_glUniform1iv = 0;
+ qt_glUniformMatrix2fv = 0;
+ qt_glUniformMatrix3fv = 0;
+ qt_glUniformMatrix4fv = 0;
+ qt_glUniformMatrix2x3fv = 0;
+ qt_glUniformMatrix2x4fv = 0;
+ qt_glUniformMatrix3x2fv = 0;
+ qt_glUniformMatrix3x4fv = 0;
+ qt_glUniformMatrix4x2fv = 0;
+ qt_glUniformMatrix4x3fv = 0;
+
+ qt_glBindAttribLocation = 0;
+ qt_glGetAttribLocation = 0;
+ qt_glVertexAttrib1fv = 0;
+ qt_glVertexAttrib2fv = 0;
+ qt_glVertexAttrib3fv = 0;
+ qt_glVertexAttrib4fv = 0;
+ qt_glVertexAttribPointer = 0;
+ qt_glDisableVertexAttribArray = 0;
+ qt_glEnableVertexAttribArray = 0;
+
+ // Extras for GL2 engine:
+ qt_glActiveTexture = 0;
+ qt_glStencilOpSeparate = 0;
+ qt_glBlendColor = 0;
+
+ qt_glActiveStencilFaceEXT = 0;
+ qt_glMultiTexCoord4f = 0;
+#else
+ qt_glslResolved = false;
+
+ qt_glGetProgramBinaryOES = 0;
+ qt_glProgramBinaryOES = 0;
+#endif
+
+ // FBOs
+#if !defined(QT_OPENGL_ES_2)
+ qt_glIsRenderbuffer = 0;
+ qt_glBindRenderbuffer = 0;
+ qt_glDeleteRenderbuffers = 0;
+ qt_glGenRenderbuffers = 0;
+ qt_glRenderbufferStorage = 0;
+ qt_glGetRenderbufferParameteriv = 0;
+ qt_glIsFramebuffer = 0;
+ qt_glBindFramebuffer = 0;
+ qt_glDeleteFramebuffers = 0;
+ qt_glGenFramebuffers = 0;
+ qt_glCheckFramebufferStatus = 0;
+ qt_glFramebufferTexture2D = 0;
+ qt_glFramebufferRenderbuffer = 0;
+ qt_glGetFramebufferAttachmentParameteriv = 0;
+ qt_glGenerateMipmap = 0;
+#endif
+ qt_glBlitFramebufferEXT = 0;
+ qt_glRenderbufferStorageMultisampleEXT = 0;
+
+ // Buffer objects:
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
+ qt_glBindBuffer = 0;
+ qt_glDeleteBuffers = 0;
+ qt_glGenBuffers = 0;
+ qt_glBufferData = 0;
+ qt_glBufferSubData = 0;
+ qt_glGetBufferSubData = 0;
+ qt_glGetBufferParameteriv = 0;
+#endif
+ qt_glMapBufferARB = 0;
+ qt_glUnmapBufferARB = 0;
+
+ qt_glProgramParameteriEXT = 0;
+ qt_glFramebufferTextureEXT = 0;
+ qt_glFramebufferTextureLayerEXT = 0;
+ qt_glFramebufferTextureFaceEXT = 0;
+#if !defined(QT_OPENGL_ES)
+ // Texture compression
+ qt_glCompressedTexImage2DARB = 0;
+#endif
+
+#ifndef QT_NO_EGL
+ // OES_EGL_image
+ qt_glEGLImageTargetTexture2DOES = 0;
+ qt_glEGLImageTargetRenderbufferStorageOES = 0;
+#endif
+ }
+
+
+#if !defined(QT_OPENGL_ES_2)
+ _glProgramStringARB qt_glProgramStringARB;
+ _glBindProgramARB qt_glBindProgramARB;
+ _glDeleteProgramsARB qt_glDeleteProgramsARB;
+ _glGenProgramsARB qt_glGenProgramsARB;
+ _glProgramLocalParameter4fvARB qt_glProgramLocalParameter4fvARB;
+
+ // GLSL definitions
+ _glCreateShader qt_glCreateShader;
+ _glShaderSource qt_glShaderSource;
+ _glShaderBinary qt_glShaderBinary;
+ _glCompileShader qt_glCompileShader;
+ _glDeleteShader qt_glDeleteShader;
+ _glIsShader qt_glIsShader;
+
+ _glCreateProgram qt_glCreateProgram;
+ _glAttachShader qt_glAttachShader;
+ _glDetachShader qt_glDetachShader;
+ _glLinkProgram qt_glLinkProgram;
+ _glUseProgram qt_glUseProgram;
+ _glDeleteProgram qt_glDeleteProgram;
+ _glIsProgram qt_glIsProgram;
+
+ _glGetShaderInfoLog qt_glGetShaderInfoLog;
+ _glGetShaderiv qt_glGetShaderiv;
+ _glGetShaderSource qt_glGetShaderSource;
+ _glGetProgramiv qt_glGetProgramiv;
+ _glGetProgramInfoLog qt_glGetProgramInfoLog;
+
+ _glGetUniformLocation qt_glGetUniformLocation;
+ _glUniform4fv qt_glUniform4fv;
+ _glUniform3fv qt_glUniform3fv;
+ _glUniform2fv qt_glUniform2fv;
+ _glUniform1fv qt_glUniform1fv;
+ _glUniform1i qt_glUniform1i;
+ _glUniform1iv qt_glUniform1iv;
+ _glUniformMatrix2fv qt_glUniformMatrix2fv;
+ _glUniformMatrix3fv qt_glUniformMatrix3fv;
+ _glUniformMatrix4fv qt_glUniformMatrix4fv;
+ _glUniformMatrix2x3fv qt_glUniformMatrix2x3fv;
+ _glUniformMatrix2x4fv qt_glUniformMatrix2x4fv;
+ _glUniformMatrix3x2fv qt_glUniformMatrix3x2fv;
+ _glUniformMatrix3x4fv qt_glUniformMatrix3x4fv;
+ _glUniformMatrix4x2fv qt_glUniformMatrix4x2fv;
+ _glUniformMatrix4x3fv qt_glUniformMatrix4x3fv;
+
+ _glBindAttribLocation qt_glBindAttribLocation;
+ _glGetAttribLocation qt_glGetAttribLocation;
+ _glVertexAttrib1fv qt_glVertexAttrib1fv;
+ _glVertexAttrib2fv qt_glVertexAttrib2fv;
+ _glVertexAttrib3fv qt_glVertexAttrib3fv;
+ _glVertexAttrib4fv qt_glVertexAttrib4fv;
+ _glVertexAttribPointer qt_glVertexAttribPointer;
+ _glDisableVertexAttribArray qt_glDisableVertexAttribArray;
+ _glEnableVertexAttribArray qt_glEnableVertexAttribArray;
+
+#else
+ bool qt_glslResolved;
+
+ _glGetProgramBinaryOES qt_glGetProgramBinaryOES;
+ _glProgramBinaryOES qt_glProgramBinaryOES;
+#endif
+
+ _glActiveStencilFaceEXT qt_glActiveStencilFaceEXT;
+ _glMultiTexCoord4f qt_glMultiTexCoord4f;
+
+#if !defined(QT_OPENGL_ES_2)
+ // Extras needed for GL2 engine:
+ _glActiveTexture qt_glActiveTexture;
+ _glStencilOpSeparate qt_glStencilOpSeparate;
+ _glBlendColor qt_glBlendColor;
+
+#endif
+
+ // FBOs
+#if !defined(QT_OPENGL_ES_2)
+ _glIsRenderbuffer qt_glIsRenderbuffer;
+ _glBindRenderbuffer qt_glBindRenderbuffer;
+ _glDeleteRenderbuffers qt_glDeleteRenderbuffers;
+ _glGenRenderbuffers qt_glGenRenderbuffers;
+ _glRenderbufferStorage qt_glRenderbufferStorage;
+ _glGetRenderbufferParameteriv qt_glGetRenderbufferParameteriv;
+ _glIsFramebuffer qt_glIsFramebuffer;
+ _glBindFramebuffer qt_glBindFramebuffer;
+ _glDeleteFramebuffers qt_glDeleteFramebuffers;
+ _glGenFramebuffers qt_glGenFramebuffers;
+ _glCheckFramebufferStatus qt_glCheckFramebufferStatus;
+ _glFramebufferTexture2D qt_glFramebufferTexture2D;
+ _glFramebufferRenderbuffer qt_glFramebufferRenderbuffer;
+ _glGetFramebufferAttachmentParameteriv qt_glGetFramebufferAttachmentParameteriv;
+ _glGenerateMipmap qt_glGenerateMipmap;
+#endif
+ _glBlitFramebufferEXT qt_glBlitFramebufferEXT;
+ _glRenderbufferStorageMultisampleEXT qt_glRenderbufferStorageMultisampleEXT;
+
+ // Buffer objects
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
+ _glBindBuffer qt_glBindBuffer;
+ _glDeleteBuffers qt_glDeleteBuffers;
+ _glGenBuffers qt_glGenBuffers;
+ _glBufferData qt_glBufferData;
+ _glBufferSubData qt_glBufferSubData;
+ _glGetBufferSubData qt_glGetBufferSubData;
+ _glGetBufferParameteriv qt_glGetBufferParameteriv;
+#endif
+ _glMapBufferARB qt_glMapBufferARB;
+ _glUnmapBufferARB qt_glUnmapBufferARB;
+
+ // Geometry shaders...
+ _glProgramParameteriEXT qt_glProgramParameteriEXT;
+ _glFramebufferTextureEXT qt_glFramebufferTextureEXT;
+ _glFramebufferTextureLayerEXT qt_glFramebufferTextureLayerEXT;
+ _glFramebufferTextureFaceEXT qt_glFramebufferTextureFaceEXT;
+#if !defined(QT_OPENGL_ES)
+ // Texture compression
+ _glCompressedTexImage2DARB qt_glCompressedTexImage2DARB;
+#endif
+
+#ifndef QT_NO_EGL
+ // OES_EGL_image
+ _glEGLImageTargetTexture2DOES qt_glEGLImageTargetTexture2DOES;
+ _glEGLImageTargetRenderbufferStorageOES qt_glEGLImageTargetRenderbufferStorageOES;
+#endif
+};
+
+
+// OpenGL constants
+
+#ifndef GL_ARRAY_BUFFER
+#define GL_ARRAY_BUFFER 0x8892
+#endif
+
+#ifndef GL_STATIC_DRAW
+#define GL_STATIC_DRAW 0x88E4
+#endif
+
+/* NV_texture_rectangle */
+#ifndef GL_NV_texture_rectangle
+#define GL_TEXTURE_RECTANGLE_NV 0x84F5
+#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6
+#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7
+#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 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_IBM_texture_mirrored_repeat
+#define GL_MIRRORED_REPEAT_IBM 0x8370
+#endif
+
+#ifndef GL_SGIS_generate_mipmap
+#define GL_GENERATE_MIPMAP_SGIS 0x8191
+#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192
+#endif
+
+// ARB_fragment_program extension protos
+#ifndef GL_FRAGMENT_PROGRAM_ARB
+#define GL_FRAGMENT_PROGRAM_ARB 0x8804
+#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875
+#endif
+
+#ifndef GL_PIXEL_UNPACK_BUFFER_ARB
+#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC
+#endif
+
+#ifndef GL_WRITE_ONLY_ARB
+#define GL_WRITE_ONLY_ARB 0x88B9
+#endif
+
+#ifndef GL_STREAM_DRAW_ARB
+#define GL_STREAM_DRAW_ARB 0x88E0
+#endif
+
+// Stencil wrap and two-side defines
+#ifndef GL_STENCIL_TEST_TWO_SIDE_EXT
+#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910
+#endif
+#ifndef GL_INCR_WRAP_EXT
+#define GL_INCR_WRAP_EXT 0x8507
+#endif
+#ifndef GL_DECR_WRAP_EXT
+#define GL_DECR_WRAP_EXT 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_OES
+#define GL_DEPTH_COMPONENT24_OES 0x81A6
+#endif
+
+#ifndef GL_EXT_framebuffer_object
+#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506
+#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8
+#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6
+#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4
+#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7
+#define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT 0x8CD8
+#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9
+#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA
+#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB
+#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC
+#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD
+#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF
+#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0
+#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1
+#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2
+#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3
+#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4
+#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5
+#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6
+#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7
+#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8
+#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9
+#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA
+#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB
+#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC
+#define GL_COLOR_ATTACHMENT13_EXT 0x8CED
+#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE
+#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF
+#define GL_DEPTH_ATTACHMENT_EXT 0x8D00
+#define GL_STENCIL_ATTACHMENT_EXT 0x8D20
+#define GL_FRAMEBUFFER_EXT 0x8D40
+#define GL_RENDERBUFFER_EXT 0x8D41
+#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42
+#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43
+#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44
+#define GL_STENCIL_INDEX_EXT 0x8D45
+#define GL_STENCIL_INDEX1_EXT 0x8D46
+#define GL_STENCIL_INDEX4_EXT 0x8D47
+#define GL_STENCIL_INDEX8_EXT 0x8D48
+#define GL_STENCIL_INDEX16_EXT 0x8D49
+#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50
+#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51
+#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52
+#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53
+#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54
+#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55
+#endif
+
+// GL_EXT_framebuffer_blit
+#ifndef GL_READ_FRAMEBUFFER_EXT
+#define GL_READ_FRAMEBUFFER_EXT 0x8CA8
+#endif
+
+// GL_EXT_framebuffer_multisample
+#ifndef GL_RENDERBUFFER_SAMPLES_EXT
+#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT
+#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56
+#endif
+
+#ifndef GL_MAX_SAMPLES_EXT
+#define GL_MAX_SAMPLES_EXT 0x8D57
+#endif
+
+#ifndef GL_DRAW_FRAMEBUFFER_EXT
+#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9
+#endif
+
+#ifndef GL_EXT_packed_depth_stencil
+#define GL_DEPTH_STENCIL_EXT 0x84F9
+#define GL_UNSIGNED_INT_24_8_EXT 0x84FA
+#define GL_DEPTH24_STENCIL8_EXT 0x88F0
+#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1
+#endif
+
+// ### hm. should be part of the GL 1.2 spec..
+#ifndef GL_CLAMP_TO_EDGE
+#define GL_CLAMP_TO_EDGE 0x812F
+#endif
+
+#ifndef GL_VERSION_1_2
+#define GL_PACK_SKIP_IMAGES 0x806B
+#define GL_PACK_IMAGE_HEIGHT 0x806C
+#define GL_UNPACK_SKIP_IMAGES 0x806D
+#define GL_UNPACK_IMAGE_HEIGHT 0x806E
+#endif
+
+#ifndef GL_VERSION_1_4
+#define GL_CONSTANT_COLOR 0x8001
+#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
+#define GL_CONSTANT_ALPHA 0x8003
+#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
+#define GL_INCR_WRAP 0x8507
+#define GL_DECR_WRAP 0x8508
+#endif
+
+#ifndef GL_VERSION_1_5
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define GL_STREAM_DRAW 0x88E0
+#define GL_STREAM_READ 0x88E1
+#define GL_STREAM_COPY 0x88E2
+#define GL_STATIC_DRAW 0x88E4
+#define GL_STATIC_READ 0x88E5
+#define GL_STATIC_COPY 0x88E6
+#define GL_DYNAMIC_DRAW 0x88E8
+#define GL_DYNAMIC_READ 0x88E9
+#define GL_DYNAMIC_COPY 0x88EA
+#endif
+
+#ifndef GL_VERSION_2_0
+#define GL_FRAGMENT_SHADER 0x8B30
+#define GL_VERTEX_SHADER 0x8B31
+#define GL_FLOAT_VEC2 0x8B50
+#define GL_FLOAT_VEC3 0x8B51
+#define GL_FLOAT_VEC4 0x8B52
+#define GL_INT_VEC2 0x8B53
+#define GL_INT_VEC3 0x8B54
+#define GL_INT_VEC4 0x8B55
+#define GL_BOOL 0x8B56
+#define GL_BOOL_VEC2 0x8B57
+#define GL_BOOL_VEC3 0x8B58
+#define GL_BOOL_VEC4 0x8B59
+#define GL_FLOAT_MAT2 0x8B5A
+#define GL_FLOAT_MAT3 0x8B5B
+#define GL_FLOAT_MAT4 0x8B5C
+#define GL_SAMPLER_1D 0x8B5D
+#define GL_SAMPLER_2D 0x8B5E
+#define GL_SAMPLER_3D 0x8B5F
+#define GL_SAMPLER_CUBE 0x8B60
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_LINK_STATUS 0x8B82
+#define GL_INFO_LOG_LENGTH 0x8B84
+#define GL_ACTIVE_UNIFORMS 0x8B86
+#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87
+#define GL_ACTIVE_ATTRIBUTES 0x8B89
+#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
+#endif
+
+// Geometry shader defines
+#ifndef GL_GEOMETRY_SHADER_EXT
+# define GL_GEOMETRY_SHADER_EXT 0x8DD9
+# define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA
+# define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB
+# define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC
+# define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29
+# define GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD
+# define GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE
+# define GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B
+# define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF
+# define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0
+# define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1
+# 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
+# define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8
+# define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9
+# define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7
+# define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4
+# define GL_PROGRAM_POINT_SIZE_EXT 0x8642
+#endif
+
+#if !defined(QT_OPENGL_ES_2)
+#define glProgramStringARB QGLContextPrivate::extensionFuncs(ctx).qt_glProgramStringARB
+#define glBindProgramARB QGLContextPrivate::extensionFuncs(ctx).qt_glBindProgramARB
+#define glDeleteProgramsARB QGLContextPrivate::extensionFuncs(ctx).qt_glDeleteProgramsARB
+#define glGenProgramsARB QGLContextPrivate::extensionFuncs(ctx).qt_glGenProgramsARB
+#define glProgramLocalParameter4fvARB QGLContextPrivate::extensionFuncs(ctx).qt_glProgramLocalParameter4fvARB
+
+#define glActiveStencilFaceEXT QGLContextPrivate::extensionFuncs(ctx).qt_glActiveStencilFaceEXT
+
+#define glMultiTexCoord4f QGLContextPrivate::extensionFuncs(ctx).qt_glMultiTexCoord4f
+
+#define glActiveTexture QGLContextPrivate::extensionFuncs(ctx).qt_glActiveTexture
+#endif // !defined(QT_OPENGL_ES_2)
+
+
+// FBOs
+#if !defined(QT_OPENGL_ES_2)
+#define glIsRenderbuffer QGLContextPrivate::extensionFuncs(ctx).qt_glIsRenderbuffer
+#define glBindRenderbuffer QGLContextPrivate::extensionFuncs(ctx).qt_glBindRenderbuffer
+#define glDeleteRenderbuffers QGLContextPrivate::extensionFuncs(ctx).qt_glDeleteRenderbuffers
+#define glGenRenderbuffers QGLContextPrivate::extensionFuncs(ctx).qt_glGenRenderbuffers
+#define glRenderbufferStorage QGLContextPrivate::extensionFuncs(ctx).qt_glRenderbufferStorage
+#define glGetRenderbufferParameteriv QGLContextPrivate::extensionFuncs(ctx).qt_glGetRenderbufferParameteriv
+#define glIsFramebuffer QGLContextPrivate::extensionFuncs(ctx).qt_glIsFramebuffer
+#define glBindFramebuffer QGLContextPrivate::extensionFuncs(ctx).qt_glBindFramebuffer
+#define glDeleteFramebuffers QGLContextPrivate::extensionFuncs(ctx).qt_glDeleteFramebuffers
+#define glGenFramebuffers QGLContextPrivate::extensionFuncs(ctx).qt_glGenFramebuffers
+#define glCheckFramebufferStatus QGLContextPrivate::extensionFuncs(ctx).qt_glCheckFramebufferStatus
+#define glFramebufferTexture2D QGLContextPrivate::extensionFuncs(ctx).qt_glFramebufferTexture2D
+#define glFramebufferRenderbuffer QGLContextPrivate::extensionFuncs(ctx).qt_glFramebufferRenderbuffer
+#define glGetFramebufferAttachmentParameteriv QGLContextPrivate::extensionFuncs(ctx).qt_glGetFramebufferAttachmentParameteriv
+#define glGenerateMipmap QGLContextPrivate::extensionFuncs(ctx).qt_glGenerateMipmap
+#endif // QT_OPENGL_ES_2
+#define glBlitFramebufferEXT QGLContextPrivate::extensionFuncs(ctx).qt_glBlitFramebufferEXT
+#define glRenderbufferStorageMultisampleEXT QGLContextPrivate::extensionFuncs(ctx).qt_glRenderbufferStorageMultisampleEXT
+
+
+// Buffer objects
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
+#define glBindBuffer QGLContextPrivate::extensionFuncs(ctx).qt_glBindBuffer
+#define glDeleteBuffers QGLContextPrivate::extensionFuncs(ctx).qt_glDeleteBuffers
+#define glGenBuffers QGLContextPrivate::extensionFuncs(ctx).qt_glGenBuffers
+#define glBufferData QGLContextPrivate::extensionFuncs(ctx).qt_glBufferData
+#define glBufferSubData QGLContextPrivate::extensionFuncs(ctx).qt_glBufferSubData
+#define glGetBufferSubData QGLContextPrivate::extensionFuncs(ctx).qt_glGetBufferSubData
+#define glGetBufferParameteriv QGLContextPrivate::extensionFuncs(ctx).qt_glGetBufferParameteriv
+#endif
+#define glMapBufferARB QGLContextPrivate::extensionFuncs(ctx).qt_glMapBufferARB
+#define glUnmapBufferARB QGLContextPrivate::extensionFuncs(ctx).qt_glUnmapBufferARB
+
+
+// GLSL
+#if !defined(QT_OPENGL_ES_2)
+
+#define glCreateShader QGLContextPrivate::extensionFuncs(ctx).qt_glCreateShader
+#define glShaderSource QGLContextPrivate::extensionFuncs(ctx).qt_glShaderSource
+#define glShaderBinary QGLContextPrivate::extensionFuncs(ctx).qt_glShaderBinary
+#define glCompileShader QGLContextPrivate::extensionFuncs(ctx).qt_glCompileShader
+#define glDeleteShader QGLContextPrivate::extensionFuncs(ctx).qt_glDeleteShader
+#define glIsShader QGLContextPrivate::extensionFuncs(ctx).qt_glIsShader
+
+#define glCreateProgram QGLContextPrivate::extensionFuncs(ctx).qt_glCreateProgram
+#define glAttachShader QGLContextPrivate::extensionFuncs(ctx).qt_glAttachShader
+#define glDetachShader QGLContextPrivate::extensionFuncs(ctx).qt_glDetachShader
+#define glLinkProgram QGLContextPrivate::extensionFuncs(ctx).qt_glLinkProgram
+#define glUseProgram QGLContextPrivate::extensionFuncs(ctx).qt_glUseProgram
+#define glDeleteProgram QGLContextPrivate::extensionFuncs(ctx).qt_glDeleteProgram
+#define glIsProgram QGLContextPrivate::extensionFuncs(ctx).qt_glIsProgram
+
+#define glGetShaderInfoLog QGLContextPrivate::extensionFuncs(ctx).qt_glGetShaderInfoLog
+#define glGetShaderiv QGLContextPrivate::extensionFuncs(ctx).qt_glGetShaderiv
+#define glGetShaderSource QGLContextPrivate::extensionFuncs(ctx).qt_glGetShaderSource
+#define glGetProgramiv QGLContextPrivate::extensionFuncs(ctx).qt_glGetProgramiv
+#define glGetProgramInfoLog QGLContextPrivate::extensionFuncs(ctx).qt_glGetProgramInfoLog
+
+#define glGetUniformLocation QGLContextPrivate::extensionFuncs(ctx).qt_glGetUniformLocation
+#define glUniform4fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniform4fv
+#define glUniform3fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniform3fv
+#define glUniform2fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniform2fv
+#define glUniform1fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniform1fv
+#define glUniform1i QGLContextPrivate::extensionFuncs(ctx).qt_glUniform1i
+#define glUniform1iv QGLContextPrivate::extensionFuncs(ctx).qt_glUniform1iv
+#define glUniformMatrix2fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniformMatrix2fv
+#define glUniformMatrix3fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniformMatrix3fv
+#define glUniformMatrix4fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniformMatrix4fv
+#define glUniformMatrix2x3fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniformMatrix2x3fv
+#define glUniformMatrix2x4fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniformMatrix2x4fv
+#define glUniformMatrix3x2fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniformMatrix3x2fv
+#define glUniformMatrix3x4fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniformMatrix3x4fv
+#define glUniformMatrix4x2fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniformMatrix4x2fv
+#define glUniformMatrix4x3fv QGLContextPrivate::extensionFuncs(ctx).qt_glUniformMatrix4x3fv
+
+#define glBindAttribLocation QGLContextPrivate::extensionFuncs(ctx).qt_glBindAttribLocation
+#define glGetAttribLocation QGLContextPrivate::extensionFuncs(ctx).qt_glGetAttribLocation
+#define glVertexAttrib1fv QGLContextPrivate::extensionFuncs(ctx).qt_glVertexAttrib1fv
+#define glVertexAttrib2fv QGLContextPrivate::extensionFuncs(ctx).qt_glVertexAttrib2fv
+#define glVertexAttrib3fv QGLContextPrivate::extensionFuncs(ctx).qt_glVertexAttrib3fv
+#define glVertexAttrib4fv QGLContextPrivate::extensionFuncs(ctx).qt_glVertexAttrib4fv
+#define glVertexAttribPointer QGLContextPrivate::extensionFuncs(ctx).qt_glVertexAttribPointer
+#define glDisableVertexAttribArray QGLContextPrivate::extensionFuncs(ctx).qt_glDisableVertexAttribArray
+#define glEnableVertexAttribArray QGLContextPrivate::extensionFuncs(ctx).qt_glEnableVertexAttribArray
+
+#else // QT_OPENGL_ES_2
+
+#define glGetProgramBinaryOES QGLContextPrivate::extensionFuncs(ctx).qt_glGetProgramBinaryOES
+#define glProgramBinaryOES QGLContextPrivate::extensionFuncs(ctx).qt_glProgramBinaryOES
+
+#endif // QT_OPENGL_ES_2
+
+
+#if !defined(QT_OPENGL_ES_2)
+#define glStencilOpSeparate QGLContextPrivate::extensionFuncs(ctx).qt_glStencilOpSeparate
+#define glBlendColor QGLContextPrivate::extensionFuncs(ctx).qt_glBlendColor
+#endif
+
+#if defined(QT_OPENGL_ES_2)
+#define glClearDepth glClearDepthf
+#endif
+
+#define glProgramParameteriEXT QGLContextPrivate::extensionFuncs(ctx).qt_glProgramParameteriEXT
+#define glFramebufferTextureEXT QGLContextPrivate::extensionFuncs(ctx).qt_glFramebufferTextureEXT
+#define glFramebufferTextureLayerEXT QGLContextPrivate::extensionFuncs(ctx).qt_glFramebufferTextureLayerEXT
+#define glFramebufferTextureFaceEXT QGLContextPrivate::extensionFuncs(ctx).qt_glFramebufferTextureFaceEXT
+
+#if !defined(QT_OPENGL_ES)
+#define glCompressedTexImage2D QGLContextPrivate::extensionFuncs(ctx).qt_glCompressedTexImage2DARB
+#endif
+
+#ifndef QT_NO_EGL
+// OES_EGL_image
+#define glEGLImageTargetTexture2DOES QGLContextPrivate::extensionFuncs(ctx).qt_glEGLImageTargetTexture2DOES
+#define glEGLImageTargetRenderbufferStorageOES QGLContextPrivate::extensionFuncs(ctx).qt_glEGLImageTargetRenderbufferStorageOES
+#endif
+
+extern bool qt_resolve_framebufferobject_extensions(QGLContext *ctx);
+bool Q_OPENGL_EXPORT qt_resolve_buffer_extensions(QGLContext *ctx);
+
+bool qt_resolve_version_1_3_functions(QGLContext *ctx);
+bool Q_OPENGL_EXPORT qt_resolve_version_2_0_functions(QGLContext *ctx);
+bool qt_resolve_stencil_face_extension(QGLContext *ctx);
+bool qt_resolve_frag_program_extensions(QGLContext *ctx);
+
+bool qt_resolve_glsl_extensions(QGLContext *ctx);
+
+#ifndef QT_NO_EGL
+Q_OPENGL_EXPORT bool qt_resolve_eglimage_gl_extensions(QGLContext *ctx);
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QGL_EXTENSIONS_P_H
diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp
new file mode 100644
index 0000000000..8eda222c8d
--- /dev/null
+++ b/src/opengl/qglframebufferobject.cpp
@@ -0,0 +1,1398 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglframebufferobject.h"
+#include "qglframebufferobject_p.h"
+
+#include <qdebug.h>
+#include <private/qgl_p.h>
+#include <private/qfont_p.h>
+#if !defined(QT_OPENGL_ES_1)
+#include <private/qpaintengineex_opengl2_p.h>
+#endif
+
+#ifndef QT_OPENGL_ES_2
+#include <private/qpaintengine_opengl_p.h>
+#endif
+
+#include <qglframebufferobject.h>
+#include <qlibrary.h>
+#include <qimage.h>
+
+QT_BEGIN_NAMESPACE
+
+extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool);
+
+#define QGL_FUNC_CONTEXT const QGLContext *ctx = d_ptr->fbo_guard.context();
+#define QGL_FUNCP_CONTEXT const QGLContext *ctx = fbo_guard.context();
+
+#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 QGLFramebufferObjectFormat
+ \brief The QGLFramebufferObjectFormat 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 QGLFramebufferObject::format()
+ after creating a QGLFramebufferObject to find the exact format that was
+ used to create the frame buffer object.
+
+ \sa QGLFramebufferObject
+*/
+
+/*!
+ \internal
+*/
+void QGLFramebufferObjectFormat::detach()
+{
+ if (d->ref != 1) {
+ QGLFramebufferObjectFormatPrivate *newd
+ = new QGLFramebufferObjectFormatPrivate(d);
+ if (!d->ref.deref())
+ delete d;
+ d = newd;
+ }
+}
+
+/*!
+ Creates a QGLFramebufferObjectFormat 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()
+*/
+
+QGLFramebufferObjectFormat::QGLFramebufferObjectFormat()
+{
+ d = new QGLFramebufferObjectFormatPrivate;
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QGLFramebufferObjectFormat::QGLFramebufferObjectFormat(const QGLFramebufferObjectFormat &other)
+{
+ d = other.d;
+ d->ref.ref();
+}
+
+/*!
+ Assigns \a other to this object.
+*/
+
+QGLFramebufferObjectFormat &QGLFramebufferObjectFormat::operator=(const QGLFramebufferObjectFormat &other)
+{
+ if (d != other.d) {
+ other.d->ref.ref();
+ if (!d->ref.deref())
+ delete d;
+ d = other.d;
+ }
+ return *this;
+}
+
+/*!
+ Destroys the QGLFramebufferObjectFormat.
+*/
+QGLFramebufferObjectFormat::~QGLFramebufferObjectFormat()
+{
+ 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 QGLFramebufferObjectFormat::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 QGLFramebufferObjectFormat::samples() const
+{
+ return d->samples;
+}
+
+/*!
+ \since 4.8
+
+ Enables or disables mipmapping. 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(), texture()
+*/
+void QGLFramebufferObjectFormat::setMipmap(bool enabled)
+{
+ detach();
+ d->mipmap = enabled;
+}
+
+/*!
+ \since 4.8
+
+ Returns true if mipmapping is enabled.
+
+ \sa setMipmap()
+*/
+bool QGLFramebufferObjectFormat::mipmap() const
+{
+ return d->mipmap;
+}
+
+/*!
+ Sets the attachment configuration of a framebuffer object to \a attachment.
+
+ \sa attachment()
+*/
+void QGLFramebufferObjectFormat::setAttachment(QGLFramebufferObject::Attachment attachment)
+{
+ detach();
+ d->attachment = attachment;
+}
+
+/*!
+ Returns the configuration of the depth and stencil buffers attached to
+ a framebuffer object. The default is QGLFramebufferObject::NoAttachment.
+
+ \sa setAttachment()
+*/
+QGLFramebufferObject::Attachment QGLFramebufferObjectFormat::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 QGLFramebufferObjectFormat::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 QGLFramebufferObjectFormat::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 QGLFramebufferObjectFormat::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 QGLFramebufferObjectFormat::internalTextureFormat() const
+{
+ return d->internal_format;
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLFramebufferObjectFormat::setTextureTarget(QMacCompatGLenum target)
+{
+ detach();
+ d->target = target;
+}
+
+/*! \internal */
+void QGLFramebufferObjectFormat::setInternalTextureFormat(QMacCompatGLenum internalTextureFormat)
+{
+ detach();
+ d->internal_format = internalTextureFormat;
+}
+#endif
+
+/*!
+ Returns true if all the options of this framebuffer object format
+ are the same as \a other; otherwise returns false.
+*/
+bool QGLFramebufferObjectFormat::operator==(const QGLFramebufferObjectFormat& 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 QGLFramebufferObjectFormat::operator!=(const QGLFramebufferObjectFormat& other) const
+{
+ return !(*this == other);
+}
+
+void QGLFBOGLPaintDevice::setFBO(QGLFramebufferObject* f,
+ QGLFramebufferObject::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 = QGLContext::currentContext()->format();
+ if (attachment == QGLFramebufferObject::CombinedDepthStencil) {
+ fboFormat.setDepth(true);
+ fboFormat.setStencil(true);
+ } else if (attachment == QGLFramebufferObject::Depth) {
+ fboFormat.setDepth(true);
+ fboFormat.setStencil(false);
+ } else {
+ fboFormat.setDepth(false);
+ fboFormat.setStencil(false);
+ }
+
+ GLenum format = f->format().internalTextureFormat();
+ reqAlpha = (format != GL_RGB
+#ifndef QT_OPENGL_ES
+ && format != GL_RGB5 && format != GL_RGB8
+#endif
+ );
+}
+
+QGLContext *QGLFBOGLPaintDevice::context() const
+{
+ QGLContext *fboContext = const_cast<QGLContext *>(fbo->d_ptr->fbo_guard.context());
+ QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext());
+
+ if (QGLContextPrivate::contextGroup(fboContext) == QGLContextPrivate::contextGroup(currentContext))
+ return currentContext;
+ else
+ return fboContext;
+}
+
+bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const
+{
+ QGL_FUNCP_CONTEXT;
+ if (!ctx)
+ return false; // Context no longer exists.
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
+ switch(status) {
+ case GL_NO_ERROR:
+ case GL_FRAMEBUFFER_COMPLETE_EXT:
+ return true;
+ break;
+ case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
+ qDebug("QGLFramebufferObject: Unsupported framebuffer format.");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete attachment.");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, missing attachment.");
+ break;
+#ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT
+ case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, duplicate attachment.");
+ break;
+#endif
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions.");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, attached images must have same format.");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, missing draw buffer.");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, missing read buffer.");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel.");
+ break;
+ default:
+ qDebug() <<"QGLFramebufferObject: An undefined error has occurred: "<< status;
+ break;
+ }
+ return false;
+}
+
+void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
+ QGLFramebufferObject::Attachment attachment,
+ GLenum texture_target, GLenum internal_format,
+ GLint samples, bool mipmap)
+{
+ QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
+ fbo_guard.setContext(ctx);
+
+ bool ext_detected = (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject);
+ if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx)))
+ return;
+
+ size = sz;
+ target = texture_target;
+ // texture dimensions
+
+ QT_RESET_GLERROR(); // reset error state
+ GLuint fbo = 0;
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo);
+ fbo_guard.setId(fbo);
+
+ glDevice.setFBO(q, attachment);
+
+ 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)
+ 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
+ glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ target, texture, 0);
+
+ QT_CHECK_GLERROR();
+ valid = checkFramebufferStatus();
+ glBindTexture(target, 0);
+
+ color_buffer = 0;
+ } else {
+ mipmap = false;
+ GLint maxSamples;
+ glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples);
+
+ samples = qBound(0, int(samples), int(maxSamples));
+
+ glGenRenderbuffers(1, &color_buffer);
+ glBindRenderbuffer(GL_RENDERBUFFER_EXT, color_buffer);
+ if (glRenderbufferStorageMultisampleEXT && samples > 0) {
+ glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
+ internal_format, size.width(), size.height());
+ } else {
+ samples = 0;
+ glRenderbufferStorage(GL_RENDERBUFFER_EXT, internal_format,
+ size.width(), size.height());
+ }
+
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_RENDERBUFFER_EXT, color_buffer);
+
+ QT_CHECK_GLERROR();
+ valid = checkFramebufferStatus();
+
+ if (valid)
+ glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_SAMPLES_EXT, &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 == QGLFramebufferObject::CombinedDepthStencil
+ && (QGLExtensions::glExtensions() & QGLExtensions::PackedDepthStencil)) {
+ // depth and stencil buffer needs another extension
+ glGenRenderbuffers(1, &depth_buffer);
+ Q_ASSERT(!glIsRenderbuffer(depth_buffer));
+ glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_buffer);
+ Q_ASSERT(glIsRenderbuffer(depth_buffer));
+ if (samples != 0 && glRenderbufferStorageMultisampleEXT)
+ glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
+ GL_DEPTH24_STENCIL8_EXT, size.width(), size.height());
+ else
+ glRenderbufferStorage(GL_RENDERBUFFER_EXT,
+ GL_DEPTH24_STENCIL8_EXT, size.width(), size.height());
+
+ stencil_buffer = depth_buffer;
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, depth_buffer);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, stencil_buffer);
+
+ valid = checkFramebufferStatus();
+ if (!valid) {
+ glDeleteRenderbuffers(1, &depth_buffer);
+ stencil_buffer = depth_buffer = 0;
+ }
+ }
+
+ if (depth_buffer == 0 && (attachment == QGLFramebufferObject::CombinedDepthStencil
+ || (attachment == QGLFramebufferObject::Depth)))
+ {
+ glGenRenderbuffers(1, &depth_buffer);
+ Q_ASSERT(!glIsRenderbuffer(depth_buffer));
+ glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_buffer);
+ Q_ASSERT(glIsRenderbuffer(depth_buffer));
+ if (samples != 0 && glRenderbufferStorageMultisampleEXT) {
+#ifdef QT_OPENGL_ES
+ if (QGLExtensions::glExtensions() & QGLExtensions::Depth24) {
+ glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
+ GL_DEPTH_COMPONENT24_OES, size.width(), size.height());
+ } else {
+ glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
+ GL_DEPTH_COMPONENT16, size.width(), size.height());
+ }
+#else
+ glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
+ GL_DEPTH_COMPONENT, size.width(), size.height());
+#endif
+ } else {
+#ifdef QT_OPENGL_ES
+ if (QGLExtensions::glExtensions() & QGLExtensions::Depth24) {
+ glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24_OES,
+ size.width(), size.height());
+ } else {
+ glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16,
+ size.width(), size.height());
+ }
+#else
+ glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, size.width(), size.height());
+#endif
+ }
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, depth_buffer);
+ valid = checkFramebufferStatus();
+ if (!valid) {
+ glDeleteRenderbuffers(1, &depth_buffer);
+ depth_buffer = 0;
+ }
+ }
+
+ if (stencil_buffer == 0 && (attachment == QGLFramebufferObject::CombinedDepthStencil)) {
+ glGenRenderbuffers(1, &stencil_buffer);
+ Q_ASSERT(!glIsRenderbuffer(stencil_buffer));
+ glBindRenderbuffer(GL_RENDERBUFFER_EXT, stencil_buffer);
+ Q_ASSERT(glIsRenderbuffer(stencil_buffer));
+ if (samples != 0 && glRenderbufferStorageMultisampleEXT) {
+#ifdef QT_OPENGL_ES
+ glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
+ GL_STENCIL_INDEX8_EXT, size.width(), size.height());
+#else
+ glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
+ GL_STENCIL_INDEX, size.width(), size.height());
+#endif
+ } else {
+#ifdef QT_OPENGL_ES
+ glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX8_EXT,
+ size.width(), size.height());
+#else
+ glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX,
+ size.width(), size.height());
+#endif
+ }
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, stencil_buffer);
+ valid = checkFramebufferStatus();
+ if (!valid) {
+ 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 = QGLFramebufferObject::CombinedDepthStencil;
+ } else if (depth_buffer) {
+ fbo_attachment = QGLFramebufferObject::Depth;
+ } else {
+ fbo_attachment = QGLFramebufferObject::NoAttachment;
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
+ if (!valid) {
+ if (color_buffer)
+ glDeleteRenderbuffers(1, &color_buffer);
+ else
+ glDeleteTextures(1, &texture);
+ if (depth_buffer)
+ glDeleteRenderbuffers(1, &depth_buffer);
+ if (stencil_buffer && depth_buffer != stencil_buffer)
+ glDeleteRenderbuffers(1, &stencil_buffer);
+ glDeleteFramebuffers(1, &fbo);
+ fbo_guard.setId(0);
+ }
+ QT_CHECK_GLERROR();
+
+ format.setTextureTarget(target);
+ format.setSamples(int(samples));
+ format.setAttachment(fbo_attachment);
+ format.setInternalTextureFormat(internal_format);
+ format.setMipmap(mipmap);
+}
+
+/*!
+ \class QGLFramebufferObject
+ \brief The QGLFramebufferObject class encapsulates an OpenGL framebuffer object.
+ \since 4.2
+
+ \ingroup painting-3D
+
+ The QGLFramebufferObject 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 QGLFramebufferObject 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
+ QGLFramebufferObject, otherwise initialization will fail.}
+
+ OpenGL framebuffer objects and pbuffers (see
+ \l{QGLPixelBuffer}{QGLPixelBuffer}) 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 QGLFramebufferObject you should take
+ care that the QGLFramebufferObject is created with the CombinedDepthStencil
+ attachment for QPainter to be able to render correctly.
+ Note that you need to create a QGLFramebufferObject 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 QGLFramebufferObject parameter, and set the
+ QGLFramebufferObject::samples() property to a non-zero value.
+
+ When painting to a QGLFramebufferObject 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 QGLContext::blitFramebuffer().
+
+ \section Threading
+
+ As of Qt 4.8, it's possible to draw into a QGLFramebufferObject
+ using a QPainter in a separate thread. Note that OpenGL 2.0 or
+ OpenGL ES 2.0 is required for this to work. Also, under X11, it's
+ necessary to set the Qt::AA_X11InitThreads application attribute.
+
+ \sa {Framebuffer Object Example}
+*/
+
+
+/*!
+ \enum QGLFramebufferObject::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 QGLFramebufferObject::QGLFramebufferObject(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 QGLFramebufferObject, otherwise the initialization
+ will fail.
+
+ \sa size(), texture(), attachment()
+*/
+
+QGLFramebufferObject::QGLFramebufferObject(const QSize &size, GLenum target)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(this, size, NoAttachment, target, DEFAULT_FORMAT);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+QGLFramebufferObject::QGLFramebufferObject(const QSize &size, QMacCompatGLenum target)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(this, size, NoAttachment, target, DEFAULT_FORMAT);
+}
+#endif
+
+/*! \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()
+*/
+QGLFramebufferObject::QGLFramebufferObject(int width, int height, GLenum target)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ 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.
+*/
+
+QGLFramebufferObject::QGLFramebufferObject(const QSize &size, const QGLFramebufferObjectFormat &format)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ 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.
+*/
+
+QGLFramebufferObject::QGLFramebufferObject(int width, int height, const QGLFramebufferObjectFormat &format)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(this, QSize(width, height), format.attachment(), format.textureTarget(),
+ format.internalTextureFormat(), format.samples(), format.mipmap());
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+QGLFramebufferObject::QGLFramebufferObject(int width, int height, QMacCompatGLenum target)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(this, QSize(width, height), NoAttachment, target, DEFAULT_FORMAT);
+}
+#endif
+
+/*! \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()
+*/
+QGLFramebufferObject::QGLFramebufferObject(int width, int height, Attachment attachment,
+ GLenum target, GLenum internal_format)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(this, QSize(width, height), attachment, target, internal_format);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+QGLFramebufferObject::QGLFramebufferObject(int width, int height, Attachment attachment,
+ QMacCompatGLenum target, QMacCompatGLenum internal_format)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(this, QSize(width, height), attachment, target, internal_format);
+}
+#endif
+
+/*! \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()
+*/
+QGLFramebufferObject::QGLFramebufferObject(const QSize &size, Attachment attachment,
+ GLenum target, GLenum internal_format)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(this, size, attachment, target, internal_format);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+QGLFramebufferObject::QGLFramebufferObject(const QSize &size, Attachment attachment,
+ QMacCompatGLenum target, QMacCompatGLenum internal_format)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(this, size, attachment, target, internal_format);
+}
+#endif
+
+/*!
+ \fn QGLFramebufferObject::~QGLFramebufferObject()
+
+ Destroys the framebuffer object and frees any allocated resources.
+*/
+QGLFramebufferObject::~QGLFramebufferObject()
+{
+ Q_D(QGLFramebufferObject);
+ QGL_FUNC_CONTEXT;
+
+ delete d->engine;
+
+ if (isValid() && ctx) {
+ QGLShareContextScope scope(ctx);
+ if (d->texture)
+ glDeleteTextures(1, &d->texture);
+ if (d->color_buffer)
+ glDeleteRenderbuffers(1, &d->color_buffer);
+ if (d->depth_buffer)
+ glDeleteRenderbuffers(1, &d->depth_buffer);
+ if (d->stencil_buffer && d->stencil_buffer != d->depth_buffer)
+ glDeleteRenderbuffers(1, &d->stencil_buffer);
+ GLuint fbo = d->fbo();
+ glDeleteFramebuffers(1, &fbo);
+ }
+}
+
+/*!
+ \fn bool QGLFramebufferObject::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 QGLContext that
+ the framebuffer was created within is destroyed and there are
+ no other shared contexts that can take over ownership of the
+ framebuffer.
+*/
+bool QGLFramebufferObject::isValid() const
+{
+ Q_D(const QGLFramebufferObject);
+ return d->valid && d->fbo_guard.context();
+}
+
+/*!
+ \fn bool QGLFramebufferObject::bind()
+
+ Switches rendering from the default, windowing system provided
+ framebuffer to this framebuffer object.
+ Returns true upon success, false otherwise.
+
+ \sa release()
+*/
+bool QGLFramebufferObject::bind()
+{
+ if (!isValid())
+ return false;
+ Q_D(QGLFramebufferObject);
+ QGL_FUNC_CONTEXT;
+ if (!ctx)
+ return false; // Context no longer exists.
+ const QGLContext *current = QGLContext::currentContext();
+#ifdef QT_DEBUG
+ if (!current ||
+ QGLContextPrivate::contextGroup(current) != QGLContextPrivate::contextGroup(ctx))
+ {
+ qWarning("QGLFramebufferObject::bind() called from incompatible context");
+ }
+#endif
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, d->fbo());
+ d->valid = d->checkFramebufferStatus();
+ if (d->valid && current)
+ current->d_ptr->current_fbo = d->fbo();
+ return d->valid;
+}
+
+/*!
+ \fn bool QGLFramebufferObject::release()
+
+ Switches rendering back to the default, windowing system provided
+ framebuffer.
+ Returns true upon success, false otherwise.
+
+ \sa bind()
+*/
+bool QGLFramebufferObject::release()
+{
+ if (!isValid())
+ return false;
+ QGL_FUNC_CONTEXT;
+ if (!ctx)
+ return false; // Context no longer exists.
+
+ const QGLContext *current = QGLContext::currentContext();
+
+#ifdef QT_DEBUG
+ if (!current ||
+ QGLContextPrivate::contextGroup(current) != QGLContextPrivate::contextGroup(ctx))
+ {
+ qWarning("QGLFramebufferObject::release() called from incompatible context");
+ }
+#endif
+
+ if (current) {
+ current->d_ptr->current_fbo = current->d_ptr->default_fbo;
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, current->d_ptr->default_fbo);
+ }
+
+ return true;
+}
+
+/*!
+ \fn GLuint QGLFramebufferObject::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 QGLFramebufferObject::texture() const
+{
+ Q_D(const QGLFramebufferObject);
+ return d->texture;
+}
+
+/*!
+ \fn QSize QGLFramebufferObject::size() const
+
+ Returns the size of the texture attached to this framebuffer
+ object.
+*/
+QSize QGLFramebufferObject::size() const
+{
+ Q_D(const QGLFramebufferObject);
+ return d->size;
+}
+
+/*!
+ Returns the format of this framebuffer object.
+*/
+QGLFramebufferObjectFormat QGLFramebufferObject::format() const
+{
+ Q_D(const QGLFramebufferObject);
+ return d->format;
+}
+
+/*!
+ \fn QImage QGLFramebufferObject::toImage() const
+
+ Returns the contents of this framebuffer object as a QImage.
+*/
+QImage QGLFramebufferObject::toImage() const
+{
+ Q_D(const QGLFramebufferObject);
+ if (!d->valid)
+ return QImage();
+
+ // qt_gl_read_framebuffer doesn't work on a multisample FBO
+ if (format().samples() != 0) {
+ QGLFramebufferObject temp(size(), QGLFramebufferObjectFormat());
+
+ QRect rect(QPoint(0, 0), size());
+ blitFramebuffer(&temp, rect, const_cast<QGLFramebufferObject *>(this), rect);
+
+ return temp.toImage();
+ }
+
+ bool wasBound = isBound();
+ if (!wasBound)
+ const_cast<QGLFramebufferObject *>(this)->bind();
+ QImage image = qt_gl_read_framebuffer(d->size, format().internalTextureFormat() != GL_RGB, true);
+ if (!wasBound)
+ const_cast<QGLFramebufferObject *>(this)->release();
+
+ return image;
+}
+
+#if !defined(QT_OPENGL_ES_1)
+Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_buffer_2_engine)
+#endif
+
+#ifndef QT_OPENGL_ES_2
+Q_GLOBAL_STATIC(QGLEngineThreadStorage<QOpenGLPaintEngine>, qt_buffer_engine)
+#endif
+
+/*! \reimp */
+QPaintEngine *QGLFramebufferObject::paintEngine() const
+{
+ Q_D(const QGLFramebufferObject);
+ if (d->engine)
+ return d->engine;
+
+#if !defined(QT_OPENGL_ES_1)
+#if !defined (QT_OPENGL_ES_2)
+ if (qt_gl_preferGL2Engine()) {
+#endif
+ QPaintEngine *engine = qt_buffer_2_engine()->engine();
+ if (engine->isActive() && engine->paintDevice() != this) {
+ d->engine = new QGL2PaintEngineEx;
+ return d->engine;
+ }
+ return engine;
+#if !defined (QT_OPENGL_ES_2)
+ }
+#endif
+#endif
+
+#if !defined(QT_OPENGL_ES_2)
+ QPaintEngine *engine = qt_buffer_engine()->engine();
+ if (engine->isActive() && engine->paintDevice() != this) {
+ d->engine = new QOpenGLPaintEngine;
+ return d->engine;
+ }
+ return engine;
+#endif
+}
+
+/*!
+ \fn bool QGLFramebufferObject::bindDefault()
+ \internal
+
+ Switches rendering back to the default, windowing system provided
+ framebuffer.
+ Returns true upon success, false otherwise.
+
+ \sa bind(), release()
+*/
+bool QGLFramebufferObject::bindDefault()
+{
+ QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
+
+ if (ctx) {
+ bool ext_detected = (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject);
+ if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx)))
+ return false;
+
+ ctx->d_ptr->current_fbo = ctx->d_ptr->default_fbo;
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->default_fbo);
+#ifdef QT_DEBUG
+ } else {
+ qWarning("QGLFramebufferObject::bindDefault() called without current context.");
+#endif
+ }
+
+ return ctx != 0;
+}
+
+/*!
+ \fn bool QGLFramebufferObject::hasOpenGLFramebufferObjects()
+
+ Returns true if the OpenGL \c{GL_EXT_framebuffer_object} extension
+ is present on this system; otherwise returns false.
+*/
+bool QGLFramebufferObject::hasOpenGLFramebufferObjects()
+{
+ return (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject);
+}
+
+/*!
+ \since 4.4
+
+ Draws the given texture, \a textureId, to the given target rectangle,
+ \a target, in OpenGL model space. The \a textureTarget should be a 2D
+ texture target.
+
+ The framebuffer object should be bound when calling this function.
+
+ Equivalent to the corresponding QGLContext::drawTexture().
+*/
+void QGLFramebufferObject::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
+{
+ const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(target, textureId, textureTarget);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLFramebufferObject::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(target, textureId, textureTarget);
+}
+#endif
+
+/*!
+ \since 4.4
+
+ Draws the given texture, \a textureId, at the given \a point in OpenGL
+ model space. The \a textureTarget should be a 2D texture target.
+
+ The framebuffer object should be bound when calling this function.
+
+ Equivalent to the corresponding QGLContext::drawTexture().
+*/
+void QGLFramebufferObject::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
+{
+ const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(point, textureId, textureTarget);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLFramebufferObject::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(point, textureId, textureTarget);
+}
+#endif
+
+/*! \reimp */
+int QGLFramebufferObject::metric(PaintDeviceMetric metric) const
+{
+ Q_D(const QGLFramebufferObject);
+
+ 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("QGLFramebufferObject::metric(), Unhandled metric type: %d.\n", metric);
+ break;
+ }
+ return 0;
+}
+
+/*!
+ \fn GLuint QGLFramebufferObject::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 QGLFramebufferObject::handle() const
+{
+ Q_D(const QGLFramebufferObject);
+ return d->fbo();
+}
+
+/*! \fn int QGLFramebufferObject::devType() const
+ \internal
+*/
+
+
+/*!
+ Returns the status of the depth and stencil buffers attached to
+ this framebuffer object.
+*/
+
+QGLFramebufferObject::Attachment QGLFramebufferObject::attachment() const
+{
+ Q_D(const QGLFramebufferObject);
+ 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 QGLFramebufferObject::isBound() const
+{
+ Q_D(const QGLFramebufferObject);
+ const QGLContext *current = QGLContext::currentContext();
+ return current ? current->d_ptr->current_fbo == d->fbo() : false;
+}
+
+/*!
+ \fn bool QGLFramebufferObject::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 QGLFramebufferObject::hasOpenGLFramebufferBlit()
+{
+ return (QGLExtensions::glExtensions() & QGLExtensions::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 QGLFramebufferObject::blitFramebuffer(QGLFramebufferObject *target, const QRect &targetRect,
+ QGLFramebufferObject *source, const QRect &sourceRect,
+ GLbitfield buffers,
+ GLenum filter)
+{
+ if (!(QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit))
+ return;
+
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (!ctx)
+ return;
+
+ const int height = ctx->device()->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();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, source ? source->handle() : 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, target ? target->handle() : 0);
+
+ glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
+ tx0, ty0, tx1, ty1,
+ buffers, filter);
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglframebufferobject.h b/src/opengl/qglframebufferobject.h
new file mode 100644
index 0000000000..72ff27966f
--- /dev/null
+++ b/src/opengl/qglframebufferobject.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLFRAMEBUFFEROBJECT_H
+#define QGLFRAMEBUFFEROBJECT_H
+
+#include <QtOpenGL/qgl.h>
+#include <QtGui/qpaintdevice.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLFramebufferObjectPrivate;
+class QGLFramebufferObjectFormat;
+
+class Q_OPENGL_EXPORT QGLFramebufferObject : public QPaintDevice
+{
+ Q_DECLARE_PRIVATE(QGLFramebufferObject)
+public:
+ enum Attachment {
+ NoAttachment,
+ CombinedDepthStencil,
+ Depth
+ };
+
+ QGLFramebufferObject(const QSize &size, GLenum target = GL_TEXTURE_2D);
+ QGLFramebufferObject(int width, int height, GLenum target = GL_TEXTURE_2D);
+#if !defined(QT_OPENGL_ES) || defined(Q_QDOC)
+ QGLFramebufferObject(const QSize &size, Attachment attachment,
+ GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA8);
+ QGLFramebufferObject(int width, int height, Attachment attachment,
+ GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA8);
+#else
+ QGLFramebufferObject(const QSize &size, Attachment attachment,
+ GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA);
+ QGLFramebufferObject(int width, int height, Attachment attachment,
+ GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA);
+#endif
+
+ QGLFramebufferObject(const QSize &size, const QGLFramebufferObjectFormat &format);
+ QGLFramebufferObject(int width, int height, const QGLFramebufferObjectFormat &format);
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ QGLFramebufferObject(const QSize &size, QMacCompatGLenum target = GL_TEXTURE_2D);
+ QGLFramebufferObject(int width, int height, QMacCompatGLenum target = GL_TEXTURE_2D);
+
+ QGLFramebufferObject(const QSize &size, Attachment attachment,
+ QMacCompatGLenum target = GL_TEXTURE_2D, QMacCompatGLenum internal_format = GL_RGBA8);
+ QGLFramebufferObject(int width, int height, Attachment attachment,
+ QMacCompatGLenum target = GL_TEXTURE_2D, QMacCompatGLenum internal_format = GL_RGBA8);
+#endif
+
+ virtual ~QGLFramebufferObject();
+
+ QGLFramebufferObjectFormat 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();
+
+ void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ void drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+#endif
+
+ static bool hasOpenGLFramebufferBlit();
+ static void blitFramebuffer(QGLFramebufferObject *target, const QRect &targetRect,
+ QGLFramebufferObject *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(QGLFramebufferObject)
+ QScopedPointer<QGLFramebufferObjectPrivate> d_ptr;
+ friend class QGLPaintDevice;
+ friend class QGLFBOGLPaintDevice;
+};
+
+class QGLFramebufferObjectFormatPrivate;
+class Q_OPENGL_EXPORT QGLFramebufferObjectFormat
+{
+public:
+ QGLFramebufferObjectFormat();
+ QGLFramebufferObjectFormat(const QGLFramebufferObjectFormat &other);
+ QGLFramebufferObjectFormat &operator=(const QGLFramebufferObjectFormat &other);
+ ~QGLFramebufferObjectFormat();
+
+ void setSamples(int samples);
+ int samples() const;
+
+ void setMipmap(bool enabled);
+ bool mipmap() const;
+
+ void setAttachment(QGLFramebufferObject::Attachment attachment);
+ QGLFramebufferObject::Attachment attachment() const;
+
+ void setTextureTarget(GLenum target);
+ GLenum textureTarget() const;
+
+ void setInternalTextureFormat(GLenum internalTextureFormat);
+ GLenum internalTextureFormat() const;
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ void setTextureTarget(QMacCompatGLenum target);
+ void setInternalTextureFormat(QMacCompatGLenum internalTextureFormat);
+#endif
+
+ bool operator==(const QGLFramebufferObjectFormat& other) const;
+ bool operator!=(const QGLFramebufferObjectFormat& other) const;
+
+private:
+ QGLFramebufferObjectFormatPrivate *d;
+
+ void detach();
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+#endif // QGLFRAMEBUFFEROBJECT_H
diff --git a/src/opengl/qglframebufferobject_p.h b/src/opengl/qglframebufferobject_p.h
new file mode 100644
index 0000000000..4d194693ea
--- /dev/null
+++ b/src/opengl/qglframebufferobject_p.h
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLFRAMEBUFFEROBJECT_P_H
+#define QGLFRAMEBUFFEROBJECT_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 <qglframebufferobject.h>
+#include <private/qglpaintdevice_p.h>
+#include <private/qgl_p.h>
+
+QT_END_INCLUDE_NAMESPACE
+
+#ifndef QT_OPENGL_ES
+#define DEFAULT_FORMAT GL_RGBA8
+#else
+#define DEFAULT_FORMAT GL_RGBA
+#endif
+
+class QGLFramebufferObjectFormatPrivate
+{
+public:
+ QGLFramebufferObjectFormatPrivate()
+ : ref(1),
+ samples(0),
+ attachment(QGLFramebufferObject::NoAttachment),
+ target(GL_TEXTURE_2D),
+ internal_format(DEFAULT_FORMAT),
+ mipmap(false)
+ {
+ }
+ QGLFramebufferObjectFormatPrivate
+ (const QGLFramebufferObjectFormatPrivate *other)
+ : ref(1),
+ samples(other->samples),
+ attachment(other->attachment),
+ target(other->target),
+ internal_format(other->internal_format),
+ mipmap(other->mipmap)
+ {
+ }
+ bool equals(const QGLFramebufferObjectFormatPrivate *other)
+ {
+ return samples == other->samples &&
+ attachment == other->attachment &&
+ target == other->target &&
+ internal_format == other->internal_format &&
+ mipmap == other->mipmap;
+ }
+
+ QAtomicInt ref;
+ int samples;
+ QGLFramebufferObject::Attachment attachment;
+ GLenum target;
+ GLenum internal_format;
+ uint mipmap : 1;
+};
+
+class QGLFBOGLPaintDevice : public QGLPaintDevice
+{
+public:
+ virtual QPaintEngine* paintEngine() const {return fbo->paintEngine();}
+ virtual QSize size() const {return fbo->size();}
+ virtual QGLContext* context() const;
+ virtual QGLFormat format() const {return fboFormat;}
+ virtual bool alphaRequested() const { return reqAlpha; }
+
+ void setFBO(QGLFramebufferObject* f,
+ QGLFramebufferObject::Attachment attachment);
+
+private:
+ QGLFramebufferObject* fbo;
+ QGLFormat fboFormat;
+ bool wasBound;
+ bool reqAlpha;
+};
+
+class QGLFramebufferObjectPrivate
+{
+public:
+ QGLFramebufferObjectPrivate() : fbo_guard(0), texture(0), depth_buffer(0), stencil_buffer(0)
+ , color_buffer(0), valid(false), engine(0) {}
+ ~QGLFramebufferObjectPrivate() {}
+
+ void init(QGLFramebufferObject *q, const QSize& sz,
+ QGLFramebufferObject::Attachment attachment,
+ GLenum internal_format, GLenum texture_target,
+ GLint samples = 0, bool mipmap = false);
+ bool checkFramebufferStatus() const;
+ QGLSharedResourceGuard fbo_guard;
+ GLuint texture;
+ GLuint depth_buffer;
+ GLuint stencil_buffer;
+ GLuint color_buffer;
+ GLenum target;
+ QSize size;
+ QGLFramebufferObjectFormat format;
+ uint valid : 1;
+ QGLFramebufferObject::Attachment fbo_attachment;
+ mutable QPaintEngine *engine;
+ QGLFBOGLPaintDevice glDevice;
+
+ inline GLuint fbo() const { return fbo_guard.id(); }
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QGLFRAMEBUFFEROBJECT_P_H
diff --git a/src/opengl/qglfunctions.cpp b/src/opengl/qglfunctions.cpp
new file mode 100644
index 0000000000..be8219a07f
--- /dev/null
+++ b/src/opengl/qglfunctions.cpp
@@ -0,0 +1,3705 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglfunctions.h"
+#include "qgl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLFunctions
+ \brief The QGLFunctions 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.
+
+ QGLFunctions 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 QGLFunctions is by
+ direct inheritance:
+
+ \code
+ class MyGLWidget : public QGLWidget, protected QGLFunctions
+ {
+ Q_OBJECT
+ public:
+ MyGLWidget(QWidget *parent = 0) : QGLWidget(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
+
+ QGLFunctions can also be used directly for ad-hoc invocation
+ of OpenGL/ES 2.0 functions on all platforms:
+
+ \code
+ QGLFunctions glFuncs(QGLContext::currentContext());
+ glFuncs.glActiveTexture(GL_TEXTURE1);
+ \endcode
+
+ QGLFunctions 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 QGLFunctions 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
+ QGLFunctions funcs(QGLContext::currentContext());
+ bool npot = funcs.hasOpenGLFeature(QGLFunctions::NPOTTextures);
+ \endcode
+*/
+
+/*!
+ \enum QGLFunctions::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 QGLFunctionsPrivateEx : public QGLFunctionsPrivate
+{
+ QGLFunctionsPrivateEx(const QGLContext *context = 0)
+ : QGLFunctionsPrivate(context)
+ , m_features(-1) {}
+
+ int m_features;
+};
+
+#if QT_VERSION >= 0x040800
+Q_GLOBAL_STATIC(QGLContextGroupResource<QGLFunctionsPrivateEx>, qt_gl_functions_resource)
+#else
+static void qt_gl_functions_free(void *data)
+{
+ delete reinterpret_cast<QGLFunctionsPrivateEx *>(data);
+}
+
+Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_gl_functions_resource, (qt_gl_functions_free))
+#endif
+static QGLFunctionsPrivateEx *qt_gl_functions(const QGLContext *context = 0)
+{
+ if (!context)
+ context = QGLContext::currentContext();
+ Q_ASSERT(context);
+ QGLFunctionsPrivateEx *funcs =
+ reinterpret_cast<QGLFunctionsPrivateEx *>
+ (qt_gl_functions_resource()->value(context));
+#if QT_VERSION < 0x040800
+ if (!funcs) {
+ funcs = new QGLFunctionsPrivateEx();
+ qt_gl_functions_resource()->insert(context, funcs);
+ }
+#endif
+ return funcs;
+}
+
+/*!
+ Constructs a default function resolver. The resolver cannot
+ be used until initializeGLFunctions() is called to specify
+ the context.
+
+ \sa initializeGLFunctions()
+*/
+QGLFunctions::QGLFunctions()
+ : d_ptr(0)
+{
+}
+
+/*!
+ Constructs a function resolver for \a context. If \a context
+ is null, then the resolver will be created for the current QGLContext.
+
+ 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()
+*/
+QGLFunctions::QGLFunctions(const QGLContext *context)
+ : d_ptr(qt_gl_functions(context))
+{
+}
+
+/*!
+ \fn QGLFunctions::~QGLFunctions()
+
+ Destroys this function resolver.
+*/
+
+static int qt_gl_resolve_features()
+{
+#if defined(QT_OPENGL_ES_2)
+ return QGLFunctions::Multitexture |
+ QGLFunctions::Shaders |
+ QGLFunctions::Buffers |
+ QGLFunctions::Framebuffers |
+ QGLFunctions::BlendColor |
+ QGLFunctions::BlendEquation |
+ QGLFunctions::BlendEquationSeparate |
+ QGLFunctions::BlendFuncSeparate |
+ QGLFunctions::BlendSubtract |
+ QGLFunctions::CompressedTextures |
+ QGLFunctions::Multisample |
+ QGLFunctions::StencilSeparate |
+ QGLFunctions::NPOTTextures;
+#elif defined(QT_OPENGL_ES)
+ int features = QGLFunctions::Multitexture |
+ QGLFunctions::Buffers |
+ QGLFunctions::CompressedTextures |
+ QGLFunctions::Multisample;
+ QGLExtensionMatcher extensions;
+ if (extensions.match("GL_OES_framebuffer_object"))
+ features |= QGLFunctions::Framebuffers;
+ if (extensions.match("GL_OES_blend_equation_separate"))
+ features |= QGLFunctions::BlendEquationSeparate;
+ if (extensions.match("GL_OES_blend_func_separate"))
+ features |= QGLFunctions::BlendFuncSeparate;
+ if (extensions.match("GL_OES_blend_subtract"))
+ features |= QGLFunctions::BlendSubtract;
+ if (extensions.match("GL_OES_texture_npot"))
+ features |= QGLFunctions::NPOTTextures;
+ return features;
+#else
+ int features = 0;
+ QGLFormat::OpenGLVersionFlags versions = QGLFormat::openGLVersionFlags();
+ QGLExtensionMatcher extensions;
+
+ // Recognize features by extension name.
+ if (extensions.match("GL_ARB_multitexture"))
+ features |= QGLFunctions::Multitexture;
+ if (extensions.match("GL_ARB_shader_objects"))
+ features |= QGLFunctions::Shaders;
+ if (extensions.match("GL_EXT_framebuffer_object") ||
+ extensions.match("GL_ARB_framebuffer_object"))
+ features |= QGLFunctions::Framebuffers;
+ if (extensions.match("GL_EXT_blend_color"))
+ features |= QGLFunctions::BlendColor;
+ if (extensions.match("GL_EXT_blend_equation_separate"))
+ features |= QGLFunctions::BlendEquationSeparate;
+ if (extensions.match("GL_EXT_blend_func_separate"))
+ features |= QGLFunctions::BlendFuncSeparate;
+ if (extensions.match("GL_EXT_blend_subtract"))
+ features |= QGLFunctions::BlendSubtract;
+ if (extensions.match("GL_ARB_texture_compression"))
+ features |= QGLFunctions::CompressedTextures;
+ if (extensions.match("GL_ARB_multisample"))
+ features |= QGLFunctions::Multisample;
+ if (extensions.match("GL_ARB_texture_non_power_of_two"))
+ features |= QGLFunctions::NPOTTextures;
+
+ // Recognize features by minimum OpenGL version.
+ if (versions & QGLFormat::OpenGL_Version_1_2) {
+ features |= QGLFunctions::BlendColor |
+ QGLFunctions::BlendEquation;
+ }
+ if (versions & QGLFormat::OpenGL_Version_1_3) {
+ features |= QGLFunctions::Multitexture |
+ QGLFunctions::CompressedTextures |
+ QGLFunctions::Multisample;
+ }
+ if (versions & QGLFormat::OpenGL_Version_1_4)
+ features |= QGLFunctions::BlendFuncSeparate;
+ if (versions & QGLFormat::OpenGL_Version_1_5)
+ features |= QGLFunctions::Buffers;
+ if (versions & QGLFormat::OpenGL_Version_2_0) {
+ features |= QGLFunctions::Shaders |
+ QGLFunctions::StencilSeparate |
+ QGLFunctions::BlendEquationSeparate |
+ QGLFunctions::NPOTTextures;
+ }
+ return features;
+#endif
+}
+
+/*!
+ Returns the set of features that are present on this system's
+ OpenGL implementation.
+
+ It is assumed that the QGLContext associated with this function
+ resolver is current.
+
+ \sa hasOpenGLFeature()
+*/
+QGLFunctions::OpenGLFeatures QGLFunctions::openGLFeatures() const
+{
+ QGLFunctionsPrivateEx *d = static_cast<QGLFunctionsPrivateEx *>(d_ptr);
+ if (!d)
+ return 0;
+ if (d->m_features == -1)
+ d->m_features = qt_gl_resolve_features();
+ return QGLFunctions::OpenGLFeatures(d->m_features);
+}
+
+/*!
+ Returns true if \a feature is present on this system's OpenGL
+ implementation; false otherwise.
+
+ It is assumed that the QGLContext associated with this function
+ resolver is current.
+
+ \sa openGLFeatures()
+*/
+bool QGLFunctions::hasOpenGLFeature(QGLFunctions::OpenGLFeature feature) const
+{
+ QGLFunctionsPrivateEx *d = static_cast<QGLFunctionsPrivateEx *>(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;
+}
+
+/*!
+ Initializes GL function resolution for \a context. If \a context
+ is null, then the current QGLContext will be used.
+
+ After calling this function, the QGLFunctions object can only be
+ used with \a context and other contexts that share with it.
+ Call initializeGLFunctions() again to change the object's context
+ association.
+*/
+void QGLFunctions::initializeGLFunctions(const QGLContext *context)
+{
+ d_ptr = qt_gl_functions(context);
+}
+
+/*!
+ \fn void QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::glBufferData(GLenum target, qgl_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 QGLFunctions::glBufferSubData(GLenum target, qgl_GLintptr offset, qgl_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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 int QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 int QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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 QGLFunctions::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.
+*/
+
+#ifndef QT_OPENGL_ES_2
+
+static void qglfResolveActiveTexture(GLenum texture)
+{
+ typedef void (QGLF_APIENTRYP type_glActiveTexture)(GLenum texture);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->activeTexture = (type_glActiveTexture)
+ context->getProcAddress(QLatin1String("glActiveTexture"));
+ if (!funcs->activeTexture) {
+ funcs->activeTexture = (type_glActiveTexture)
+ context->getProcAddress(QLatin1String("glActiveTextureARB"));
+ }
+
+ if (funcs->activeTexture)
+ funcs->activeTexture(texture);
+ else
+ funcs->activeTexture = qglfResolveActiveTexture;
+}
+
+static void qglfResolveAttachShader(GLuint program, GLuint shader)
+{
+ typedef void (QGLF_APIENTRYP type_glAttachShader)(GLuint program, GLuint shader);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->attachShader = (type_glAttachShader)
+ context->getProcAddress(QLatin1String("glAttachShader"));
+ if (!funcs->attachShader) {
+ funcs->attachShader = (type_glAttachShader)
+ context->getProcAddress(QLatin1String("glAttachObjectARB"));
+ }
+
+ if (funcs->attachShader)
+ funcs->attachShader(program, shader);
+ else
+ funcs->attachShader = qglfResolveAttachShader;
+}
+
+static void qglfResolveBindAttribLocation(GLuint program, GLuint index, const char* name)
+{
+ typedef void (QGLF_APIENTRYP type_glBindAttribLocation)(GLuint program, GLuint index, const char* name);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->bindAttribLocation = (type_glBindAttribLocation)
+ context->getProcAddress(QLatin1String("glBindAttribLocation"));
+ if (!funcs->bindAttribLocation) {
+ funcs->bindAttribLocation = (type_glBindAttribLocation)
+ context->getProcAddress(QLatin1String("glBindAttribLocationARB"));
+ }
+
+ if (funcs->bindAttribLocation)
+ funcs->bindAttribLocation(program, index, name);
+ else
+ funcs->bindAttribLocation = qglfResolveBindAttribLocation;
+}
+
+static void qglfResolveBindBuffer(GLenum target, GLuint buffer)
+{
+ typedef void (QGLF_APIENTRYP type_glBindBuffer)(GLenum target, GLuint buffer);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->bindBuffer = (type_glBindBuffer)
+ context->getProcAddress(QLatin1String("glBindBuffer"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->bindBuffer) {
+ funcs->bindBuffer = (type_glBindBuffer)
+ context->getProcAddress(QLatin1String("glBindBufferOES"));
+ }
+#endif
+ if (!funcs->bindBuffer) {
+ funcs->bindBuffer = (type_glBindBuffer)
+ context->getProcAddress(QLatin1String("glBindBufferEXT"));
+ }
+ if (!funcs->bindBuffer) {
+ funcs->bindBuffer = (type_glBindBuffer)
+ context->getProcAddress(QLatin1String("glBindBufferARB"));
+ }
+
+ if (funcs->bindBuffer)
+ funcs->bindBuffer(target, buffer);
+ else
+ funcs->bindBuffer = qglfResolveBindBuffer;
+}
+
+static void qglfResolveBindFramebuffer(GLenum target, GLuint framebuffer)
+{
+ typedef void (QGLF_APIENTRYP type_glBindFramebuffer)(GLenum target, GLuint framebuffer);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->bindFramebuffer = (type_glBindFramebuffer)
+ context->getProcAddress(QLatin1String("glBindFramebuffer"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->bindFramebuffer) {
+ funcs->bindFramebuffer = (type_glBindFramebuffer)
+ context->getProcAddress(QLatin1String("glBindFramebufferOES"));
+ }
+#endif
+ if (!funcs->bindFramebuffer) {
+ funcs->bindFramebuffer = (type_glBindFramebuffer)
+ context->getProcAddress(QLatin1String("glBindFramebufferEXT"));
+ }
+ if (!funcs->bindFramebuffer) {
+ funcs->bindFramebuffer = (type_glBindFramebuffer)
+ context->getProcAddress(QLatin1String("glBindFramebufferARB"));
+ }
+
+ if (funcs->bindFramebuffer)
+ funcs->bindFramebuffer(target, framebuffer);
+ else
+ funcs->bindFramebuffer = qglfResolveBindFramebuffer;
+}
+
+static void qglfResolveBindRenderbuffer(GLenum target, GLuint renderbuffer)
+{
+ typedef void (QGLF_APIENTRYP type_glBindRenderbuffer)(GLenum target, GLuint renderbuffer);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->bindRenderbuffer = (type_glBindRenderbuffer)
+ context->getProcAddress(QLatin1String("glBindRenderbuffer"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->bindRenderbuffer) {
+ funcs->bindRenderbuffer = (type_glBindRenderbuffer)
+ context->getProcAddress(QLatin1String("glBindRenderbufferOES"));
+ }
+#endif
+ if (!funcs->bindRenderbuffer) {
+ funcs->bindRenderbuffer = (type_glBindRenderbuffer)
+ context->getProcAddress(QLatin1String("glBindRenderbufferEXT"));
+ }
+ if (!funcs->bindRenderbuffer) {
+ funcs->bindRenderbuffer = (type_glBindRenderbuffer)
+ context->getProcAddress(QLatin1String("glBindRenderbufferARB"));
+ }
+
+ if (funcs->bindRenderbuffer)
+ funcs->bindRenderbuffer(target, renderbuffer);
+ else
+ funcs->bindRenderbuffer = qglfResolveBindRenderbuffer;
+}
+
+static void qglfResolveBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
+{
+ typedef void (QGLF_APIENTRYP type_glBlendColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->blendColor = (type_glBlendColor)
+ context->getProcAddress(QLatin1String("glBlendColor"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->blendColor) {
+ funcs->blendColor = (type_glBlendColor)
+ context->getProcAddress(QLatin1String("glBlendColorOES"));
+ }
+#endif
+ if (!funcs->blendColor) {
+ funcs->blendColor = (type_glBlendColor)
+ context->getProcAddress(QLatin1String("glBlendColorEXT"));
+ }
+ if (!funcs->blendColor) {
+ funcs->blendColor = (type_glBlendColor)
+ context->getProcAddress(QLatin1String("glBlendColorARB"));
+ }
+
+ if (funcs->blendColor)
+ funcs->blendColor(red, green, blue, alpha);
+ else
+ funcs->blendColor = qglfResolveBlendColor;
+}
+
+static void qglfResolveBlendEquation(GLenum mode)
+{
+ typedef void (QGLF_APIENTRYP type_glBlendEquation)(GLenum mode);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->blendEquation = (type_glBlendEquation)
+ context->getProcAddress(QLatin1String("glBlendEquation"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->blendEquation) {
+ funcs->blendEquation = (type_glBlendEquation)
+ context->getProcAddress(QLatin1String("glBlendEquationOES"));
+ }
+#endif
+ if (!funcs->blendEquation) {
+ funcs->blendEquation = (type_glBlendEquation)
+ context->getProcAddress(QLatin1String("glBlendEquationEXT"));
+ }
+ if (!funcs->blendEquation) {
+ funcs->blendEquation = (type_glBlendEquation)
+ context->getProcAddress(QLatin1String("glBlendEquationARB"));
+ }
+
+ if (funcs->blendEquation)
+ funcs->blendEquation(mode);
+ else
+ funcs->blendEquation = qglfResolveBlendEquation;
+}
+
+static void qglfResolveBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha)
+{
+ typedef void (QGLF_APIENTRYP type_glBlendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->blendEquationSeparate = (type_glBlendEquationSeparate)
+ context->getProcAddress(QLatin1String("glBlendEquationSeparate"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->blendEquationSeparate) {
+ funcs->blendEquationSeparate = (type_glBlendEquationSeparate)
+ context->getProcAddress(QLatin1String("glBlendEquationSeparateOES"));
+ }
+#endif
+ if (!funcs->blendEquationSeparate) {
+ funcs->blendEquationSeparate = (type_glBlendEquationSeparate)
+ context->getProcAddress(QLatin1String("glBlendEquationSeparateEXT"));
+ }
+ if (!funcs->blendEquationSeparate) {
+ funcs->blendEquationSeparate = (type_glBlendEquationSeparate)
+ context->getProcAddress(QLatin1String("glBlendEquationSeparateARB"));
+ }
+
+ if (funcs->blendEquationSeparate)
+ funcs->blendEquationSeparate(modeRGB, modeAlpha);
+ else
+ funcs->blendEquationSeparate = qglfResolveBlendEquationSeparate;
+}
+
+static void qglfResolveBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)
+{
+ typedef void (QGLF_APIENTRYP type_glBlendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->blendFuncSeparate = (type_glBlendFuncSeparate)
+ context->getProcAddress(QLatin1String("glBlendFuncSeparate"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->blendFuncSeparate) {
+ funcs->blendFuncSeparate = (type_glBlendFuncSeparate)
+ context->getProcAddress(QLatin1String("glBlendFuncSeparateOES"));
+ }
+#endif
+ if (!funcs->blendFuncSeparate) {
+ funcs->blendFuncSeparate = (type_glBlendFuncSeparate)
+ context->getProcAddress(QLatin1String("glBlendFuncSeparateEXT"));
+ }
+ if (!funcs->blendFuncSeparate) {
+ funcs->blendFuncSeparate = (type_glBlendFuncSeparate)
+ context->getProcAddress(QLatin1String("glBlendFuncSeparateARB"));
+ }
+
+ if (funcs->blendFuncSeparate)
+ funcs->blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
+ else
+ funcs->blendFuncSeparate = qglfResolveBlendFuncSeparate;
+}
+
+static void qglfResolveBufferData(GLenum target, qgl_GLsizeiptr size, const void* data, GLenum usage)
+{
+ typedef void (QGLF_APIENTRYP type_glBufferData)(GLenum target, qgl_GLsizeiptr size, const void* data, GLenum usage);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->bufferData = (type_glBufferData)
+ context->getProcAddress(QLatin1String("glBufferData"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->bufferData) {
+ funcs->bufferData = (type_glBufferData)
+ context->getProcAddress(QLatin1String("glBufferDataOES"));
+ }
+#endif
+ if (!funcs->bufferData) {
+ funcs->bufferData = (type_glBufferData)
+ context->getProcAddress(QLatin1String("glBufferDataEXT"));
+ }
+ if (!funcs->bufferData) {
+ funcs->bufferData = (type_glBufferData)
+ context->getProcAddress(QLatin1String("glBufferDataARB"));
+ }
+
+ if (funcs->bufferData)
+ funcs->bufferData(target, size, data, usage);
+ else
+ funcs->bufferData = qglfResolveBufferData;
+}
+
+static void qglfResolveBufferSubData(GLenum target, qgl_GLintptr offset, qgl_GLsizeiptr size, const void* data)
+{
+ typedef void (QGLF_APIENTRYP type_glBufferSubData)(GLenum target, qgl_GLintptr offset, qgl_GLsizeiptr size, const void* data);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->bufferSubData = (type_glBufferSubData)
+ context->getProcAddress(QLatin1String("glBufferSubData"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->bufferSubData) {
+ funcs->bufferSubData = (type_glBufferSubData)
+ context->getProcAddress(QLatin1String("glBufferSubDataOES"));
+ }
+#endif
+ if (!funcs->bufferSubData) {
+ funcs->bufferSubData = (type_glBufferSubData)
+ context->getProcAddress(QLatin1String("glBufferSubDataEXT"));
+ }
+ if (!funcs->bufferSubData) {
+ funcs->bufferSubData = (type_glBufferSubData)
+ context->getProcAddress(QLatin1String("glBufferSubDataARB"));
+ }
+
+ if (funcs->bufferSubData)
+ funcs->bufferSubData(target, offset, size, data);
+ else
+ funcs->bufferSubData = qglfResolveBufferSubData;
+}
+
+static GLenum qglfResolveCheckFramebufferStatus(GLenum target)
+{
+ typedef GLenum (QGLF_APIENTRYP type_glCheckFramebufferStatus)(GLenum target);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->checkFramebufferStatus = (type_glCheckFramebufferStatus)
+ context->getProcAddress(QLatin1String("glCheckFramebufferStatus"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->checkFramebufferStatus) {
+ funcs->checkFramebufferStatus = (type_glCheckFramebufferStatus)
+ context->getProcAddress(QLatin1String("glCheckFramebufferStatusOES"));
+ }
+#endif
+ if (!funcs->checkFramebufferStatus) {
+ funcs->checkFramebufferStatus = (type_glCheckFramebufferStatus)
+ context->getProcAddress(QLatin1String("glCheckFramebufferStatusEXT"));
+ }
+ if (!funcs->checkFramebufferStatus) {
+ funcs->checkFramebufferStatus = (type_glCheckFramebufferStatus)
+ context->getProcAddress(QLatin1String("glCheckFramebufferStatusARB"));
+ }
+
+ if (funcs->checkFramebufferStatus)
+ return funcs->checkFramebufferStatus(target);
+ funcs->checkFramebufferStatus = qglfResolveCheckFramebufferStatus;
+ return GLenum(0);
+}
+
+static void qglfResolveCompileShader(GLuint shader)
+{
+ typedef void (QGLF_APIENTRYP type_glCompileShader)(GLuint shader);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->compileShader = (type_glCompileShader)
+ context->getProcAddress(QLatin1String("glCompileShader"));
+ if (!funcs->compileShader) {
+ funcs->compileShader = (type_glCompileShader)
+ context->getProcAddress(QLatin1String("glCompileShader"));
+ }
+
+ if (funcs->compileShader)
+ funcs->compileShader(shader);
+ else
+ funcs->compileShader = qglfResolveCompileShader;
+}
+
+static void qglfResolveCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data)
+{
+ typedef void (QGLF_APIENTRYP type_glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->compressedTexImage2D = (type_glCompressedTexImage2D)
+ context->getProcAddress(QLatin1String("glCompressedTexImage2D"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->compressedTexImage2D) {
+ funcs->compressedTexImage2D = (type_glCompressedTexImage2D)
+ context->getProcAddress(QLatin1String("glCompressedTexImage2DOES"));
+ }
+#endif
+ if (!funcs->compressedTexImage2D) {
+ funcs->compressedTexImage2D = (type_glCompressedTexImage2D)
+ context->getProcAddress(QLatin1String("glCompressedTexImage2DEXT"));
+ }
+ if (!funcs->compressedTexImage2D) {
+ funcs->compressedTexImage2D = (type_glCompressedTexImage2D)
+ context->getProcAddress(QLatin1String("glCompressedTexImage2DARB"));
+ }
+
+ if (funcs->compressedTexImage2D)
+ funcs->compressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data);
+ else
+ funcs->compressedTexImage2D = qglfResolveCompressedTexImage2D;
+}
+
+static void qglfResolveCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data)
+{
+ typedef void (QGLF_APIENTRYP type_glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->compressedTexSubImage2D = (type_glCompressedTexSubImage2D)
+ context->getProcAddress(QLatin1String("glCompressedTexSubImage2D"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->compressedTexSubImage2D) {
+ funcs->compressedTexSubImage2D = (type_glCompressedTexSubImage2D)
+ context->getProcAddress(QLatin1String("glCompressedTexSubImage2DOES"));
+ }
+#endif
+ if (!funcs->compressedTexSubImage2D) {
+ funcs->compressedTexSubImage2D = (type_glCompressedTexSubImage2D)
+ context->getProcAddress(QLatin1String("glCompressedTexSubImage2DEXT"));
+ }
+ if (!funcs->compressedTexSubImage2D) {
+ funcs->compressedTexSubImage2D = (type_glCompressedTexSubImage2D)
+ context->getProcAddress(QLatin1String("glCompressedTexSubImage2DARB"));
+ }
+
+ if (funcs->compressedTexSubImage2D)
+ funcs->compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data);
+ else
+ funcs->compressedTexSubImage2D = qglfResolveCompressedTexSubImage2D;
+}
+
+static GLuint qglfResolveCreateProgram()
+{
+ typedef GLuint (QGLF_APIENTRYP type_glCreateProgram)();
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->createProgram = (type_glCreateProgram)
+ context->getProcAddress(QLatin1String("glCreateProgram"));
+ if (!funcs->createProgram) {
+ funcs->createProgram = (type_glCreateProgram)
+ context->getProcAddress(QLatin1String("glCreateProgramObjectARB"));
+ }
+
+ if (funcs->createProgram)
+ return funcs->createProgram();
+ funcs->createProgram = qglfResolveCreateProgram;
+ return GLuint(0);
+}
+
+static GLuint qglfResolveCreateShader(GLenum type)
+{
+ typedef GLuint (QGLF_APIENTRYP type_glCreateShader)(GLenum type);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->createShader = (type_glCreateShader)
+ context->getProcAddress(QLatin1String("glCreateShader"));
+ if (!funcs->createShader) {
+ funcs->createShader = (type_glCreateShader)
+ context->getProcAddress(QLatin1String("glCreateShaderObjectARB"));
+ }
+
+ if (funcs->createShader)
+ return funcs->createShader(type);
+ funcs->createShader = qglfResolveCreateShader;
+ return GLuint(0);
+}
+
+static void qglfResolveDeleteBuffers(GLsizei n, const GLuint* buffers)
+{
+ typedef void (QGLF_APIENTRYP type_glDeleteBuffers)(GLsizei n, const GLuint* buffers);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->deleteBuffers = (type_glDeleteBuffers)
+ context->getProcAddress(QLatin1String("glDeleteBuffers"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->deleteBuffers) {
+ funcs->deleteBuffers = (type_glDeleteBuffers)
+ context->getProcAddress(QLatin1String("glDeleteBuffersOES"));
+ }
+#endif
+ if (!funcs->deleteBuffers) {
+ funcs->deleteBuffers = (type_glDeleteBuffers)
+ context->getProcAddress(QLatin1String("glDeleteBuffersEXT"));
+ }
+ if (!funcs->deleteBuffers) {
+ funcs->deleteBuffers = (type_glDeleteBuffers)
+ context->getProcAddress(QLatin1String("glDeleteBuffersARB"));
+ }
+
+ if (funcs->deleteBuffers)
+ funcs->deleteBuffers(n, buffers);
+ else
+ funcs->deleteBuffers = qglfResolveDeleteBuffers;
+}
+
+static void qglfResolveDeleteFramebuffers(GLsizei n, const GLuint* framebuffers)
+{
+ typedef void (QGLF_APIENTRYP type_glDeleteFramebuffers)(GLsizei n, const GLuint* framebuffers);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->deleteFramebuffers = (type_glDeleteFramebuffers)
+ context->getProcAddress(QLatin1String("glDeleteFramebuffers"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->deleteFramebuffers) {
+ funcs->deleteFramebuffers = (type_glDeleteFramebuffers)
+ context->getProcAddress(QLatin1String("glDeleteFramebuffersOES"));
+ }
+#endif
+ if (!funcs->deleteFramebuffers) {
+ funcs->deleteFramebuffers = (type_glDeleteFramebuffers)
+ context->getProcAddress(QLatin1String("glDeleteFramebuffersEXT"));
+ }
+ if (!funcs->deleteFramebuffers) {
+ funcs->deleteFramebuffers = (type_glDeleteFramebuffers)
+ context->getProcAddress(QLatin1String("glDeleteFramebuffersARB"));
+ }
+
+ if (funcs->deleteFramebuffers)
+ funcs->deleteFramebuffers(n, framebuffers);
+ else
+ funcs->deleteFramebuffers = qglfResolveDeleteFramebuffers;
+}
+
+static void qglfResolveDeleteProgram(GLuint program)
+{
+ typedef void (QGLF_APIENTRYP type_glDeleteProgram)(GLuint program);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->deleteProgram = (type_glDeleteProgram)
+ context->getProcAddress(QLatin1String("glDeleteProgram"));
+ if (!funcs->deleteProgram) {
+ funcs->deleteProgram = (type_glDeleteProgram)
+ context->getProcAddress(QLatin1String("glDeleteObjectARB"));
+ }
+
+ if (funcs->deleteProgram)
+ funcs->deleteProgram(program);
+ else
+ funcs->deleteProgram = qglfResolveDeleteProgram;
+}
+
+static void qglfResolveDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers)
+{
+ typedef void (QGLF_APIENTRYP type_glDeleteRenderbuffers)(GLsizei n, const GLuint* renderbuffers);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->deleteRenderbuffers = (type_glDeleteRenderbuffers)
+ context->getProcAddress(QLatin1String("glDeleteRenderbuffers"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->deleteRenderbuffers) {
+ funcs->deleteRenderbuffers = (type_glDeleteRenderbuffers)
+ context->getProcAddress(QLatin1String("glDeleteRenderbuffersOES"));
+ }
+#endif
+ if (!funcs->deleteRenderbuffers) {
+ funcs->deleteRenderbuffers = (type_glDeleteRenderbuffers)
+ context->getProcAddress(QLatin1String("glDeleteRenderbuffersEXT"));
+ }
+ if (!funcs->deleteRenderbuffers) {
+ funcs->deleteRenderbuffers = (type_glDeleteRenderbuffers)
+ context->getProcAddress(QLatin1String("glDeleteRenderbuffersARB"));
+ }
+
+ if (funcs->deleteRenderbuffers)
+ funcs->deleteRenderbuffers(n, renderbuffers);
+ else
+ funcs->deleteRenderbuffers = qglfResolveDeleteRenderbuffers;
+}
+
+static void qglfResolveDeleteShader(GLuint shader)
+{
+ typedef void (QGLF_APIENTRYP type_glDeleteShader)(GLuint shader);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->deleteShader = (type_glDeleteShader)
+ context->getProcAddress(QLatin1String("glDeleteShader"));
+ if (!funcs->deleteShader) {
+ funcs->deleteShader = (type_glDeleteShader)
+ context->getProcAddress(QLatin1String("glDeleteObjectARB"));
+ }
+
+ if (funcs->deleteShader)
+ funcs->deleteShader(shader);
+ else
+ funcs->deleteShader = qglfResolveDeleteShader;
+}
+
+static void qglfResolveDetachShader(GLuint program, GLuint shader)
+{
+ typedef void (QGLF_APIENTRYP type_glDetachShader)(GLuint program, GLuint shader);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->detachShader = (type_glDetachShader)
+ context->getProcAddress(QLatin1String("glDetachShader"));
+ if (!funcs->detachShader) {
+ funcs->detachShader = (type_glDetachShader)
+ context->getProcAddress(QLatin1String("glDetachObjectARB"));
+ }
+
+ if (funcs->detachShader)
+ funcs->detachShader(program, shader);
+ else
+ funcs->detachShader = qglfResolveDetachShader;
+}
+
+static void qglfResolveDisableVertexAttribArray(GLuint index)
+{
+ typedef void (QGLF_APIENTRYP type_glDisableVertexAttribArray)(GLuint index);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->disableVertexAttribArray = (type_glDisableVertexAttribArray)
+ context->getProcAddress(QLatin1String("glDisableVertexAttribArray"));
+ if (!funcs->disableVertexAttribArray) {
+ funcs->disableVertexAttribArray = (type_glDisableVertexAttribArray)
+ context->getProcAddress(QLatin1String("glDisableVertexAttribArrayARB"));
+ }
+
+ if (funcs->disableVertexAttribArray)
+ funcs->disableVertexAttribArray(index);
+ else
+ funcs->disableVertexAttribArray = qglfResolveDisableVertexAttribArray;
+}
+
+static void qglfResolveEnableVertexAttribArray(GLuint index)
+{
+ typedef void (QGLF_APIENTRYP type_glEnableVertexAttribArray)(GLuint index);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->enableVertexAttribArray = (type_glEnableVertexAttribArray)
+ context->getProcAddress(QLatin1String("glEnableVertexAttribArray"));
+ if (!funcs->enableVertexAttribArray) {
+ funcs->enableVertexAttribArray = (type_glEnableVertexAttribArray)
+ context->getProcAddress(QLatin1String("glEnableVertexAttribArrayARB"));
+ }
+
+ if (funcs->enableVertexAttribArray)
+ funcs->enableVertexAttribArray(index);
+ else
+ funcs->enableVertexAttribArray = qglfResolveEnableVertexAttribArray;
+}
+
+static void qglfResolveFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
+{
+ typedef void (QGLF_APIENTRYP type_glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->framebufferRenderbuffer = (type_glFramebufferRenderbuffer)
+ context->getProcAddress(QLatin1String("glFramebufferRenderbuffer"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->framebufferRenderbuffer) {
+ funcs->framebufferRenderbuffer = (type_glFramebufferRenderbuffer)
+ context->getProcAddress(QLatin1String("glFramebufferRenderbufferOES"));
+ }
+#endif
+ if (!funcs->framebufferRenderbuffer) {
+ funcs->framebufferRenderbuffer = (type_glFramebufferRenderbuffer)
+ context->getProcAddress(QLatin1String("glFramebufferRenderbufferEXT"));
+ }
+ if (!funcs->framebufferRenderbuffer) {
+ funcs->framebufferRenderbuffer = (type_glFramebufferRenderbuffer)
+ context->getProcAddress(QLatin1String("glFramebufferRenderbufferARB"));
+ }
+
+ if (funcs->framebufferRenderbuffer)
+ funcs->framebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
+ else
+ funcs->framebufferRenderbuffer = qglfResolveFramebufferRenderbuffer;
+}
+
+static void qglfResolveFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
+{
+ typedef void (QGLF_APIENTRYP type_glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->framebufferTexture2D = (type_glFramebufferTexture2D)
+ context->getProcAddress(QLatin1String("glFramebufferTexture2D"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->framebufferTexture2D) {
+ funcs->framebufferTexture2D = (type_glFramebufferTexture2D)
+ context->getProcAddress(QLatin1String("glFramebufferTexture2DOES"));
+ }
+#endif
+ if (!funcs->framebufferTexture2D) {
+ funcs->framebufferTexture2D = (type_glFramebufferTexture2D)
+ context->getProcAddress(QLatin1String("glFramebufferTexture2DEXT"));
+ }
+ if (!funcs->framebufferTexture2D) {
+ funcs->framebufferTexture2D = (type_glFramebufferTexture2D)
+ context->getProcAddress(QLatin1String("glFramebufferTexture2DARB"));
+ }
+
+ if (funcs->framebufferTexture2D)
+ funcs->framebufferTexture2D(target, attachment, textarget, texture, level);
+ else
+ funcs->framebufferTexture2D = qglfResolveFramebufferTexture2D;
+}
+
+static void qglfResolveGenBuffers(GLsizei n, GLuint* buffers)
+{
+ typedef void (QGLF_APIENTRYP type_glGenBuffers)(GLsizei n, GLuint* buffers);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->genBuffers = (type_glGenBuffers)
+ context->getProcAddress(QLatin1String("glGenBuffers"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->genBuffers) {
+ funcs->genBuffers = (type_glGenBuffers)
+ context->getProcAddress(QLatin1String("glGenBuffersOES"));
+ }
+#endif
+ if (!funcs->genBuffers) {
+ funcs->genBuffers = (type_glGenBuffers)
+ context->getProcAddress(QLatin1String("glGenBuffersEXT"));
+ }
+ if (!funcs->genBuffers) {
+ funcs->genBuffers = (type_glGenBuffers)
+ context->getProcAddress(QLatin1String("glGenBuffersARB"));
+ }
+
+ if (funcs->genBuffers)
+ funcs->genBuffers(n, buffers);
+ else
+ funcs->genBuffers = qglfResolveGenBuffers;
+}
+
+static void qglfResolveGenerateMipmap(GLenum target)
+{
+ typedef void (QGLF_APIENTRYP type_glGenerateMipmap)(GLenum target);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->generateMipmap = (type_glGenerateMipmap)
+ context->getProcAddress(QLatin1String("glGenerateMipmap"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->generateMipmap) {
+ funcs->generateMipmap = (type_glGenerateMipmap)
+ context->getProcAddress(QLatin1String("glGenerateMipmapOES"));
+ }
+#endif
+ if (!funcs->generateMipmap) {
+ funcs->generateMipmap = (type_glGenerateMipmap)
+ context->getProcAddress(QLatin1String("glGenerateMipmapEXT"));
+ }
+ if (!funcs->generateMipmap) {
+ funcs->generateMipmap = (type_glGenerateMipmap)
+ context->getProcAddress(QLatin1String("glGenerateMipmapARB"));
+ }
+
+ if (funcs->generateMipmap)
+ funcs->generateMipmap(target);
+ else
+ funcs->generateMipmap = qglfResolveGenerateMipmap;
+}
+
+static void qglfResolveGenFramebuffers(GLsizei n, GLuint* framebuffers)
+{
+ typedef void (QGLF_APIENTRYP type_glGenFramebuffers)(GLsizei n, GLuint* framebuffers);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->genFramebuffers = (type_glGenFramebuffers)
+ context->getProcAddress(QLatin1String("glGenFramebuffers"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->genFramebuffers) {
+ funcs->genFramebuffers = (type_glGenFramebuffers)
+ context->getProcAddress(QLatin1String("glGenFramebuffersOES"));
+ }
+#endif
+ if (!funcs->genFramebuffers) {
+ funcs->genFramebuffers = (type_glGenFramebuffers)
+ context->getProcAddress(QLatin1String("glGenFramebuffersEXT"));
+ }
+ if (!funcs->genFramebuffers) {
+ funcs->genFramebuffers = (type_glGenFramebuffers)
+ context->getProcAddress(QLatin1String("glGenFramebuffersARB"));
+ }
+
+ if (funcs->genFramebuffers)
+ funcs->genFramebuffers(n, framebuffers);
+ else
+ funcs->genFramebuffers = qglfResolveGenFramebuffers;
+}
+
+static void qglfResolveGenRenderbuffers(GLsizei n, GLuint* renderbuffers)
+{
+ typedef void (QGLF_APIENTRYP type_glGenRenderbuffers)(GLsizei n, GLuint* renderbuffers);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->genRenderbuffers = (type_glGenRenderbuffers)
+ context->getProcAddress(QLatin1String("glGenRenderbuffers"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->genRenderbuffers) {
+ funcs->genRenderbuffers = (type_glGenRenderbuffers)
+ context->getProcAddress(QLatin1String("glGenRenderbuffersOES"));
+ }
+#endif
+ if (!funcs->genRenderbuffers) {
+ funcs->genRenderbuffers = (type_glGenRenderbuffers)
+ context->getProcAddress(QLatin1String("glGenRenderbuffersEXT"));
+ }
+ if (!funcs->genRenderbuffers) {
+ funcs->genRenderbuffers = (type_glGenRenderbuffers)
+ context->getProcAddress(QLatin1String("glGenRenderbuffersARB"));
+ }
+
+ if (funcs->genRenderbuffers)
+ funcs->genRenderbuffers(n, renderbuffers);
+ else
+ funcs->genRenderbuffers = qglfResolveGenRenderbuffers;
+}
+
+static void qglfResolveGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name)
+{
+ typedef void (QGLF_APIENTRYP type_glGetActiveAttrib)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getActiveAttrib = (type_glGetActiveAttrib)
+ context->getProcAddress(QLatin1String("glGetActiveAttrib"));
+ if (!funcs->getActiveAttrib) {
+ funcs->getActiveAttrib = (type_glGetActiveAttrib)
+ context->getProcAddress(QLatin1String("glGetActiveAttribARB"));
+ }
+
+ if (funcs->getActiveAttrib)
+ funcs->getActiveAttrib(program, index, bufsize, length, size, type, name);
+ else
+ funcs->getActiveAttrib = qglfResolveGetActiveAttrib;
+}
+
+static void qglfResolveGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name)
+{
+ typedef void (QGLF_APIENTRYP type_glGetActiveUniform)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getActiveUniform = (type_glGetActiveUniform)
+ context->getProcAddress(QLatin1String("glGetActiveUniform"));
+ if (!funcs->getActiveUniform) {
+ funcs->getActiveUniform = (type_glGetActiveUniform)
+ context->getProcAddress(QLatin1String("glGetActiveUniformARB"));
+ }
+
+ if (funcs->getActiveUniform)
+ funcs->getActiveUniform(program, index, bufsize, length, size, type, name);
+ else
+ funcs->getActiveUniform = qglfResolveGetActiveUniform;
+}
+
+static void qglfResolveGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders)
+{
+ typedef void (QGLF_APIENTRYP type_glGetAttachedShaders)(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getAttachedShaders = (type_glGetAttachedShaders)
+ context->getProcAddress(QLatin1String("glGetAttachedShaders"));
+ if (!funcs->getAttachedShaders) {
+ funcs->getAttachedShaders = (type_glGetAttachedShaders)
+ context->getProcAddress(QLatin1String("glGetAttachedObjectsARB"));
+ }
+
+ if (funcs->getAttachedShaders)
+ funcs->getAttachedShaders(program, maxcount, count, shaders);
+ else
+ funcs->getAttachedShaders = qglfResolveGetAttachedShaders;
+}
+
+static int qglfResolveGetAttribLocation(GLuint program, const char* name)
+{
+ typedef int (QGLF_APIENTRYP type_glGetAttribLocation)(GLuint program, const char* name);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getAttribLocation = (type_glGetAttribLocation)
+ context->getProcAddress(QLatin1String("glGetAttribLocation"));
+ if (!funcs->getAttribLocation) {
+ funcs->getAttribLocation = (type_glGetAttribLocation)
+ context->getProcAddress(QLatin1String("glGetAttribLocationARB"));
+ }
+
+ if (funcs->getAttribLocation)
+ return funcs->getAttribLocation(program, name);
+ funcs->getAttribLocation = qglfResolveGetAttribLocation;
+ return int(0);
+}
+
+static void qglfResolveGetBufferParameteriv(GLenum target, GLenum pname, GLint* params)
+{
+ typedef void (QGLF_APIENTRYP type_glGetBufferParameteriv)(GLenum target, GLenum pname, GLint* params);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getBufferParameteriv = (type_glGetBufferParameteriv)
+ context->getProcAddress(QLatin1String("glGetBufferParameteriv"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->getBufferParameteriv) {
+ funcs->getBufferParameteriv = (type_glGetBufferParameteriv)
+ context->getProcAddress(QLatin1String("glGetBufferParameterivOES"));
+ }
+#endif
+ if (!funcs->getBufferParameteriv) {
+ funcs->getBufferParameteriv = (type_glGetBufferParameteriv)
+ context->getProcAddress(QLatin1String("glGetBufferParameterivEXT"));
+ }
+ if (!funcs->getBufferParameteriv) {
+ funcs->getBufferParameteriv = (type_glGetBufferParameteriv)
+ context->getProcAddress(QLatin1String("glGetBufferParameterivARB"));
+ }
+
+ if (funcs->getBufferParameteriv)
+ funcs->getBufferParameteriv(target, pname, params);
+ else
+ funcs->getBufferParameteriv = qglfResolveGetBufferParameteriv;
+}
+
+static void qglfResolveGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params)
+{
+ typedef void (QGLF_APIENTRYP type_glGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint* params);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getFramebufferAttachmentParameteriv = (type_glGetFramebufferAttachmentParameteriv)
+ context->getProcAddress(QLatin1String("glGetFramebufferAttachmentParameteriv"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->getFramebufferAttachmentParameteriv) {
+ funcs->getFramebufferAttachmentParameteriv = (type_glGetFramebufferAttachmentParameteriv)
+ context->getProcAddress(QLatin1String("glGetFramebufferAttachmentParameterivOES"));
+ }
+#endif
+ if (!funcs->getFramebufferAttachmentParameteriv) {
+ funcs->getFramebufferAttachmentParameteriv = (type_glGetFramebufferAttachmentParameteriv)
+ context->getProcAddress(QLatin1String("glGetFramebufferAttachmentParameterivEXT"));
+ }
+ if (!funcs->getFramebufferAttachmentParameteriv) {
+ funcs->getFramebufferAttachmentParameteriv = (type_glGetFramebufferAttachmentParameteriv)
+ context->getProcAddress(QLatin1String("glGetFramebufferAttachmentParameterivARB"));
+ }
+
+ if (funcs->getFramebufferAttachmentParameteriv)
+ funcs->getFramebufferAttachmentParameteriv(target, attachment, pname, params);
+ else
+ funcs->getFramebufferAttachmentParameteriv = qglfResolveGetFramebufferAttachmentParameteriv;
+}
+
+static void qglfResolveGetProgramiv(GLuint program, GLenum pname, GLint* params)
+{
+ typedef void (QGLF_APIENTRYP type_glGetProgramiv)(GLuint program, GLenum pname, GLint* params);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getProgramiv = (type_glGetProgramiv)
+ context->getProcAddress(QLatin1String("glGetProgramiv"));
+ if (!funcs->getProgramiv) {
+ funcs->getProgramiv = (type_glGetProgramiv)
+ context->getProcAddress(QLatin1String("glGetObjectParameterivARB"));
+ }
+
+ if (funcs->getProgramiv)
+ funcs->getProgramiv(program, pname, params);
+ else
+ funcs->getProgramiv = qglfResolveGetProgramiv;
+}
+
+static void qglfResolveGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog)
+{
+ typedef void (QGLF_APIENTRYP type_glGetProgramInfoLog)(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getProgramInfoLog = (type_glGetProgramInfoLog)
+ context->getProcAddress(QLatin1String("glGetProgramInfoLog"));
+ if (!funcs->getProgramInfoLog) {
+ funcs->getProgramInfoLog = (type_glGetProgramInfoLog)
+ context->getProcAddress(QLatin1String("glGetInfoLogARB"));
+ }
+
+ if (funcs->getProgramInfoLog)
+ funcs->getProgramInfoLog(program, bufsize, length, infolog);
+ else
+ funcs->getProgramInfoLog = qglfResolveGetProgramInfoLog;
+}
+
+static void qglfResolveGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params)
+{
+ typedef void (QGLF_APIENTRYP type_glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint* params);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getRenderbufferParameteriv = (type_glGetRenderbufferParameteriv)
+ context->getProcAddress(QLatin1String("glGetRenderbufferParameteriv"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->getRenderbufferParameteriv) {
+ funcs->getRenderbufferParameteriv = (type_glGetRenderbufferParameteriv)
+ context->getProcAddress(QLatin1String("glGetRenderbufferParameterivOES"));
+ }
+#endif
+ if (!funcs->getRenderbufferParameteriv) {
+ funcs->getRenderbufferParameteriv = (type_glGetRenderbufferParameteriv)
+ context->getProcAddress(QLatin1String("glGetRenderbufferParameterivEXT"));
+ }
+ if (!funcs->getRenderbufferParameteriv) {
+ funcs->getRenderbufferParameteriv = (type_glGetRenderbufferParameteriv)
+ context->getProcAddress(QLatin1String("glGetRenderbufferParameterivARB"));
+ }
+
+ if (funcs->getRenderbufferParameteriv)
+ funcs->getRenderbufferParameteriv(target, pname, params);
+ else
+ funcs->getRenderbufferParameteriv = qglfResolveGetRenderbufferParameteriv;
+}
+
+static void qglfResolveGetShaderiv(GLuint shader, GLenum pname, GLint* params)
+{
+ typedef void (QGLF_APIENTRYP type_glGetShaderiv)(GLuint shader, GLenum pname, GLint* params);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getShaderiv = (type_glGetShaderiv)
+ context->getProcAddress(QLatin1String("glGetShaderiv"));
+ if (!funcs->getShaderiv) {
+ funcs->getShaderiv = (type_glGetShaderiv)
+ context->getProcAddress(QLatin1String("glGetObjectParameterivARB"));
+ }
+
+ if (funcs->getShaderiv)
+ funcs->getShaderiv(shader, pname, params);
+ else
+ funcs->getShaderiv = qglfResolveGetShaderiv;
+}
+
+static void qglfResolveGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog)
+{
+ typedef void (QGLF_APIENTRYP type_glGetShaderInfoLog)(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getShaderInfoLog = (type_glGetShaderInfoLog)
+ context->getProcAddress(QLatin1String("glGetShaderInfoLog"));
+ if (!funcs->getShaderInfoLog) {
+ funcs->getShaderInfoLog = (type_glGetShaderInfoLog)
+ context->getProcAddress(QLatin1String("glGetInfoLogARB"));
+ }
+
+ if (funcs->getShaderInfoLog)
+ funcs->getShaderInfoLog(shader, bufsize, length, infolog);
+ else
+ funcs->getShaderInfoLog = qglfResolveGetShaderInfoLog;
+}
+
+static void qglfSpecialGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision)
+{
+ Q_UNUSED(shadertype);
+ Q_UNUSED(precisiontype);
+ range[0] = range[1] = precision[0] = 0;
+}
+
+static void qglfResolveGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision)
+{
+ typedef void (QGLF_APIENTRYP type_glGetShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getShaderPrecisionFormat = (type_glGetShaderPrecisionFormat)
+ context->getProcAddress(QLatin1String("glGetShaderPrecisionFormat"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->getShaderPrecisionFormat) {
+ funcs->getShaderPrecisionFormat = (type_glGetShaderPrecisionFormat)
+ context->getProcAddress(QLatin1String("glGetShaderPrecisionFormatOES"));
+ }
+#endif
+ if (!funcs->getShaderPrecisionFormat) {
+ funcs->getShaderPrecisionFormat = (type_glGetShaderPrecisionFormat)
+ context->getProcAddress(QLatin1String("glGetShaderPrecisionFormatEXT"));
+ }
+ if (!funcs->getShaderPrecisionFormat) {
+ funcs->getShaderPrecisionFormat = (type_glGetShaderPrecisionFormat)
+ context->getProcAddress(QLatin1String("glGetShaderPrecisionFormatARB"));
+ }
+
+ if (!funcs->getShaderPrecisionFormat)
+ funcs->getShaderPrecisionFormat = qglfSpecialGetShaderPrecisionFormat;
+
+ funcs->getShaderPrecisionFormat(shadertype, precisiontype, range, precision);
+}
+
+static void qglfResolveGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source)
+{
+ typedef void (QGLF_APIENTRYP type_glGetShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, char* source);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getShaderSource = (type_glGetShaderSource)
+ context->getProcAddress(QLatin1String("glGetShaderSource"));
+ if (!funcs->getShaderSource) {
+ funcs->getShaderSource = (type_glGetShaderSource)
+ context->getProcAddress(QLatin1String("glGetShaderSourceARB"));
+ }
+
+ if (funcs->getShaderSource)
+ funcs->getShaderSource(shader, bufsize, length, source);
+ else
+ funcs->getShaderSource = qglfResolveGetShaderSource;
+}
+
+static void qglfResolveGetUniformfv(GLuint program, GLint location, GLfloat* params)
+{
+ typedef void (QGLF_APIENTRYP type_glGetUniformfv)(GLuint program, GLint location, GLfloat* params);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getUniformfv = (type_glGetUniformfv)
+ context->getProcAddress(QLatin1String("glGetUniformfv"));
+ if (!funcs->getUniformfv) {
+ funcs->getUniformfv = (type_glGetUniformfv)
+ context->getProcAddress(QLatin1String("glGetUniformfvARB"));
+ }
+
+ if (funcs->getUniformfv)
+ funcs->getUniformfv(program, location, params);
+ else
+ funcs->getUniformfv = qglfResolveGetUniformfv;
+}
+
+static void qglfResolveGetUniformiv(GLuint program, GLint location, GLint* params)
+{
+ typedef void (QGLF_APIENTRYP type_glGetUniformiv)(GLuint program, GLint location, GLint* params);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getUniformiv = (type_glGetUniformiv)
+ context->getProcAddress(QLatin1String("glGetUniformiv"));
+ if (!funcs->getUniformiv) {
+ funcs->getUniformiv = (type_glGetUniformiv)
+ context->getProcAddress(QLatin1String("glGetUniformivARB"));
+ }
+
+ if (funcs->getUniformiv)
+ funcs->getUniformiv(program, location, params);
+ else
+ funcs->getUniformiv = qglfResolveGetUniformiv;
+}
+
+static int qglfResolveGetUniformLocation(GLuint program, const char* name)
+{
+ typedef int (QGLF_APIENTRYP type_glGetUniformLocation)(GLuint program, const char* name);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getUniformLocation = (type_glGetUniformLocation)
+ context->getProcAddress(QLatin1String("glGetUniformLocation"));
+ if (!funcs->getUniformLocation) {
+ funcs->getUniformLocation = (type_glGetUniformLocation)
+ context->getProcAddress(QLatin1String("glGetUniformLocationARB"));
+ }
+
+ if (funcs->getUniformLocation)
+ return funcs->getUniformLocation(program, name);
+ funcs->getUniformLocation = qglfResolveGetUniformLocation;
+ return int(0);
+}
+
+static void qglfResolveGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params)
+{
+ typedef void (QGLF_APIENTRYP type_glGetVertexAttribfv)(GLuint index, GLenum pname, GLfloat* params);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getVertexAttribfv = (type_glGetVertexAttribfv)
+ context->getProcAddress(QLatin1String("glGetVertexAttribfv"));
+ if (!funcs->getVertexAttribfv) {
+ funcs->getVertexAttribfv = (type_glGetVertexAttribfv)
+ context->getProcAddress(QLatin1String("glGetVertexAttribfvARB"));
+ }
+
+ if (funcs->getVertexAttribfv)
+ funcs->getVertexAttribfv(index, pname, params);
+ else
+ funcs->getVertexAttribfv = qglfResolveGetVertexAttribfv;
+}
+
+static void qglfResolveGetVertexAttribiv(GLuint index, GLenum pname, GLint* params)
+{
+ typedef void (QGLF_APIENTRYP type_glGetVertexAttribiv)(GLuint index, GLenum pname, GLint* params);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getVertexAttribiv = (type_glGetVertexAttribiv)
+ context->getProcAddress(QLatin1String("glGetVertexAttribiv"));
+ if (!funcs->getVertexAttribiv) {
+ funcs->getVertexAttribiv = (type_glGetVertexAttribiv)
+ context->getProcAddress(QLatin1String("glGetVertexAttribivARB"));
+ }
+
+ if (funcs->getVertexAttribiv)
+ funcs->getVertexAttribiv(index, pname, params);
+ else
+ funcs->getVertexAttribiv = qglfResolveGetVertexAttribiv;
+}
+
+static void qglfResolveGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer)
+{
+ typedef void (QGLF_APIENTRYP type_glGetVertexAttribPointerv)(GLuint index, GLenum pname, void** pointer);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->getVertexAttribPointerv = (type_glGetVertexAttribPointerv)
+ context->getProcAddress(QLatin1String("glGetVertexAttribPointerv"));
+ if (!funcs->getVertexAttribPointerv) {
+ funcs->getVertexAttribPointerv = (type_glGetVertexAttribPointerv)
+ context->getProcAddress(QLatin1String("glGetVertexAttribPointervARB"));
+ }
+
+ if (funcs->getVertexAttribPointerv)
+ funcs->getVertexAttribPointerv(index, pname, pointer);
+ else
+ funcs->getVertexAttribPointerv = qglfResolveGetVertexAttribPointerv;
+}
+
+static GLboolean qglfResolveIsBuffer(GLuint buffer)
+{
+ typedef GLboolean (QGLF_APIENTRYP type_glIsBuffer)(GLuint buffer);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->isBuffer = (type_glIsBuffer)
+ context->getProcAddress(QLatin1String("glIsBuffer"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->isBuffer) {
+ funcs->isBuffer = (type_glIsBuffer)
+ context->getProcAddress(QLatin1String("glIsBufferOES"));
+ }
+#endif
+ if (!funcs->isBuffer) {
+ funcs->isBuffer = (type_glIsBuffer)
+ context->getProcAddress(QLatin1String("glIsBufferEXT"));
+ }
+ if (!funcs->isBuffer) {
+ funcs->isBuffer = (type_glIsBuffer)
+ context->getProcAddress(QLatin1String("glIsBufferARB"));
+ }
+
+ if (funcs->isBuffer)
+ return funcs->isBuffer(buffer);
+ funcs->isBuffer = qglfResolveIsBuffer;
+ return GLboolean(0);
+}
+
+static GLboolean qglfResolveIsFramebuffer(GLuint framebuffer)
+{
+ typedef GLboolean (QGLF_APIENTRYP type_glIsFramebuffer)(GLuint framebuffer);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->isFramebuffer = (type_glIsFramebuffer)
+ context->getProcAddress(QLatin1String("glIsFramebuffer"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->isFramebuffer) {
+ funcs->isFramebuffer = (type_glIsFramebuffer)
+ context->getProcAddress(QLatin1String("glIsFramebufferOES"));
+ }
+#endif
+ if (!funcs->isFramebuffer) {
+ funcs->isFramebuffer = (type_glIsFramebuffer)
+ context->getProcAddress(QLatin1String("glIsFramebufferEXT"));
+ }
+ if (!funcs->isFramebuffer) {
+ funcs->isFramebuffer = (type_glIsFramebuffer)
+ context->getProcAddress(QLatin1String("glIsFramebufferARB"));
+ }
+
+ if (funcs->isFramebuffer)
+ return funcs->isFramebuffer(framebuffer);
+ funcs->isFramebuffer = qglfResolveIsFramebuffer;
+ return GLboolean(0);
+}
+
+static GLboolean qglfSpecialIsProgram(GLuint program)
+{
+ return program != 0;
+}
+
+static GLboolean qglfResolveIsProgram(GLuint program)
+{
+ typedef GLboolean (QGLF_APIENTRYP type_glIsProgram)(GLuint program);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->isProgram = (type_glIsProgram)
+ context->getProcAddress(QLatin1String("glIsProgram"));
+ if (!funcs->isProgram) {
+ funcs->isProgram = (type_glIsProgram)
+ context->getProcAddress(QLatin1String("glIsProgramARB"));
+ }
+
+ if (!funcs->isProgram)
+ funcs->isProgram = qglfSpecialIsProgram;
+
+ return funcs->isProgram(program);
+}
+
+static GLboolean qglfResolveIsRenderbuffer(GLuint renderbuffer)
+{
+ typedef GLboolean (QGLF_APIENTRYP type_glIsRenderbuffer)(GLuint renderbuffer);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->isRenderbuffer = (type_glIsRenderbuffer)
+ context->getProcAddress(QLatin1String("glIsRenderbuffer"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->isRenderbuffer) {
+ funcs->isRenderbuffer = (type_glIsRenderbuffer)
+ context->getProcAddress(QLatin1String("glIsRenderbufferOES"));
+ }
+#endif
+ if (!funcs->isRenderbuffer) {
+ funcs->isRenderbuffer = (type_glIsRenderbuffer)
+ context->getProcAddress(QLatin1String("glIsRenderbufferEXT"));
+ }
+ if (!funcs->isRenderbuffer) {
+ funcs->isRenderbuffer = (type_glIsRenderbuffer)
+ context->getProcAddress(QLatin1String("glIsRenderbufferARB"));
+ }
+
+ if (funcs->isRenderbuffer)
+ return funcs->isRenderbuffer(renderbuffer);
+ funcs->isRenderbuffer = qglfResolveIsRenderbuffer;
+ return GLboolean(0);
+}
+
+static GLboolean qglfSpecialIsShader(GLuint shader)
+{
+ return shader != 0;
+}
+
+static GLboolean qglfResolveIsShader(GLuint shader)
+{
+ typedef GLboolean (QGLF_APIENTRYP type_glIsShader)(GLuint shader);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->isShader = (type_glIsShader)
+ context->getProcAddress(QLatin1String("glIsShader"));
+ if (!funcs->isShader) {
+ funcs->isShader = (type_glIsShader)
+ context->getProcAddress(QLatin1String("glIsShaderARB"));
+ }
+
+ if (!funcs->isShader)
+ funcs->isShader = qglfSpecialIsShader;
+
+ return funcs->isShader(shader);
+}
+
+static void qglfResolveLinkProgram(GLuint program)
+{
+ typedef void (QGLF_APIENTRYP type_glLinkProgram)(GLuint program);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->linkProgram = (type_glLinkProgram)
+ context->getProcAddress(QLatin1String("glLinkProgram"));
+ if (!funcs->linkProgram) {
+ funcs->linkProgram = (type_glLinkProgram)
+ context->getProcAddress(QLatin1String("glLinkProgramARB"));
+ }
+
+ if (funcs->linkProgram)
+ funcs->linkProgram(program);
+ else
+ funcs->linkProgram = qglfResolveLinkProgram;
+}
+
+static void qglfSpecialReleaseShaderCompiler()
+{
+}
+
+static void qglfResolveReleaseShaderCompiler()
+{
+ typedef void (QGLF_APIENTRYP type_glReleaseShaderCompiler)();
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->releaseShaderCompiler = (type_glReleaseShaderCompiler)
+ context->getProcAddress(QLatin1String("glReleaseShaderCompiler"));
+ if (!funcs->releaseShaderCompiler) {
+ funcs->releaseShaderCompiler = (type_glReleaseShaderCompiler)
+ context->getProcAddress(QLatin1String("glReleaseShaderCompilerARB"));
+ }
+
+ if (!funcs->releaseShaderCompiler)
+ funcs->releaseShaderCompiler = qglfSpecialReleaseShaderCompiler;
+
+ funcs->releaseShaderCompiler();
+}
+
+static void qglfResolveRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
+{
+ typedef void (QGLF_APIENTRYP type_glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->renderbufferStorage = (type_glRenderbufferStorage)
+ context->getProcAddress(QLatin1String("glRenderbufferStorage"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->renderbufferStorage) {
+ funcs->renderbufferStorage = (type_glRenderbufferStorage)
+ context->getProcAddress(QLatin1String("glRenderbufferStorageOES"));
+ }
+#endif
+ if (!funcs->renderbufferStorage) {
+ funcs->renderbufferStorage = (type_glRenderbufferStorage)
+ context->getProcAddress(QLatin1String("glRenderbufferStorageEXT"));
+ }
+ if (!funcs->renderbufferStorage) {
+ funcs->renderbufferStorage = (type_glRenderbufferStorage)
+ context->getProcAddress(QLatin1String("glRenderbufferStorageARB"));
+ }
+
+ if (funcs->renderbufferStorage)
+ funcs->renderbufferStorage(target, internalformat, width, height);
+ else
+ funcs->renderbufferStorage = qglfResolveRenderbufferStorage;
+}
+
+static void qglfResolveSampleCoverage(GLclampf value, GLboolean invert)
+{
+ typedef void (QGLF_APIENTRYP type_glSampleCoverage)(GLclampf value, GLboolean invert);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->sampleCoverage = (type_glSampleCoverage)
+ context->getProcAddress(QLatin1String("glSampleCoverage"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->sampleCoverage) {
+ funcs->sampleCoverage = (type_glSampleCoverage)
+ context->getProcAddress(QLatin1String("glSampleCoverageOES"));
+ }
+#endif
+ if (!funcs->sampleCoverage) {
+ funcs->sampleCoverage = (type_glSampleCoverage)
+ context->getProcAddress(QLatin1String("glSampleCoverageEXT"));
+ }
+ if (!funcs->sampleCoverage) {
+ funcs->sampleCoverage = (type_glSampleCoverage)
+ context->getProcAddress(QLatin1String("glSampleCoverageARB"));
+ }
+
+ if (funcs->sampleCoverage)
+ funcs->sampleCoverage(value, invert);
+ else
+ funcs->sampleCoverage = qglfResolveSampleCoverage;
+}
+
+static void qglfResolveShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length)
+{
+ typedef void (QGLF_APIENTRYP type_glShaderBinary)(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->shaderBinary = (type_glShaderBinary)
+ context->getProcAddress(QLatin1String("glShaderBinary"));
+ if (!funcs->shaderBinary) {
+ funcs->shaderBinary = (type_glShaderBinary)
+ context->getProcAddress(QLatin1String("glShaderBinaryARB"));
+ }
+
+ if (funcs->shaderBinary)
+ funcs->shaderBinary(n, shaders, binaryformat, binary, length);
+ else
+ funcs->shaderBinary = qglfResolveShaderBinary;
+}
+
+static void qglfResolveShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length)
+{
+ typedef void (QGLF_APIENTRYP type_glShaderSource)(GLuint shader, GLsizei count, const char** string, const GLint* length);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->shaderSource = (type_glShaderSource)
+ context->getProcAddress(QLatin1String("glShaderSource"));
+ if (!funcs->shaderSource) {
+ funcs->shaderSource = (type_glShaderSource)
+ context->getProcAddress(QLatin1String("glShaderSourceARB"));
+ }
+
+ if (funcs->shaderSource)
+ funcs->shaderSource(shader, count, string, length);
+ else
+ funcs->shaderSource = qglfResolveShaderSource;
+}
+
+static void qglfResolveStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
+{
+ typedef void (QGLF_APIENTRYP type_glStencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->stencilFuncSeparate = (type_glStencilFuncSeparate)
+ context->getProcAddress(QLatin1String("glStencilFuncSeparate"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->stencilFuncSeparate) {
+ funcs->stencilFuncSeparate = (type_glStencilFuncSeparate)
+ context->getProcAddress(QLatin1String("glStencilFuncSeparateOES"));
+ }
+#endif
+ if (!funcs->stencilFuncSeparate) {
+ funcs->stencilFuncSeparate = (type_glStencilFuncSeparate)
+ context->getProcAddress(QLatin1String("glStencilFuncSeparateEXT"));
+ }
+ if (!funcs->stencilFuncSeparate) {
+ funcs->stencilFuncSeparate = (type_glStencilFuncSeparate)
+ context->getProcAddress(QLatin1String("glStencilFuncSeparateARB"));
+ }
+
+ if (funcs->stencilFuncSeparate)
+ funcs->stencilFuncSeparate(face, func, ref, mask);
+ else
+ funcs->stencilFuncSeparate = qglfResolveStencilFuncSeparate;
+}
+
+static void qglfResolveStencilMaskSeparate(GLenum face, GLuint mask)
+{
+ typedef void (QGLF_APIENTRYP type_glStencilMaskSeparate)(GLenum face, GLuint mask);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->stencilMaskSeparate = (type_glStencilMaskSeparate)
+ context->getProcAddress(QLatin1String("glStencilMaskSeparate"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->stencilMaskSeparate) {
+ funcs->stencilMaskSeparate = (type_glStencilMaskSeparate)
+ context->getProcAddress(QLatin1String("glStencilMaskSeparateOES"));
+ }
+#endif
+ if (!funcs->stencilMaskSeparate) {
+ funcs->stencilMaskSeparate = (type_glStencilMaskSeparate)
+ context->getProcAddress(QLatin1String("glStencilMaskSeparateEXT"));
+ }
+ if (!funcs->stencilMaskSeparate) {
+ funcs->stencilMaskSeparate = (type_glStencilMaskSeparate)
+ context->getProcAddress(QLatin1String("glStencilMaskSeparateARB"));
+ }
+
+ if (funcs->stencilMaskSeparate)
+ funcs->stencilMaskSeparate(face, mask);
+ else
+ funcs->stencilMaskSeparate = qglfResolveStencilMaskSeparate;
+}
+
+static void qglfResolveStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass)
+{
+ typedef void (QGLF_APIENTRYP type_glStencilOpSeparate)(GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->stencilOpSeparate = (type_glStencilOpSeparate)
+ context->getProcAddress(QLatin1String("glStencilOpSeparate"));
+#ifdef QT_OPENGL_ES
+ if (!funcs->stencilOpSeparate) {
+ funcs->stencilOpSeparate = (type_glStencilOpSeparate)
+ context->getProcAddress(QLatin1String("glStencilOpSeparateOES"));
+ }
+#endif
+ if (!funcs->stencilOpSeparate) {
+ funcs->stencilOpSeparate = (type_glStencilOpSeparate)
+ context->getProcAddress(QLatin1String("glStencilOpSeparateEXT"));
+ }
+ if (!funcs->stencilOpSeparate) {
+ funcs->stencilOpSeparate = (type_glStencilOpSeparate)
+ context->getProcAddress(QLatin1String("glStencilOpSeparateARB"));
+ }
+
+ if (funcs->stencilOpSeparate)
+ funcs->stencilOpSeparate(face, fail, zfail, zpass);
+ else
+ funcs->stencilOpSeparate = qglfResolveStencilOpSeparate;
+}
+
+static void qglfResolveUniform1f(GLint location, GLfloat x)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform1f)(GLint location, GLfloat x);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform1f = (type_glUniform1f)
+ context->getProcAddress(QLatin1String("glUniform1f"));
+ if (!funcs->uniform1f) {
+ funcs->uniform1f = (type_glUniform1f)
+ context->getProcAddress(QLatin1String("glUniform1fARB"));
+ }
+
+ if (funcs->uniform1f)
+ funcs->uniform1f(location, x);
+ else
+ funcs->uniform1f = qglfResolveUniform1f;
+}
+
+static void qglfResolveUniform1fv(GLint location, GLsizei count, const GLfloat* v)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform1fv)(GLint location, GLsizei count, const GLfloat* v);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform1fv = (type_glUniform1fv)
+ context->getProcAddress(QLatin1String("glUniform1fv"));
+ if (!funcs->uniform1fv) {
+ funcs->uniform1fv = (type_glUniform1fv)
+ context->getProcAddress(QLatin1String("glUniform1fvARB"));
+ }
+
+ if (funcs->uniform1fv)
+ funcs->uniform1fv(location, count, v);
+ else
+ funcs->uniform1fv = qglfResolveUniform1fv;
+}
+
+static void qglfResolveUniform1i(GLint location, GLint x)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform1i)(GLint location, GLint x);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform1i = (type_glUniform1i)
+ context->getProcAddress(QLatin1String("glUniform1i"));
+ if (!funcs->uniform1i) {
+ funcs->uniform1i = (type_glUniform1i)
+ context->getProcAddress(QLatin1String("glUniform1iARB"));
+ }
+
+ if (funcs->uniform1i)
+ funcs->uniform1i(location, x);
+ else
+ funcs->uniform1i = qglfResolveUniform1i;
+}
+
+static void qglfResolveUniform1iv(GLint location, GLsizei count, const GLint* v)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform1iv)(GLint location, GLsizei count, const GLint* v);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform1iv = (type_glUniform1iv)
+ context->getProcAddress(QLatin1String("glUniform1iv"));
+ if (!funcs->uniform1iv) {
+ funcs->uniform1iv = (type_glUniform1iv)
+ context->getProcAddress(QLatin1String("glUniform1ivARB"));
+ }
+
+ if (funcs->uniform1iv)
+ funcs->uniform1iv(location, count, v);
+ else
+ funcs->uniform1iv = qglfResolveUniform1iv;
+}
+
+static void qglfResolveUniform2f(GLint location, GLfloat x, GLfloat y)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform2f)(GLint location, GLfloat x, GLfloat y);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform2f = (type_glUniform2f)
+ context->getProcAddress(QLatin1String("glUniform2f"));
+ if (!funcs->uniform2f) {
+ funcs->uniform2f = (type_glUniform2f)
+ context->getProcAddress(QLatin1String("glUniform2fARB"));
+ }
+
+ if (funcs->uniform2f)
+ funcs->uniform2f(location, x, y);
+ else
+ funcs->uniform2f = qglfResolveUniform2f;
+}
+
+static void qglfResolveUniform2fv(GLint location, GLsizei count, const GLfloat* v)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform2fv)(GLint location, GLsizei count, const GLfloat* v);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform2fv = (type_glUniform2fv)
+ context->getProcAddress(QLatin1String("glUniform2fv"));
+ if (!funcs->uniform2fv) {
+ funcs->uniform2fv = (type_glUniform2fv)
+ context->getProcAddress(QLatin1String("glUniform2fvARB"));
+ }
+
+ if (funcs->uniform2fv)
+ funcs->uniform2fv(location, count, v);
+ else
+ funcs->uniform2fv = qglfResolveUniform2fv;
+}
+
+static void qglfResolveUniform2i(GLint location, GLint x, GLint y)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform2i)(GLint location, GLint x, GLint y);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform2i = (type_glUniform2i)
+ context->getProcAddress(QLatin1String("glUniform2i"));
+ if (!funcs->uniform2i) {
+ funcs->uniform2i = (type_glUniform2i)
+ context->getProcAddress(QLatin1String("glUniform2iARB"));
+ }
+
+ if (funcs->uniform2i)
+ funcs->uniform2i(location, x, y);
+ else
+ funcs->uniform2i = qglfResolveUniform2i;
+}
+
+static void qglfResolveUniform2iv(GLint location, GLsizei count, const GLint* v)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform2iv)(GLint location, GLsizei count, const GLint* v);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform2iv = (type_glUniform2iv)
+ context->getProcAddress(QLatin1String("glUniform2iv"));
+ if (!funcs->uniform2iv) {
+ funcs->uniform2iv = (type_glUniform2iv)
+ context->getProcAddress(QLatin1String("glUniform2ivARB"));
+ }
+
+ if (funcs->uniform2iv)
+ funcs->uniform2iv(location, count, v);
+ else
+ funcs->uniform2iv = qglfResolveUniform2iv;
+}
+
+static void qglfResolveUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform3f)(GLint location, GLfloat x, GLfloat y, GLfloat z);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform3f = (type_glUniform3f)
+ context->getProcAddress(QLatin1String("glUniform3f"));
+ if (!funcs->uniform3f) {
+ funcs->uniform3f = (type_glUniform3f)
+ context->getProcAddress(QLatin1String("glUniform3fARB"));
+ }
+
+ if (funcs->uniform3f)
+ funcs->uniform3f(location, x, y, z);
+ else
+ funcs->uniform3f = qglfResolveUniform3f;
+}
+
+static void qglfResolveUniform3fv(GLint location, GLsizei count, const GLfloat* v)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform3fv)(GLint location, GLsizei count, const GLfloat* v);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform3fv = (type_glUniform3fv)
+ context->getProcAddress(QLatin1String("glUniform3fv"));
+ if (!funcs->uniform3fv) {
+ funcs->uniform3fv = (type_glUniform3fv)
+ context->getProcAddress(QLatin1String("glUniform3fvARB"));
+ }
+
+ if (funcs->uniform3fv)
+ funcs->uniform3fv(location, count, v);
+ else
+ funcs->uniform3fv = qglfResolveUniform3fv;
+}
+
+static void qglfResolveUniform3i(GLint location, GLint x, GLint y, GLint z)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform3i)(GLint location, GLint x, GLint y, GLint z);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform3i = (type_glUniform3i)
+ context->getProcAddress(QLatin1String("glUniform3i"));
+ if (!funcs->uniform3i) {
+ funcs->uniform3i = (type_glUniform3i)
+ context->getProcAddress(QLatin1String("glUniform3iARB"));
+ }
+
+ if (funcs->uniform3i)
+ funcs->uniform3i(location, x, y, z);
+ else
+ funcs->uniform3i = qglfResolveUniform3i;
+}
+
+static void qglfResolveUniform3iv(GLint location, GLsizei count, const GLint* v)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform3iv)(GLint location, GLsizei count, const GLint* v);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform3iv = (type_glUniform3iv)
+ context->getProcAddress(QLatin1String("glUniform3iv"));
+ if (!funcs->uniform3iv) {
+ funcs->uniform3iv = (type_glUniform3iv)
+ context->getProcAddress(QLatin1String("glUniform3ivARB"));
+ }
+
+ if (funcs->uniform3iv)
+ funcs->uniform3iv(location, count, v);
+ else
+ funcs->uniform3iv = qglfResolveUniform3iv;
+}
+
+static void qglfResolveUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform4f)(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform4f = (type_glUniform4f)
+ context->getProcAddress(QLatin1String("glUniform4f"));
+ if (!funcs->uniform4f) {
+ funcs->uniform4f = (type_glUniform4f)
+ context->getProcAddress(QLatin1String("glUniform4fARB"));
+ }
+
+ if (funcs->uniform4f)
+ funcs->uniform4f(location, x, y, z, w);
+ else
+ funcs->uniform4f = qglfResolveUniform4f;
+}
+
+static void qglfResolveUniform4fv(GLint location, GLsizei count, const GLfloat* v)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform4fv)(GLint location, GLsizei count, const GLfloat* v);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform4fv = (type_glUniform4fv)
+ context->getProcAddress(QLatin1String("glUniform4fv"));
+ if (!funcs->uniform4fv) {
+ funcs->uniform4fv = (type_glUniform4fv)
+ context->getProcAddress(QLatin1String("glUniform4fvARB"));
+ }
+
+ if (funcs->uniform4fv)
+ funcs->uniform4fv(location, count, v);
+ else
+ funcs->uniform4fv = qglfResolveUniform4fv;
+}
+
+static void qglfResolveUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform4i)(GLint location, GLint x, GLint y, GLint z, GLint w);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform4i = (type_glUniform4i)
+ context->getProcAddress(QLatin1String("glUniform4i"));
+ if (!funcs->uniform4i) {
+ funcs->uniform4i = (type_glUniform4i)
+ context->getProcAddress(QLatin1String("glUniform4iARB"));
+ }
+
+ if (funcs->uniform4i)
+ funcs->uniform4i(location, x, y, z, w);
+ else
+ funcs->uniform4i = qglfResolveUniform4i;
+}
+
+static void qglfResolveUniform4iv(GLint location, GLsizei count, const GLint* v)
+{
+ typedef void (QGLF_APIENTRYP type_glUniform4iv)(GLint location, GLsizei count, const GLint* v);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniform4iv = (type_glUniform4iv)
+ context->getProcAddress(QLatin1String("glUniform4iv"));
+ if (!funcs->uniform4iv) {
+ funcs->uniform4iv = (type_glUniform4iv)
+ context->getProcAddress(QLatin1String("glUniform4ivARB"));
+ }
+
+ if (funcs->uniform4iv)
+ funcs->uniform4iv(location, count, v);
+ else
+ funcs->uniform4iv = qglfResolveUniform4iv;
+}
+
+static void qglfResolveUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
+{
+ typedef void (QGLF_APIENTRYP type_glUniformMatrix2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniformMatrix2fv = (type_glUniformMatrix2fv)
+ context->getProcAddress(QLatin1String("glUniformMatrix2fv"));
+ if (!funcs->uniformMatrix2fv) {
+ funcs->uniformMatrix2fv = (type_glUniformMatrix2fv)
+ context->getProcAddress(QLatin1String("glUniformMatrix2fvARB"));
+ }
+
+ if (funcs->uniformMatrix2fv)
+ funcs->uniformMatrix2fv(location, count, transpose, value);
+ else
+ funcs->uniformMatrix2fv = qglfResolveUniformMatrix2fv;
+}
+
+static void qglfResolveUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
+{
+ typedef void (QGLF_APIENTRYP type_glUniformMatrix3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniformMatrix3fv = (type_glUniformMatrix3fv)
+ context->getProcAddress(QLatin1String("glUniformMatrix3fv"));
+ if (!funcs->uniformMatrix3fv) {
+ funcs->uniformMatrix3fv = (type_glUniformMatrix3fv)
+ context->getProcAddress(QLatin1String("glUniformMatrix3fvARB"));
+ }
+
+ if (funcs->uniformMatrix3fv)
+ funcs->uniformMatrix3fv(location, count, transpose, value);
+ else
+ funcs->uniformMatrix3fv = qglfResolveUniformMatrix3fv;
+}
+
+static void qglfResolveUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
+{
+ typedef void (QGLF_APIENTRYP type_glUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->uniformMatrix4fv = (type_glUniformMatrix4fv)
+ context->getProcAddress(QLatin1String("glUniformMatrix4fv"));
+ if (!funcs->uniformMatrix4fv) {
+ funcs->uniformMatrix4fv = (type_glUniformMatrix4fv)
+ context->getProcAddress(QLatin1String("glUniformMatrix4fvARB"));
+ }
+
+ if (funcs->uniformMatrix4fv)
+ funcs->uniformMatrix4fv(location, count, transpose, value);
+ else
+ funcs->uniformMatrix4fv = qglfResolveUniformMatrix4fv;
+}
+
+static void qglfResolveUseProgram(GLuint program)
+{
+ typedef void (QGLF_APIENTRYP type_glUseProgram)(GLuint program);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->useProgram = (type_glUseProgram)
+ context->getProcAddress(QLatin1String("glUseProgram"));
+ if (!funcs->useProgram) {
+ funcs->useProgram = (type_glUseProgram)
+ context->getProcAddress(QLatin1String("glUseProgramObjectARB"));
+ }
+
+ if (funcs->useProgram)
+ funcs->useProgram(program);
+ else
+ funcs->useProgram = qglfResolveUseProgram;
+}
+
+static void qglfResolveValidateProgram(GLuint program)
+{
+ typedef void (QGLF_APIENTRYP type_glValidateProgram)(GLuint program);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->validateProgram = (type_glValidateProgram)
+ context->getProcAddress(QLatin1String("glValidateProgram"));
+ if (!funcs->validateProgram) {
+ funcs->validateProgram = (type_glValidateProgram)
+ context->getProcAddress(QLatin1String("glValidateProgramARB"));
+ }
+
+ if (funcs->validateProgram)
+ funcs->validateProgram(program);
+ else
+ funcs->validateProgram = qglfResolveValidateProgram;
+}
+
+static void qglfResolveVertexAttrib1f(GLuint indx, GLfloat x)
+{
+ typedef void (QGLF_APIENTRYP type_glVertexAttrib1f)(GLuint indx, GLfloat x);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->vertexAttrib1f = (type_glVertexAttrib1f)
+ context->getProcAddress(QLatin1String("glVertexAttrib1f"));
+ if (!funcs->vertexAttrib1f) {
+ funcs->vertexAttrib1f = (type_glVertexAttrib1f)
+ context->getProcAddress(QLatin1String("glVertexAttrib1fARB"));
+ }
+
+ if (funcs->vertexAttrib1f)
+ funcs->vertexAttrib1f(indx, x);
+ else
+ funcs->vertexAttrib1f = qglfResolveVertexAttrib1f;
+}
+
+static void qglfResolveVertexAttrib1fv(GLuint indx, const GLfloat* values)
+{
+ typedef void (QGLF_APIENTRYP type_glVertexAttrib1fv)(GLuint indx, const GLfloat* values);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->vertexAttrib1fv = (type_glVertexAttrib1fv)
+ context->getProcAddress(QLatin1String("glVertexAttrib1fv"));
+ if (!funcs->vertexAttrib1fv) {
+ funcs->vertexAttrib1fv = (type_glVertexAttrib1fv)
+ context->getProcAddress(QLatin1String("glVertexAttrib1fvARB"));
+ }
+
+ if (funcs->vertexAttrib1fv)
+ funcs->vertexAttrib1fv(indx, values);
+ else
+ funcs->vertexAttrib1fv = qglfResolveVertexAttrib1fv;
+}
+
+static void qglfResolveVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y)
+{
+ typedef void (QGLF_APIENTRYP type_glVertexAttrib2f)(GLuint indx, GLfloat x, GLfloat y);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->vertexAttrib2f = (type_glVertexAttrib2f)
+ context->getProcAddress(QLatin1String("glVertexAttrib2f"));
+ if (!funcs->vertexAttrib2f) {
+ funcs->vertexAttrib2f = (type_glVertexAttrib2f)
+ context->getProcAddress(QLatin1String("glVertexAttrib2fARB"));
+ }
+
+ if (funcs->vertexAttrib2f)
+ funcs->vertexAttrib2f(indx, x, y);
+ else
+ funcs->vertexAttrib2f = qglfResolveVertexAttrib2f;
+}
+
+static void qglfResolveVertexAttrib2fv(GLuint indx, const GLfloat* values)
+{
+ typedef void (QGLF_APIENTRYP type_glVertexAttrib2fv)(GLuint indx, const GLfloat* values);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->vertexAttrib2fv = (type_glVertexAttrib2fv)
+ context->getProcAddress(QLatin1String("glVertexAttrib2fv"));
+ if (!funcs->vertexAttrib2fv) {
+ funcs->vertexAttrib2fv = (type_glVertexAttrib2fv)
+ context->getProcAddress(QLatin1String("glVertexAttrib2fvARB"));
+ }
+
+ if (funcs->vertexAttrib2fv)
+ funcs->vertexAttrib2fv(indx, values);
+ else
+ funcs->vertexAttrib2fv = qglfResolveVertexAttrib2fv;
+}
+
+static void qglfResolveVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z)
+{
+ typedef void (QGLF_APIENTRYP type_glVertexAttrib3f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->vertexAttrib3f = (type_glVertexAttrib3f)
+ context->getProcAddress(QLatin1String("glVertexAttrib3f"));
+ if (!funcs->vertexAttrib3f) {
+ funcs->vertexAttrib3f = (type_glVertexAttrib3f)
+ context->getProcAddress(QLatin1String("glVertexAttrib3fARB"));
+ }
+
+ if (funcs->vertexAttrib3f)
+ funcs->vertexAttrib3f(indx, x, y, z);
+ else
+ funcs->vertexAttrib3f = qglfResolveVertexAttrib3f;
+}
+
+static void qglfResolveVertexAttrib3fv(GLuint indx, const GLfloat* values)
+{
+ typedef void (QGLF_APIENTRYP type_glVertexAttrib3fv)(GLuint indx, const GLfloat* values);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->vertexAttrib3fv = (type_glVertexAttrib3fv)
+ context->getProcAddress(QLatin1String("glVertexAttrib3fv"));
+ if (!funcs->vertexAttrib3fv) {
+ funcs->vertexAttrib3fv = (type_glVertexAttrib3fv)
+ context->getProcAddress(QLatin1String("glVertexAttrib3fvARB"));
+ }
+
+ if (funcs->vertexAttrib3fv)
+ funcs->vertexAttrib3fv(indx, values);
+ else
+ funcs->vertexAttrib3fv = qglfResolveVertexAttrib3fv;
+}
+
+static void qglfResolveVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ typedef void (QGLF_APIENTRYP type_glVertexAttrib4f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->vertexAttrib4f = (type_glVertexAttrib4f)
+ context->getProcAddress(QLatin1String("glVertexAttrib4f"));
+ if (!funcs->vertexAttrib4f) {
+ funcs->vertexAttrib4f = (type_glVertexAttrib4f)
+ context->getProcAddress(QLatin1String("glVertexAttrib4fARB"));
+ }
+
+ if (funcs->vertexAttrib4f)
+ funcs->vertexAttrib4f(indx, x, y, z, w);
+ else
+ funcs->vertexAttrib4f = qglfResolveVertexAttrib4f;
+}
+
+static void qglfResolveVertexAttrib4fv(GLuint indx, const GLfloat* values)
+{
+ typedef void (QGLF_APIENTRYP type_glVertexAttrib4fv)(GLuint indx, const GLfloat* values);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->vertexAttrib4fv = (type_glVertexAttrib4fv)
+ context->getProcAddress(QLatin1String("glVertexAttrib4fv"));
+ if (!funcs->vertexAttrib4fv) {
+ funcs->vertexAttrib4fv = (type_glVertexAttrib4fv)
+ context->getProcAddress(QLatin1String("glVertexAttrib4fvARB"));
+ }
+
+ if (funcs->vertexAttrib4fv)
+ funcs->vertexAttrib4fv(indx, values);
+ else
+ funcs->vertexAttrib4fv = qglfResolveVertexAttrib4fv;
+}
+
+static void qglfResolveVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr)
+{
+ typedef void (QGLF_APIENTRYP type_glVertexAttribPointer)(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr);
+
+ const QGLContext *context = QGLContext::currentContext();
+ QGLFunctionsPrivate *funcs = qt_gl_functions(context);
+
+ funcs->vertexAttribPointer = (type_glVertexAttribPointer)
+ context->getProcAddress(QLatin1String("glVertexAttribPointer"));
+ if (!funcs->vertexAttribPointer) {
+ funcs->vertexAttribPointer = (type_glVertexAttribPointer)
+ context->getProcAddress(QLatin1String("glVertexAttribPointerARB"));
+ }
+
+ if (funcs->vertexAttribPointer)
+ funcs->vertexAttribPointer(indx, size, type, normalized, stride, ptr);
+ else
+ funcs->vertexAttribPointer = qglfResolveVertexAttribPointer;
+}
+
+#endif // !QT_OPENGL_ES_2
+
+QGLFunctionsPrivate::QGLFunctionsPrivate(const QGLContext *)
+{
+#ifndef QT_OPENGL_ES_2
+ activeTexture = qglfResolveActiveTexture;
+ attachShader = qglfResolveAttachShader;
+ bindAttribLocation = qglfResolveBindAttribLocation;
+ bindBuffer = qglfResolveBindBuffer;
+ bindFramebuffer = qglfResolveBindFramebuffer;
+ bindRenderbuffer = qglfResolveBindRenderbuffer;
+ blendColor = qglfResolveBlendColor;
+ blendEquation = qglfResolveBlendEquation;
+ blendEquationSeparate = qglfResolveBlendEquationSeparate;
+ blendFuncSeparate = qglfResolveBlendFuncSeparate;
+ bufferData = qglfResolveBufferData;
+ bufferSubData = qglfResolveBufferSubData;
+ checkFramebufferStatus = qglfResolveCheckFramebufferStatus;
+ compileShader = qglfResolveCompileShader;
+ compressedTexImage2D = qglfResolveCompressedTexImage2D;
+ compressedTexSubImage2D = qglfResolveCompressedTexSubImage2D;
+ createProgram = qglfResolveCreateProgram;
+ createShader = qglfResolveCreateShader;
+ deleteBuffers = qglfResolveDeleteBuffers;
+ deleteFramebuffers = qglfResolveDeleteFramebuffers;
+ deleteProgram = qglfResolveDeleteProgram;
+ deleteRenderbuffers = qglfResolveDeleteRenderbuffers;
+ deleteShader = qglfResolveDeleteShader;
+ detachShader = qglfResolveDetachShader;
+ disableVertexAttribArray = qglfResolveDisableVertexAttribArray;
+ enableVertexAttribArray = qglfResolveEnableVertexAttribArray;
+ framebufferRenderbuffer = qglfResolveFramebufferRenderbuffer;
+ framebufferTexture2D = qglfResolveFramebufferTexture2D;
+ genBuffers = qglfResolveGenBuffers;
+ generateMipmap = qglfResolveGenerateMipmap;
+ genFramebuffers = qglfResolveGenFramebuffers;
+ genRenderbuffers = qglfResolveGenRenderbuffers;
+ getActiveAttrib = qglfResolveGetActiveAttrib;
+ getActiveUniform = qglfResolveGetActiveUniform;
+ getAttachedShaders = qglfResolveGetAttachedShaders;
+ getAttribLocation = qglfResolveGetAttribLocation;
+ getBufferParameteriv = qglfResolveGetBufferParameteriv;
+ getFramebufferAttachmentParameteriv = qglfResolveGetFramebufferAttachmentParameteriv;
+ getProgramiv = qglfResolveGetProgramiv;
+ getProgramInfoLog = qglfResolveGetProgramInfoLog;
+ getRenderbufferParameteriv = qglfResolveGetRenderbufferParameteriv;
+ getShaderiv = qglfResolveGetShaderiv;
+ getShaderInfoLog = qglfResolveGetShaderInfoLog;
+ getShaderPrecisionFormat = qglfResolveGetShaderPrecisionFormat;
+ getShaderSource = qglfResolveGetShaderSource;
+ getUniformfv = qglfResolveGetUniformfv;
+ getUniformiv = qglfResolveGetUniformiv;
+ getUniformLocation = qglfResolveGetUniformLocation;
+ getVertexAttribfv = qglfResolveGetVertexAttribfv;
+ getVertexAttribiv = qglfResolveGetVertexAttribiv;
+ getVertexAttribPointerv = qglfResolveGetVertexAttribPointerv;
+ isBuffer = qglfResolveIsBuffer;
+ isFramebuffer = qglfResolveIsFramebuffer;
+ isProgram = qglfResolveIsProgram;
+ isRenderbuffer = qglfResolveIsRenderbuffer;
+ isShader = qglfResolveIsShader;
+ linkProgram = qglfResolveLinkProgram;
+ releaseShaderCompiler = qglfResolveReleaseShaderCompiler;
+ renderbufferStorage = qglfResolveRenderbufferStorage;
+ sampleCoverage = qglfResolveSampleCoverage;
+ shaderBinary = qglfResolveShaderBinary;
+ shaderSource = qglfResolveShaderSource;
+ stencilFuncSeparate = qglfResolveStencilFuncSeparate;
+ stencilMaskSeparate = qglfResolveStencilMaskSeparate;
+ stencilOpSeparate = qglfResolveStencilOpSeparate;
+ uniform1f = qglfResolveUniform1f;
+ uniform1fv = qglfResolveUniform1fv;
+ uniform1i = qglfResolveUniform1i;
+ uniform1iv = qglfResolveUniform1iv;
+ uniform2f = qglfResolveUniform2f;
+ uniform2fv = qglfResolveUniform2fv;
+ uniform2i = qglfResolveUniform2i;
+ uniform2iv = qglfResolveUniform2iv;
+ uniform3f = qglfResolveUniform3f;
+ uniform3fv = qglfResolveUniform3fv;
+ uniform3i = qglfResolveUniform3i;
+ uniform3iv = qglfResolveUniform3iv;
+ uniform4f = qglfResolveUniform4f;
+ uniform4fv = qglfResolveUniform4fv;
+ uniform4i = qglfResolveUniform4i;
+ uniform4iv = qglfResolveUniform4iv;
+ uniformMatrix2fv = qglfResolveUniformMatrix2fv;
+ uniformMatrix3fv = qglfResolveUniformMatrix3fv;
+ uniformMatrix4fv = qglfResolveUniformMatrix4fv;
+ useProgram = qglfResolveUseProgram;
+ validateProgram = qglfResolveValidateProgram;
+ vertexAttrib1f = qglfResolveVertexAttrib1f;
+ vertexAttrib1fv = qglfResolveVertexAttrib1fv;
+ vertexAttrib2f = qglfResolveVertexAttrib2f;
+ vertexAttrib2fv = qglfResolveVertexAttrib2fv;
+ vertexAttrib3f = qglfResolveVertexAttrib3f;
+ vertexAttrib3fv = qglfResolveVertexAttrib3fv;
+ vertexAttrib4f = qglfResolveVertexAttrib4f;
+ vertexAttrib4fv = qglfResolveVertexAttrib4fv;
+ vertexAttribPointer = qglfResolveVertexAttribPointer;
+#endif // !QT_OPENGL_ES_2
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglfunctions.h b/src/opengl/qglfunctions.h
new file mode 100644
index 0000000000..44d9bad9fa
--- /dev/null
+++ b/src/opengl/qglfunctions.h
@@ -0,0 +1,2295 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLFUNCTIONS_H
+#define QGLFUNCTIONS_H
+
+#ifdef __GLEW_H__
+#warning qglfunctions.h is not compatible with GLEW, GLEW defines will be undefined
+#warning To use GLEW with Qt, do not include <QtOpenGL> or <QGLFunctions> after glew.h
+#endif
+
+#include <QtOpenGL/qgl.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+// Types that aren't defined in all system's gl.h files.
+typedef ptrdiff_t qgl_GLintptr;
+typedef ptrdiff_t qgl_GLsizeiptr;
+
+#ifndef Q_WS_MAC
+# ifndef QGLF_APIENTRYP
+# ifdef QGLF_APIENTRY
+# define QGLF_APIENTRYP QGLF_APIENTRY *
+# else
+# define QGLF_APIENTRY
+# define QGLF_APIENTRYP *
+# endif
+# endif
+#else
+# define QGLF_APIENTRY
+# define QGLF_APIENTRYP *
+#endif
+
+struct QGLFunctionsPrivate;
+
+// Undefine any macros from GLEW, qglextensions_p.h, etc that
+// may interfere with the definition of QGLFunctions.
+#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_OPENGL_EXPORT QGLFunctions
+{
+public:
+ QGLFunctions();
+ explicit QGLFunctions(const QGLContext *context);
+ ~QGLFunctions() {}
+
+ 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)
+
+ QGLFunctions::OpenGLFeatures openGLFeatures() const;
+ bool hasOpenGLFeature(QGLFunctions::OpenGLFeature feature) const;
+
+ void initializeGLFunctions(const QGLContext *context = 0);
+
+ 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, qgl_GLsizeiptr size, const void* data, GLenum usage);
+ void glBufferSubData(GLenum target, qgl_GLintptr offset, qgl_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);
+ int 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);
+ int 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);
+
+private:
+ QGLFunctionsPrivate *d_ptr;
+ static bool isInitialized(const QGLFunctionsPrivate *d) { return d != 0; }
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGLFunctions::OpenGLFeatures)
+
+struct QGLFunctionsPrivate
+{
+ QGLFunctionsPrivate(const QGLContext *context = 0);
+
+#ifndef QT_OPENGL_ES_2
+ void (QGLF_APIENTRYP activeTexture)(GLenum texture);
+ void (QGLF_APIENTRYP attachShader)(GLuint program, GLuint shader);
+ void (QGLF_APIENTRYP bindAttribLocation)(GLuint program, GLuint index, const char* name);
+ void (QGLF_APIENTRYP bindBuffer)(GLenum target, GLuint buffer);
+ void (QGLF_APIENTRYP bindFramebuffer)(GLenum target, GLuint framebuffer);
+ void (QGLF_APIENTRYP bindRenderbuffer)(GLenum target, GLuint renderbuffer);
+ void (QGLF_APIENTRYP blendColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
+ void (QGLF_APIENTRYP blendEquation)(GLenum mode);
+ void (QGLF_APIENTRYP blendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha);
+ void (QGLF_APIENTRYP blendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
+ void (QGLF_APIENTRYP bufferData)(GLenum target, qgl_GLsizeiptr size, const void* data, GLenum usage);
+ void (QGLF_APIENTRYP bufferSubData)(GLenum target, qgl_GLintptr offset, qgl_GLsizeiptr size, const void* data);
+ GLenum (QGLF_APIENTRYP checkFramebufferStatus)(GLenum target);
+ void (QGLF_APIENTRYP compileShader)(GLuint shader);
+ void (QGLF_APIENTRYP compressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data);
+ void (QGLF_APIENTRYP compressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data);
+ GLuint (QGLF_APIENTRYP createProgram)();
+ GLuint (QGLF_APIENTRYP createShader)(GLenum type);
+ void (QGLF_APIENTRYP deleteBuffers)(GLsizei n, const GLuint* buffers);
+ void (QGLF_APIENTRYP deleteFramebuffers)(GLsizei n, const GLuint* framebuffers);
+ void (QGLF_APIENTRYP deleteProgram)(GLuint program);
+ void (QGLF_APIENTRYP deleteRenderbuffers)(GLsizei n, const GLuint* renderbuffers);
+ void (QGLF_APIENTRYP deleteShader)(GLuint shader);
+ void (QGLF_APIENTRYP detachShader)(GLuint program, GLuint shader);
+ void (QGLF_APIENTRYP disableVertexAttribArray)(GLuint index);
+ void (QGLF_APIENTRYP enableVertexAttribArray)(GLuint index);
+ void (QGLF_APIENTRYP framebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
+ void (QGLF_APIENTRYP framebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+ void (QGLF_APIENTRYP genBuffers)(GLsizei n, GLuint* buffers);
+ void (QGLF_APIENTRYP generateMipmap)(GLenum target);
+ void (QGLF_APIENTRYP genFramebuffers)(GLsizei n, GLuint* framebuffers);
+ void (QGLF_APIENTRYP genRenderbuffers)(GLsizei n, GLuint* renderbuffers);
+ void (QGLF_APIENTRYP getActiveAttrib)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name);
+ void (QGLF_APIENTRYP getActiveUniform)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name);
+ void (QGLF_APIENTRYP getAttachedShaders)(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders);
+ int (QGLF_APIENTRYP getAttribLocation)(GLuint program, const char* name);
+ void (QGLF_APIENTRYP getBufferParameteriv)(GLenum target, GLenum pname, GLint* params);
+ void (QGLF_APIENTRYP getFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint* params);
+ void (QGLF_APIENTRYP getProgramiv)(GLuint program, GLenum pname, GLint* params);
+ void (QGLF_APIENTRYP getProgramInfoLog)(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog);
+ void (QGLF_APIENTRYP getRenderbufferParameteriv)(GLenum target, GLenum pname, GLint* params);
+ void (QGLF_APIENTRYP getShaderiv)(GLuint shader, GLenum pname, GLint* params);
+ void (QGLF_APIENTRYP getShaderInfoLog)(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog);
+ void (QGLF_APIENTRYP getShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision);
+ void (QGLF_APIENTRYP getShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, char* source);
+ void (QGLF_APIENTRYP getUniformfv)(GLuint program, GLint location, GLfloat* params);
+ void (QGLF_APIENTRYP getUniformiv)(GLuint program, GLint location, GLint* params);
+ int (QGLF_APIENTRYP getUniformLocation)(GLuint program, const char* name);
+ void (QGLF_APIENTRYP getVertexAttribfv)(GLuint index, GLenum pname, GLfloat* params);
+ void (QGLF_APIENTRYP getVertexAttribiv)(GLuint index, GLenum pname, GLint* params);
+ void (QGLF_APIENTRYP getVertexAttribPointerv)(GLuint index, GLenum pname, void** pointer);
+ GLboolean (QGLF_APIENTRYP isBuffer)(GLuint buffer);
+ GLboolean (QGLF_APIENTRYP isFramebuffer)(GLuint framebuffer);
+ GLboolean (QGLF_APIENTRYP isProgram)(GLuint program);
+ GLboolean (QGLF_APIENTRYP isRenderbuffer)(GLuint renderbuffer);
+ GLboolean (QGLF_APIENTRYP isShader)(GLuint shader);
+ void (QGLF_APIENTRYP linkProgram)(GLuint program);
+ void (QGLF_APIENTRYP releaseShaderCompiler)();
+ void (QGLF_APIENTRYP renderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
+ void (QGLF_APIENTRYP sampleCoverage)(GLclampf value, GLboolean invert);
+ void (QGLF_APIENTRYP shaderBinary)(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length);
+ void (QGLF_APIENTRYP shaderSource)(GLuint shader, GLsizei count, const char** string, const GLint* length);
+ void (QGLF_APIENTRYP stencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask);
+ void (QGLF_APIENTRYP stencilMaskSeparate)(GLenum face, GLuint mask);
+ void (QGLF_APIENTRYP stencilOpSeparate)(GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
+ void (QGLF_APIENTRYP uniform1f)(GLint location, GLfloat x);
+ void (QGLF_APIENTRYP uniform1fv)(GLint location, GLsizei count, const GLfloat* v);
+ void (QGLF_APIENTRYP uniform1i)(GLint location, GLint x);
+ void (QGLF_APIENTRYP uniform1iv)(GLint location, GLsizei count, const GLint* v);
+ void (QGLF_APIENTRYP uniform2f)(GLint location, GLfloat x, GLfloat y);
+ void (QGLF_APIENTRYP uniform2fv)(GLint location, GLsizei count, const GLfloat* v);
+ void (QGLF_APIENTRYP uniform2i)(GLint location, GLint x, GLint y);
+ void (QGLF_APIENTRYP uniform2iv)(GLint location, GLsizei count, const GLint* v);
+ void (QGLF_APIENTRYP uniform3f)(GLint location, GLfloat x, GLfloat y, GLfloat z);
+ void (QGLF_APIENTRYP uniform3fv)(GLint location, GLsizei count, const GLfloat* v);
+ void (QGLF_APIENTRYP uniform3i)(GLint location, GLint x, GLint y, GLint z);
+ void (QGLF_APIENTRYP uniform3iv)(GLint location, GLsizei count, const GLint* v);
+ void (QGLF_APIENTRYP uniform4f)(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+ void (QGLF_APIENTRYP uniform4fv)(GLint location, GLsizei count, const GLfloat* v);
+ void (QGLF_APIENTRYP uniform4i)(GLint location, GLint x, GLint y, GLint z, GLint w);
+ void (QGLF_APIENTRYP uniform4iv)(GLint location, GLsizei count, const GLint* v);
+ void (QGLF_APIENTRYP uniformMatrix2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
+ void (QGLF_APIENTRYP uniformMatrix3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
+ void (QGLF_APIENTRYP uniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
+ void (QGLF_APIENTRYP useProgram)(GLuint program);
+ void (QGLF_APIENTRYP validateProgram)(GLuint program);
+ void (QGLF_APIENTRYP vertexAttrib1f)(GLuint indx, GLfloat x);
+ void (QGLF_APIENTRYP vertexAttrib1fv)(GLuint indx, const GLfloat* values);
+ void (QGLF_APIENTRYP vertexAttrib2f)(GLuint indx, GLfloat x, GLfloat y);
+ void (QGLF_APIENTRYP vertexAttrib2fv)(GLuint indx, const GLfloat* values);
+ void (QGLF_APIENTRYP vertexAttrib3f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z);
+ void (QGLF_APIENTRYP vertexAttrib3fv)(GLuint indx, const GLfloat* values);
+ void (QGLF_APIENTRYP vertexAttrib4f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+ void (QGLF_APIENTRYP vertexAttrib4fv)(GLuint indx, const GLfloat* values);
+ void (QGLF_APIENTRYP vertexAttribPointer)(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr);
+#endif
+};
+
+inline void QGLFunctions::glActiveTexture(GLenum texture)
+{
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
+ ::glActiveTexture(texture);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->activeTexture(texture);
+#endif
+}
+
+inline void QGLFunctions::glAttachShader(GLuint program, GLuint shader)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glAttachShader(program, shader);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->attachShader(program, shader);
+#endif
+}
+
+inline void QGLFunctions::glBindAttribLocation(GLuint program, GLuint index, const char* name)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glBindAttribLocation(program, index, name);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->bindAttribLocation(program, index, name);
+#endif
+}
+
+inline void QGLFunctions::glBindBuffer(GLenum target, GLuint buffer)
+{
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
+ ::glBindBuffer(target, buffer);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->bindBuffer(target, buffer);
+#endif
+}
+
+inline void QGLFunctions::glBindFramebuffer(GLenum target, GLuint framebuffer)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glBindFramebuffer(target, framebuffer);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->bindFramebuffer(target, framebuffer);
+#endif
+}
+
+inline void QGLFunctions::glBindRenderbuffer(GLenum target, GLuint renderbuffer)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glBindRenderbuffer(target, renderbuffer);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->bindRenderbuffer(target, renderbuffer);
+#endif
+}
+
+inline void QGLFunctions::glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glBlendColor(red, green, blue, alpha);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->blendColor(red, green, blue, alpha);
+#endif
+}
+
+inline void QGLFunctions::glBlendEquation(GLenum mode)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glBlendEquation(mode);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->blendEquation(mode);
+#endif
+}
+
+inline void QGLFunctions::glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glBlendEquationSeparate(modeRGB, modeAlpha);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->blendEquationSeparate(modeRGB, modeAlpha);
+#endif
+}
+
+inline void QGLFunctions::glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
+#endif
+}
+
+inline void QGLFunctions::glBufferData(GLenum target, qgl_GLsizeiptr size, const void* data, GLenum usage)
+{
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
+ ::glBufferData(target, size, data, usage);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->bufferData(target, size, data, usage);
+#endif
+}
+
+inline void QGLFunctions::glBufferSubData(GLenum target, qgl_GLintptr offset, qgl_GLsizeiptr size, const void* data)
+{
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
+ ::glBufferSubData(target, offset, size, data);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->bufferSubData(target, offset, size, data);
+#endif
+}
+
+inline GLenum QGLFunctions::glCheckFramebufferStatus(GLenum target)
+{
+#if defined(QT_OPENGL_ES_2)
+ return ::glCheckFramebufferStatus(target);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ return d_ptr->checkFramebufferStatus(target);
+#endif
+}
+
+inline void QGLFunctions::glClearDepthf(GLclampf depth)
+{
+#ifndef QT_OPENGL_ES
+ ::glClearDepth(depth);
+#else
+ ::glClearDepthf(depth);
+#endif
+}
+
+inline void QGLFunctions::glCompileShader(GLuint shader)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glCompileShader(shader);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->compileShader(shader);
+#endif
+}
+
+inline void QGLFunctions::glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data)
+{
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
+ ::glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->compressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data);
+#endif
+}
+
+inline void QGLFunctions::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_1) || defined(QT_OPENGL_ES_2)
+ ::glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data);
+#endif
+}
+
+inline GLuint QGLFunctions::glCreateProgram()
+{
+#if defined(QT_OPENGL_ES_2)
+ return ::glCreateProgram();
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ return d_ptr->createProgram();
+#endif
+}
+
+inline GLuint QGLFunctions::glCreateShader(GLenum type)
+{
+#if defined(QT_OPENGL_ES_2)
+ return ::glCreateShader(type);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ return d_ptr->createShader(type);
+#endif
+}
+
+inline void QGLFunctions::glDeleteBuffers(GLsizei n, const GLuint* buffers)
+{
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
+ ::glDeleteBuffers(n, buffers);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->deleteBuffers(n, buffers);
+#endif
+}
+
+inline void QGLFunctions::glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glDeleteFramebuffers(n, framebuffers);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->deleteFramebuffers(n, framebuffers);
+#endif
+}
+
+inline void QGLFunctions::glDeleteProgram(GLuint program)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glDeleteProgram(program);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->deleteProgram(program);
+#endif
+}
+
+inline void QGLFunctions::glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glDeleteRenderbuffers(n, renderbuffers);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->deleteRenderbuffers(n, renderbuffers);
+#endif
+}
+
+inline void QGLFunctions::glDeleteShader(GLuint shader)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glDeleteShader(shader);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->deleteShader(shader);
+#endif
+}
+
+inline void QGLFunctions::glDepthRangef(GLclampf zNear, GLclampf zFar)
+{
+#ifndef QT_OPENGL_ES
+ ::glDepthRange(zNear, zFar);
+#else
+ ::glDepthRangef(zNear, zFar);
+#endif
+}
+
+inline void QGLFunctions::glDetachShader(GLuint program, GLuint shader)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glDetachShader(program, shader);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->detachShader(program, shader);
+#endif
+}
+
+inline void QGLFunctions::glDisableVertexAttribArray(GLuint index)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glDisableVertexAttribArray(index);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->disableVertexAttribArray(index);
+#endif
+}
+
+inline void QGLFunctions::glEnableVertexAttribArray(GLuint index)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glEnableVertexAttribArray(index);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->enableVertexAttribArray(index);
+#endif
+}
+
+inline void QGLFunctions::glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->framebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
+#endif
+}
+
+inline void QGLFunctions::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(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->framebufferTexture2D(target, attachment, textarget, texture, level);
+#endif
+}
+
+inline void QGLFunctions::glGenBuffers(GLsizei n, GLuint* buffers)
+{
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
+ ::glGenBuffers(n, buffers);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->genBuffers(n, buffers);
+#endif
+}
+
+inline void QGLFunctions::glGenerateMipmap(GLenum target)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGenerateMipmap(target);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->generateMipmap(target);
+#endif
+}
+
+inline void QGLFunctions::glGenFramebuffers(GLsizei n, GLuint* framebuffers)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGenFramebuffers(n, framebuffers);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->genFramebuffers(n, framebuffers);
+#endif
+}
+
+inline void QGLFunctions::glGenRenderbuffers(GLsizei n, GLuint* renderbuffers)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGenRenderbuffers(n, renderbuffers);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->genRenderbuffers(n, renderbuffers);
+#endif
+}
+
+inline void QGLFunctions::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(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getActiveAttrib(program, index, bufsize, length, size, type, name);
+#endif
+}
+
+inline void QGLFunctions::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(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getActiveUniform(program, index, bufsize, length, size, type, name);
+#endif
+}
+
+inline void QGLFunctions::glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetAttachedShaders(program, maxcount, count, shaders);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getAttachedShaders(program, maxcount, count, shaders);
+#endif
+}
+
+inline int QGLFunctions::glGetAttribLocation(GLuint program, const char* name)
+{
+#if defined(QT_OPENGL_ES_2)
+ return ::glGetAttribLocation(program, name);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ return d_ptr->getAttribLocation(program, name);
+#endif
+}
+
+inline void QGLFunctions::glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetBufferParameteriv(target, pname, params);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getBufferParameteriv(target, pname, params);
+#endif
+}
+
+inline void QGLFunctions::glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetFramebufferAttachmentParameteriv(target, attachment, pname, params);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getFramebufferAttachmentParameteriv(target, attachment, pname, params);
+#endif
+}
+
+inline void QGLFunctions::glGetProgramiv(GLuint program, GLenum pname, GLint* params)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetProgramiv(program, pname, params);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getProgramiv(program, pname, params);
+#endif
+}
+
+inline void QGLFunctions::glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetProgramInfoLog(program, bufsize, length, infolog);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getProgramInfoLog(program, bufsize, length, infolog);
+#endif
+}
+
+inline void QGLFunctions::glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetRenderbufferParameteriv(target, pname, params);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getRenderbufferParameteriv(target, pname, params);
+#endif
+}
+
+inline void QGLFunctions::glGetShaderiv(GLuint shader, GLenum pname, GLint* params)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetShaderiv(shader, pname, params);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getShaderiv(shader, pname, params);
+#endif
+}
+
+inline void QGLFunctions::glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetShaderInfoLog(shader, bufsize, length, infolog);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getShaderInfoLog(shader, bufsize, length, infolog);
+#endif
+}
+
+inline void QGLFunctions::glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getShaderPrecisionFormat(shadertype, precisiontype, range, precision);
+#endif
+}
+
+inline void QGLFunctions::glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetShaderSource(shader, bufsize, length, source);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getShaderSource(shader, bufsize, length, source);
+#endif
+}
+
+inline void QGLFunctions::glGetUniformfv(GLuint program, GLint location, GLfloat* params)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetUniformfv(program, location, params);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getUniformfv(program, location, params);
+#endif
+}
+
+inline void QGLFunctions::glGetUniformiv(GLuint program, GLint location, GLint* params)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetUniformiv(program, location, params);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getUniformiv(program, location, params);
+#endif
+}
+
+inline int QGLFunctions::glGetUniformLocation(GLuint program, const char* name)
+{
+#if defined(QT_OPENGL_ES_2)
+ return ::glGetUniformLocation(program, name);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ return d_ptr->getUniformLocation(program, name);
+#endif
+}
+
+inline void QGLFunctions::glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetVertexAttribfv(index, pname, params);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getVertexAttribfv(index, pname, params);
+#endif
+}
+
+inline void QGLFunctions::glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetVertexAttribiv(index, pname, params);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getVertexAttribiv(index, pname, params);
+#endif
+}
+
+inline void QGLFunctions::glGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glGetVertexAttribPointerv(index, pname, pointer);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->getVertexAttribPointerv(index, pname, pointer);
+#endif
+}
+
+inline GLboolean QGLFunctions::glIsBuffer(GLuint buffer)
+{
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
+ return ::glIsBuffer(buffer);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ return d_ptr->isBuffer(buffer);
+#endif
+}
+
+inline GLboolean QGLFunctions::glIsFramebuffer(GLuint framebuffer)
+{
+#if defined(QT_OPENGL_ES_2)
+ return ::glIsFramebuffer(framebuffer);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ return d_ptr->isFramebuffer(framebuffer);
+#endif
+}
+
+inline GLboolean QGLFunctions::glIsProgram(GLuint program)
+{
+#if defined(QT_OPENGL_ES_2)
+ return ::glIsProgram(program);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ return d_ptr->isProgram(program);
+#endif
+}
+
+inline GLboolean QGLFunctions::glIsRenderbuffer(GLuint renderbuffer)
+{
+#if defined(QT_OPENGL_ES_2)
+ return ::glIsRenderbuffer(renderbuffer);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ return d_ptr->isRenderbuffer(renderbuffer);
+#endif
+}
+
+inline GLboolean QGLFunctions::glIsShader(GLuint shader)
+{
+#if defined(QT_OPENGL_ES_2)
+ return ::glIsShader(shader);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ return d_ptr->isShader(shader);
+#endif
+}
+
+inline void QGLFunctions::glLinkProgram(GLuint program)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glLinkProgram(program);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->linkProgram(program);
+#endif
+}
+
+inline void QGLFunctions::glReleaseShaderCompiler()
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glReleaseShaderCompiler();
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->releaseShaderCompiler();
+#endif
+}
+
+inline void QGLFunctions::glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glRenderbufferStorage(target, internalformat, width, height);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->renderbufferStorage(target, internalformat, width, height);
+#endif
+}
+
+inline void QGLFunctions::glSampleCoverage(GLclampf value, GLboolean invert)
+{
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
+ ::glSampleCoverage(value, invert);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->sampleCoverage(value, invert);
+#endif
+}
+
+inline void QGLFunctions::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(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->shaderBinary(n, shaders, binaryformat, binary, length);
+#endif
+}
+
+inline void QGLFunctions::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(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->shaderSource(shader, count, string, length);
+#endif
+}
+
+inline void QGLFunctions::glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glStencilFuncSeparate(face, func, ref, mask);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->stencilFuncSeparate(face, func, ref, mask);
+#endif
+}
+
+inline void QGLFunctions::glStencilMaskSeparate(GLenum face, GLuint mask)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glStencilMaskSeparate(face, mask);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->stencilMaskSeparate(face, mask);
+#endif
+}
+
+inline void QGLFunctions::glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glStencilOpSeparate(face, fail, zfail, zpass);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->stencilOpSeparate(face, fail, zfail, zpass);
+#endif
+}
+
+inline void QGLFunctions::glUniform1f(GLint location, GLfloat x)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform1f(location, x);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform1f(location, x);
+#endif
+}
+
+inline void QGLFunctions::glUniform1fv(GLint location, GLsizei count, const GLfloat* v)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform1fv(location, count, v);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform1fv(location, count, v);
+#endif
+}
+
+inline void QGLFunctions::glUniform1i(GLint location, GLint x)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform1i(location, x);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform1i(location, x);
+#endif
+}
+
+inline void QGLFunctions::glUniform1iv(GLint location, GLsizei count, const GLint* v)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform1iv(location, count, v);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform1iv(location, count, v);
+#endif
+}
+
+inline void QGLFunctions::glUniform2f(GLint location, GLfloat x, GLfloat y)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform2f(location, x, y);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform2f(location, x, y);
+#endif
+}
+
+inline void QGLFunctions::glUniform2fv(GLint location, GLsizei count, const GLfloat* v)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform2fv(location, count, v);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform2fv(location, count, v);
+#endif
+}
+
+inline void QGLFunctions::glUniform2i(GLint location, GLint x, GLint y)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform2i(location, x, y);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform2i(location, x, y);
+#endif
+}
+
+inline void QGLFunctions::glUniform2iv(GLint location, GLsizei count, const GLint* v)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform2iv(location, count, v);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform2iv(location, count, v);
+#endif
+}
+
+inline void QGLFunctions::glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform3f(location, x, y, z);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform3f(location, x, y, z);
+#endif
+}
+
+inline void QGLFunctions::glUniform3fv(GLint location, GLsizei count, const GLfloat* v)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform3fv(location, count, v);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform3fv(location, count, v);
+#endif
+}
+
+inline void QGLFunctions::glUniform3i(GLint location, GLint x, GLint y, GLint z)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform3i(location, x, y, z);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform3i(location, x, y, z);
+#endif
+}
+
+inline void QGLFunctions::glUniform3iv(GLint location, GLsizei count, const GLint* v)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform3iv(location, count, v);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform3iv(location, count, v);
+#endif
+}
+
+inline void QGLFunctions::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(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform4f(location, x, y, z, w);
+#endif
+}
+
+inline void QGLFunctions::glUniform4fv(GLint location, GLsizei count, const GLfloat* v)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform4fv(location, count, v);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform4fv(location, count, v);
+#endif
+}
+
+inline void QGLFunctions::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(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform4i(location, x, y, z, w);
+#endif
+}
+
+inline void QGLFunctions::glUniform4iv(GLint location, GLsizei count, const GLint* v)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniform4iv(location, count, v);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniform4iv(location, count, v);
+#endif
+}
+
+inline void QGLFunctions::glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniformMatrix2fv(location, count, transpose, value);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniformMatrix2fv(location, count, transpose, value);
+#endif
+}
+
+inline void QGLFunctions::glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniformMatrix3fv(location, count, transpose, value);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniformMatrix3fv(location, count, transpose, value);
+#endif
+}
+
+inline void QGLFunctions::glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUniformMatrix4fv(location, count, transpose, value);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->uniformMatrix4fv(location, count, transpose, value);
+#endif
+}
+
+inline void QGLFunctions::glUseProgram(GLuint program)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glUseProgram(program);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->useProgram(program);
+#endif
+}
+
+inline void QGLFunctions::glValidateProgram(GLuint program)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glValidateProgram(program);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->validateProgram(program);
+#endif
+}
+
+inline void QGLFunctions::glVertexAttrib1f(GLuint indx, GLfloat x)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glVertexAttrib1f(indx, x);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->vertexAttrib1f(indx, x);
+#endif
+}
+
+inline void QGLFunctions::glVertexAttrib1fv(GLuint indx, const GLfloat* values)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glVertexAttrib1fv(indx, values);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->vertexAttrib1fv(indx, values);
+#endif
+}
+
+inline void QGLFunctions::glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glVertexAttrib2f(indx, x, y);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->vertexAttrib2f(indx, x, y);
+#endif
+}
+
+inline void QGLFunctions::glVertexAttrib2fv(GLuint indx, const GLfloat* values)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glVertexAttrib2fv(indx, values);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->vertexAttrib2fv(indx, values);
+#endif
+}
+
+inline void QGLFunctions::glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glVertexAttrib3f(indx, x, y, z);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->vertexAttrib3f(indx, x, y, z);
+#endif
+}
+
+inline void QGLFunctions::glVertexAttrib3fv(GLuint indx, const GLfloat* values)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glVertexAttrib3fv(indx, values);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->vertexAttrib3fv(indx, values);
+#endif
+}
+
+inline void QGLFunctions::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(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->vertexAttrib4f(indx, x, y, z, w);
+#endif
+}
+
+inline void QGLFunctions::glVertexAttrib4fv(GLuint indx, const GLfloat* values)
+{
+#if defined(QT_OPENGL_ES_2)
+ ::glVertexAttrib4fv(indx, values);
+#else
+ Q_ASSERT(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->vertexAttrib4fv(indx, values);
+#endif
+}
+
+inline void QGLFunctions::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(QGLFunctions::isInitialized(d_ptr));
+ d_ptr->vertexAttribPointer(indx, size, type, normalized, stride, ptr);
+#endif
+}
+
+#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_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/opengl/qglpaintdevice.cpp b/src/opengl/qglpaintdevice.cpp
new file mode 100644
index 0000000000..61d8f26fda
--- /dev/null
+++ b/src/opengl/qglpaintdevice.cpp
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qglpaintdevice_p.h>
+#include <private/qgl_p.h>
+#include <private/qglpixelbuffer_p.h>
+#include <private/qglframebufferobject_p.h>
+#ifdef Q_WS_X11
+#include <private/qpixmapdata_x11gl_p.h>
+#endif
+
+#if !defined(QT_OPENGL_ES_1)
+#include <private/qpixmapdata_gl_p.h>
+#include <private/qwindowsurface_gl_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QGLPaintDevice::QGLPaintDevice()
+ : m_thisFBO(0)
+{
+}
+
+QGLPaintDevice::~QGLPaintDevice()
+{
+}
+
+int QGLPaintDevice::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ switch(metric) {
+ case PdmWidth:
+ return size().width();
+ case PdmHeight:
+ return size().height();
+ case PdmDepth: {
+ const QGLFormat f = format();
+ return f.redBufferSize() + f.greenBufferSize() + f.blueBufferSize() + f.alphaBufferSize();
+ }
+ default:
+ qWarning("QGLPaintDevice::metric() - metric %d not known", metric);
+ return 0;
+ }
+}
+
+void QGLPaintDevice::beginPaint()
+{
+ // Make sure our context is the current one:
+ QGLContext *ctx = context();
+ if (ctx != QGLContext::currentContext())
+ ctx->makeCurrent();
+
+ // 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_ptr->current_fbo = m_thisFBO;
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_thisFBO);
+ }
+
+ // Set the default fbo for the context to m_thisFBO so that
+ // if some raw GL code between beginNativePainting() and
+ // endNativePainting() calls QGLFramebufferObject::release(),
+ // painting will revert to the window surface's fbo.
+ ctx->d_ptr->default_fbo = m_thisFBO;
+}
+
+void QGLPaintDevice::ensureActiveTarget()
+{
+ QGLContext* ctx = context();
+ if (ctx != QGLContext::currentContext())
+ ctx->makeCurrent();
+
+ if (ctx->d_ptr->current_fbo != m_thisFBO) {
+ ctx->d_ptr->current_fbo = m_thisFBO;
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_thisFBO);
+ }
+
+ ctx->d_ptr->default_fbo = m_thisFBO;
+}
+
+void QGLPaintDevice::endPaint()
+{
+ // Make sure the FBO bound at beginPaint is re-bound again here:
+ QGLContext *ctx = context();
+ if (m_previousFBO != ctx->d_func()->current_fbo) {
+ ctx->d_ptr->current_fbo = m_previousFBO;
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_previousFBO);
+ }
+
+ ctx->d_ptr->default_fbo = 0;
+}
+
+QGLFormat QGLPaintDevice::format() const
+{
+ return context()->format();
+}
+
+bool QGLPaintDevice::alphaRequested() const
+{
+ return context()->d_func()->reqFormat.alpha();
+}
+
+bool QGLPaintDevice::isFlipped() const
+{
+ return false;
+}
+
+////////////////// QGLWidgetGLPaintDevice //////////////////
+
+QGLWidgetGLPaintDevice::QGLWidgetGLPaintDevice()
+{
+}
+
+QPaintEngine* QGLWidgetGLPaintDevice::paintEngine() const
+{
+ return glWidget->paintEngine();
+}
+
+void QGLWidgetGLPaintDevice::setWidget(QGLWidget* w)
+{
+ glWidget = w;
+}
+
+void QGLWidgetGLPaintDevice::beginPaint()
+{
+ QGLPaintDevice::beginPaint();
+ if (!glWidget->d_func()->disable_clear_on_painter_begin && glWidget->autoFillBackground()) {
+ if (glWidget->testAttribute(Qt::WA_TranslucentBackground))
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ else {
+ const QColor &c = glWidget->palette().brush(glWidget->backgroundRole()).color();
+ float alpha = c.alphaF();
+ glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha);
+ }
+ if (context()->d_func()->workaround_needsFullClearOnEveryFrame)
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ else
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+}
+
+void QGLWidgetGLPaintDevice::endPaint()
+{
+ if (glWidget->autoBufferSwap())
+ glWidget->swapBuffers();
+ QGLPaintDevice::endPaint();
+}
+
+
+QSize QGLWidgetGLPaintDevice::size() const
+{
+ return glWidget->size();
+}
+
+QGLContext* QGLWidgetGLPaintDevice::context() const
+{
+ return const_cast<QGLContext*>(glWidget->context());
+}
+
+// returns the QGLPaintDevice for the given QPaintDevice
+QGLPaintDevice* QGLPaintDevice::getDevice(QPaintDevice* pd)
+{
+ QGLPaintDevice* glpd = 0;
+
+ switch(pd->devType()) {
+ case QInternal::Widget:
+ // Should not be called on a non-gl widget:
+ Q_ASSERT(qobject_cast<QGLWidget*>(static_cast<QWidget*>(pd)));
+ glpd = &(static_cast<QGLWidget*>(pd)->d_func()->glDevice);
+ break;
+ case QInternal::Pbuffer:
+ glpd = &(static_cast<QGLPixelBuffer*>(pd)->d_func()->glDevice);
+ break;
+ case QInternal::FramebufferObject:
+ glpd = &(static_cast<QGLFramebufferObject*>(pd)->d_func()->glDevice);
+ break;
+ case QInternal::Pixmap: {
+#if !defined(QT_OPENGL_ES_1)
+ QPixmapData* pmd = static_cast<QPixmap*>(pd)->pixmapData();
+ if (pmd->classId() == QPixmapData::OpenGLClass)
+ glpd = static_cast<QGLPixmapData*>(pmd)->glDevice();
+#ifdef Q_WS_X11
+ else if (pmd->classId() == QPixmapData::X11Class)
+ glpd = static_cast<QX11GLPixmapData*>(pmd);
+#endif
+ else
+ qWarning("Pixmap type not supported for GL rendering");
+#else
+ qWarning("Pixmap render targets not supported on OpenGL ES 1.x");
+#endif
+ break;
+ }
+ default:
+ qWarning("QGLPaintDevice::getDevice() - Unknown device type %d", pd->devType());
+ break;
+ }
+
+ return glpd;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpaintdevice_p.h b/src/opengl/qglpaintdevice_p.h
new file mode 100644
index 0000000000..f4176fb2e7
--- /dev/null
+++ b/src/opengl/qglpaintdevice_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLPAINTDEVICE_P_H
+#define QGLPAINTDEVICE_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 QtOpenGL module. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+
+#include <qpaintdevice.h>
+#include <qgl.h>
+
+
+QT_BEGIN_NAMESPACE
+
+class Q_OPENGL_EXPORT QGLPaintDevice : public QPaintDevice
+{
+public:
+ QGLPaintDevice();
+ virtual ~QGLPaintDevice();
+
+ int devType() const {return QInternal::OpenGL;}
+
+ virtual void beginPaint();
+ virtual void ensureActiveTarget();
+ virtual void endPaint();
+
+ virtual QGLContext* context() const = 0;
+ virtual QGLFormat format() const;
+ virtual QSize size() const = 0;
+ virtual bool alphaRequested() const;
+ virtual bool isFlipped() const;
+
+ // returns the QGLPaintDevice for the given QPaintDevice
+ static QGLPaintDevice* getDevice(QPaintDevice*);
+
+protected:
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+ GLuint m_previousFBO;
+ GLuint m_thisFBO;
+};
+
+
+// Wraps a QGLWidget
+class QGLWidget;
+class QGLWidgetGLPaintDevice : public QGLPaintDevice
+{
+public:
+ QGLWidgetGLPaintDevice();
+
+ virtual QPaintEngine* paintEngine() const;
+
+ // QGLWidgets need to do swapBufers in endPaint:
+ virtual void beginPaint();
+ virtual void endPaint();
+ virtual QSize size() const;
+ virtual QGLContext* context() const;
+
+ void setWidget(QGLWidget*);
+
+private:
+ friend class QGLWidget;
+ QGLWidget *glWidget;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGLPAINTDEVICE_P_H
diff --git a/src/opengl/qglpixelbuffer.cpp b/src/opengl/qglpixelbuffer.cpp
new file mode 100644
index 0000000000..32785960e1
--- /dev/null
+++ b/src/opengl/qglpixelbuffer.cpp
@@ -0,0 +1,628 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGLPixelBuffer
+ \brief The QGLPixelBuffer class encapsulates an OpenGL pbuffer.
+ \since 4.1
+
+ \ingroup painting-3D
+
+ Rendering into a pbuffer is normally done using full hardware
+ acceleration. This can be significantly faster than rendering
+ into a QPixmap.
+
+ There are three approaches to using this class:
+
+ \list 1
+ \o \bold{We can draw into the pbuffer and convert it to a QImage
+ using toImage().} This is normally much faster than calling
+ QGLWidget::renderPixmap().
+
+ \o \bold{We can draw into the pbuffer and copy the contents into
+ an OpenGL texture using updateDynamicTexture().} This allows
+ us to create dynamic textures and works on all systems
+ with pbuffer support.
+
+ \o \bold{On systems that support it, we can bind the pbuffer to
+ an OpenGL texture.} The texture is then updated automatically
+ when the pbuffer contents change, eliminating the need for
+ additional copy operations. This is supported only on Windows
+ and Mac OS X systems that provide the \c render_texture
+ extension. Note that under Windows, a multi-sampled pbuffer
+ can't be used in conjunction with the \c render_texture
+ extension. If a multi-sampled pbuffer is requested under
+ Windows, the \c render_texture extension is turned off for that
+ pbuffer.
+
+
+ \endlist
+
+
+ \section Threading
+
+ As of Qt 4.8, it's possible to render into a QGLPixelBuffer using
+ a QPainter in a separate thread. Note that OpenGL 2.0 or OpenGL ES
+ 2.0 is required for this to work. Also, under X11, it's necessary
+ to set the Qt::AA_X11InitThreads application attribute.
+
+ Pbuffers are provided by the OpenGL \c pbuffer extension; call
+ hasOpenGLPbuffer() to find out if the system provides pbuffers.
+
+ \sa {opengl/pbuffers}{Pbuffers Example}
+*/
+
+#include <QtCore/qglobal.h>
+
+#if !defined(QT_OPENGL_ES_1)
+#include <private/qpaintengineex_opengl2_p.h>
+#endif
+
+#include <qglpixelbuffer.h>
+#include <private/qglpixelbuffer_p.h>
+#include <private/qfont_p.h>
+#include <qimage.h>
+
+#ifndef QT_OPENGL_ES_2
+#include <private/qpaintengine_opengl_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_OPENGL_ES_2)
+extern void qgl_cleanup_glyph_cache(QGLContext *);
+#else
+void qgl_cleanup_glyph_cache(QGLContext *) {}
+#endif
+
+extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool);
+
+
+QGLContext* QGLPBufferGLPaintDevice::context() const
+{
+ return pbuf->d_func()->qctx;
+}
+
+void QGLPBufferGLPaintDevice::endPaint() {
+ glFlush();
+ QGLPaintDevice::endPaint();
+}
+
+void QGLPBufferGLPaintDevice::setPBuffer(QGLPixelBuffer* pb)
+{
+ pbuf = pb;
+}
+
+void QGLPixelBufferPrivate::common_init(const QSize &size, const QGLFormat &format, QGLWidget *shareWidget)
+{
+ Q_Q(QGLPixelBuffer);
+ if(init(size, format, shareWidget)) {
+ req_size = size;
+ req_format = format;
+ req_shareWidget = shareWidget;
+ invalid = false;
+ qctx = new QGLContext(format);
+ qctx->d_func()->sharing = (shareWidget != 0);
+ if (shareWidget != 0 && shareWidget->d_func()->glcx) {
+ QGLContextGroup::addShare(qctx, shareWidget->d_func()->glcx);
+ shareWidget->d_func()->glcx->d_func()->sharing = true;
+ }
+
+ glDevice.setPBuffer(q);
+ qctx->d_func()->paintDevice = q;
+ qctx->d_func()->valid = true;
+#if defined(Q_WS_WIN) && !defined(QT_OPENGL_ES)
+ qctx->d_func()->dc = dc;
+ qctx->d_func()->rc = ctx;
+#elif (defined(Q_WS_X11) && defined(QT_NO_EGL))
+ qctx->d_func()->cx = ctx;
+ qctx->d_func()->pbuf = (void *) pbuf;
+ qctx->d_func()->vi = 0;
+#elif defined(Q_WS_MAC)
+ qctx->d_func()->cx = ctx;
+ qctx->d_func()->vi = 0;
+#elif !defined(QT_NO_EGL)
+ qctx->d_func()->eglContext = ctx;
+ qctx->d_func()->eglSurface = pbuf;
+#endif
+ }
+}
+
+/*!
+ Constructs an OpenGL pbuffer of the given \a size. If no \a
+ format is specified, the \l{QGLFormat::defaultFormat()}{default
+ format} is used. If the \a shareWidget parameter points to a
+ valid QGLWidget, the pbuffer will share its context with \a
+ shareWidget.
+
+ If you intend to bind this pbuffer as a dynamic texture, the width
+ and height components of \c size must be powers of two (e.g., 512
+ x 128).
+
+ \sa size(), format()
+*/
+QGLPixelBuffer::QGLPixelBuffer(const QSize &size, const QGLFormat &format, QGLWidget *shareWidget)
+ : d_ptr(new QGLPixelBufferPrivate(this))
+{
+ Q_D(QGLPixelBuffer);
+ d->common_init(size, format, shareWidget);
+}
+
+
+/*! \overload
+
+ Constructs an OpenGL pbuffer with the \a width and \a height. If
+ no \a format is specified, the
+ \l{QGLFormat::defaultFormat()}{default format} is used. If the \a
+ shareWidget parameter points to a valid QGLWidget, the pbuffer
+ will share its context with \a shareWidget.
+
+ If you intend to bind this pbuffer as a dynamic texture, the width
+ and height components of \c size must be powers of two (e.g., 512
+ x 128).
+
+ \sa size(), format()
+*/
+QGLPixelBuffer::QGLPixelBuffer(int width, int height, const QGLFormat &format, QGLWidget *shareWidget)
+ : d_ptr(new QGLPixelBufferPrivate(this))
+{
+ Q_D(QGLPixelBuffer);
+ d->common_init(QSize(width, height), format, shareWidget);
+}
+
+
+/*! \fn QGLPixelBuffer::~QGLPixelBuffer()
+
+ Destroys the pbuffer and frees any allocated resources.
+*/
+QGLPixelBuffer::~QGLPixelBuffer()
+{
+ Q_D(QGLPixelBuffer);
+
+ // defined in qpaintengine_opengl.cpp
+ QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext());
+ if (current != d->qctx)
+ makeCurrent();
+ qgl_cleanup_glyph_cache(d->qctx);
+ d->cleanup();
+ delete d->qctx;
+ if (current && current != d->qctx)
+ current->makeCurrent();
+}
+
+/*! \fn bool QGLPixelBuffer::makeCurrent()
+
+ Makes this pbuffer the current OpenGL rendering context. Returns
+ true on success; otherwise returns false.
+
+ \sa QGLContext::makeCurrent(), doneCurrent()
+*/
+
+bool QGLPixelBuffer::makeCurrent()
+{
+ Q_D(QGLPixelBuffer);
+ if (d->invalid)
+ return false;
+ d->qctx->makeCurrent();
+ return true;
+}
+
+/*! \fn bool QGLPixelBuffer::doneCurrent()
+
+ Makes no context the current OpenGL context. Returns true on
+ success; otherwise returns false.
+*/
+
+bool QGLPixelBuffer::doneCurrent()
+{
+ Q_D(QGLPixelBuffer);
+ if (d->invalid)
+ return false;
+ d->qctx->doneCurrent();
+ return true;
+}
+
+/*!
+ Generates and binds a 2D GL texture that is the same size as the
+ pbuffer, and returns the texture's ID. This can be used in
+ conjunction with bindToDynamicTexture() and
+ updateDynamicTexture().
+
+ \sa size()
+*/
+
+#if (defined(Q_WS_X11) || defined(Q_WS_WIN)) && defined(QT_NO_EGL)
+GLuint QGLPixelBuffer::generateDynamicTexture() const
+{
+ Q_D(const QGLPixelBuffer);
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, d->req_size.width(), d->req_size.height(), 0, GL_RGBA, GL_FLOAT, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ return texture;
+}
+#endif
+
+/*! \fn bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id)
+
+ Binds the texture specified by \a texture_id to this pbuffer.
+ Returns true on success; otherwise returns false.
+
+ The texture must be of the same size and format as the pbuffer.
+
+ To unbind the texture, call releaseFromDynamicTexture(). While
+ the texture is bound, it is updated automatically when the
+ pbuffer contents change, eliminating the need for additional copy
+ operations.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_opengl_qglpixelbuffer.cpp 0
+
+ \warning This function uses the \c {render_texture} extension,
+ which is currently not supported under X11. An alternative that
+ works on all systems (including X11) is to manually copy the
+ pbuffer contents to a texture using updateDynamicTexture().
+
+ \warning For the bindToDynamicTexture() call to succeed on the
+ Mac OS X, the pbuffer needs a shared context, i.e. the
+ QGLPixelBuffer must be created with a share widget.
+
+ \sa generateDynamicTexture(), releaseFromDynamicTexture()
+*/
+
+/*! \fn void QGLPixelBuffer::releaseFromDynamicTexture()
+
+ Releases the pbuffer from any previously bound texture.
+
+ \sa bindToDynamicTexture()
+*/
+
+/*! \fn bool QGLPixelBuffer::hasOpenGLPbuffers()
+
+ Returns true if the OpenGL \c pbuffer extension is present on
+ this system; otherwise returns false.
+*/
+
+/*!
+ Copies the pbuffer contents into the texture specified with \a
+ texture_id.
+
+ The texture must be of the same size and format as the pbuffer.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_opengl_qglpixelbuffer.cpp 1
+
+ An alternative on Windows and Mac OS X systems that support the
+ \c render_texture extension is to use bindToDynamicTexture() to
+ get dynamic updates of the texture.
+
+ \sa generateDynamicTexture(), bindToDynamicTexture()
+*/
+void QGLPixelBuffer::updateDynamicTexture(GLuint texture_id) const
+{
+ Q_D(const QGLPixelBuffer);
+ if (d->invalid)
+ return;
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+#ifndef QT_OPENGL_ES
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, d->req_size.width(), d->req_size.height(), 0);
+#else
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, d->req_size.width(), d->req_size.height(), 0);
+#endif
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+void QGLPixelBuffer::updateDynamicTexture(QMacCompatGLuint texture_id) const
+{
+ updateDynamicTexture(GLuint(texture_id));
+}
+#endif
+
+/*!
+ Returns the size of the pbuffer.
+*/
+QSize QGLPixelBuffer::size() const
+{
+ Q_D(const QGLPixelBuffer);
+ return d->req_size;
+}
+
+/*!
+ Returns the contents of the pbuffer as a QImage.
+*/
+QImage QGLPixelBuffer::toImage() const
+{
+ Q_D(const QGLPixelBuffer);
+ if (d->invalid)
+ return QImage();
+
+ const_cast<QGLPixelBuffer *>(this)->makeCurrent();
+ return qt_gl_read_framebuffer(d->req_size, d->format.alpha(), true);
+}
+
+/*!
+ Returns the native pbuffer handle.
+*/
+Qt::HANDLE QGLPixelBuffer::handle() const
+{
+ Q_D(const QGLPixelBuffer);
+ if (d->invalid)
+ return 0;
+ return (Qt::HANDLE) d->pbuf;
+}
+
+/*!
+ Returns true if this pbuffer is valid; otherwise returns false.
+*/
+bool QGLPixelBuffer::isValid() const
+{
+ Q_D(const QGLPixelBuffer);
+ return !d->invalid;
+}
+
+#if !defined(QT_OPENGL_ES_1)
+Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_buffer_2_engine)
+#endif
+
+#ifndef QT_OPENGL_ES_2
+Q_GLOBAL_STATIC(QGLEngineThreadStorage<QOpenGLPaintEngine>, qt_buffer_engine)
+#endif
+
+/*! \reimp */
+QPaintEngine *QGLPixelBuffer::paintEngine() const
+{
+#if defined(QT_OPENGL_ES_1)
+ return qt_buffer_engine()->engine();
+#elif defined(QT_OPENGL_ES_2)
+ return qt_buffer_2_engine()->engine();
+#else
+ if (qt_gl_preferGL2Engine())
+ return qt_buffer_2_engine()->engine();
+ else
+ return qt_buffer_engine()->engine();
+#endif
+}
+
+/*! \reimp */
+int QGLPixelBuffer::metric(PaintDeviceMetric metric) const
+{
+ Q_D(const QGLPixelBuffer);
+
+ float dpmx = qt_defaultDpiX()*100./2.54;
+ float dpmy = qt_defaultDpiY()*100./2.54;
+ int w = d->req_size.width();
+ int h = d->req_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("QGLPixelBuffer::metric(), Unhandled metric type: %d\n", metric);
+ break;
+ }
+ return 0;
+}
+
+/*!
+ Generates and binds a 2D GL texture to the current context, based
+ on \a image. The generated texture id is returned and can be used
+ in later glBindTexture() calls.
+
+ The \a target parameter specifies the texture target.
+
+ Equivalent to calling QGLContext::bindTexture().
+
+ \sa deleteTexture()
+*/
+GLuint QGLPixelBuffer::bindTexture(const QImage &image, GLenum target)
+{
+ Q_D(QGLPixelBuffer);
+#ifndef QT_OPENGL_ES
+ return d->qctx->bindTexture(image, target, GLint(GL_RGBA8));
+#else
+ return d->qctx->bindTexture(image, target, GL_RGBA);
+#endif
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+GLuint QGLPixelBuffer::bindTexture(const QImage &image, QMacCompatGLenum target)
+{
+ Q_D(QGLPixelBuffer);
+ return d->qctx->bindTexture(image, target, QMacCompatGLint(GL_RGBA8));
+}
+#endif
+
+/*! \overload
+
+ Generates and binds a 2D GL texture based on \a pixmap.
+
+ Equivalent to calling QGLContext::bindTexture().
+
+ \sa deleteTexture()
+*/
+GLuint QGLPixelBuffer::bindTexture(const QPixmap &pixmap, GLenum target)
+{
+ Q_D(QGLPixelBuffer);
+#ifndef QT_OPENGL_ES
+ return d->qctx->bindTexture(pixmap, target, GLint(GL_RGBA8));
+#else
+ return d->qctx->bindTexture(pixmap, target, GL_RGBA);
+#endif
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+GLuint QGLPixelBuffer::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target)
+{
+ Q_D(QGLPixelBuffer);
+ return d->qctx->bindTexture(pixmap, target, QMacCompatGLint(GL_RGBA8));
+}
+#endif
+
+/*! \overload
+
+ Reads the DirectDrawSurface (DDS) compressed file \a fileName and
+ generates a 2D GL texture from it.
+
+ Equivalent to calling QGLContext::bindTexture().
+
+ \sa deleteTexture()
+*/
+GLuint QGLPixelBuffer::bindTexture(const QString &fileName)
+{
+ Q_D(QGLPixelBuffer);
+ return d->qctx->bindTexture(fileName);
+}
+
+/*!
+ Removes the texture identified by \a texture_id from the texture cache.
+
+ Equivalent to calling QGLContext::deleteTexture().
+ */
+void QGLPixelBuffer::deleteTexture(GLuint texture_id)
+{
+ Q_D(QGLPixelBuffer);
+ d->qctx->deleteTexture(texture_id);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLPixelBuffer::deleteTexture(QMacCompatGLuint texture_id)
+{
+ Q_D(QGLPixelBuffer);
+ d->qctx->deleteTexture(texture_id);
+}
+#endif
+
+/*!
+ \since 4.4
+
+ Draws the given texture, \a textureId, to the given target rectangle,
+ \a target, in OpenGL model space. The \a textureTarget should be a 2D
+ texture target.
+
+ Equivalent to the corresponding QGLContext::drawTexture().
+*/
+void QGLPixelBuffer::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
+{
+ Q_D(QGLPixelBuffer);
+ d->qctx->drawTexture(target, textureId, textureTarget);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLPixelBuffer::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ Q_D(QGLPixelBuffer);
+ d->qctx->drawTexture(target, textureId, textureTarget);
+}
+#endif
+
+/*!
+ \since 4.4
+
+ Draws the given texture, \a textureId, at the given \a point in OpenGL model
+ space. The textureTarget parameter should be a 2D texture target.
+
+ Equivalent to the corresponding QGLContext::drawTexture().
+*/
+void QGLPixelBuffer::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
+{
+ Q_D(QGLPixelBuffer);
+ d->qctx->drawTexture(point, textureId, textureTarget);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLPixelBuffer::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ Q_D(QGLPixelBuffer);
+ d->qctx->drawTexture(point, textureId, textureTarget);
+}
+#endif
+
+/*!
+ Returns the format of the pbuffer. The format may be different
+ from the one that was requested.
+*/
+QGLFormat QGLPixelBuffer::format() const
+{
+ Q_D(const QGLPixelBuffer);
+ return d->format;
+}
+
+/*! \fn int QGLPixelBuffer::devType() const
+ \internal
+*/
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixelbuffer.h b/src/opengl/qglpixelbuffer.h
new file mode 100644
index 0000000000..ae053dc0c3
--- /dev/null
+++ b/src/opengl/qglpixelbuffer.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLPIXELBUFFER_H
+#define QGLPIXELBUFFER_H
+
+#include <QtOpenGL/qgl.h>
+#include <QtGui/qpaintdevice.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLPixelBufferPrivate;
+
+class Q_OPENGL_EXPORT QGLPixelBuffer : public QPaintDevice
+{
+ Q_DECLARE_PRIVATE(QGLPixelBuffer)
+public:
+ QGLPixelBuffer(const QSize &size, const QGLFormat &format = QGLFormat::defaultFormat(),
+ QGLWidget *shareWidget = 0);
+ QGLPixelBuffer(int width, int height, const QGLFormat &format = QGLFormat::defaultFormat(),
+ QGLWidget *shareWidget = 0);
+ virtual ~QGLPixelBuffer();
+
+ bool isValid() const;
+ bool makeCurrent();
+ bool doneCurrent();
+
+ GLuint generateDynamicTexture() const;
+ bool bindToDynamicTexture(GLuint texture);
+ void releaseFromDynamicTexture();
+ void updateDynamicTexture(GLuint texture_id) const;
+
+ GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D);
+ GLuint bindTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D);
+ GLuint bindTexture(const QString &fileName);
+ void deleteTexture(GLuint texture_id);
+
+ void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ bool bindToDynamicTexture(QMacCompatGLuint texture);
+ void updateDynamicTexture(QMacCompatGLuint texture_id) const;
+ GLuint bindTexture(const QImage &image, QMacCompatGLenum target = GL_TEXTURE_2D);
+ GLuint bindTexture(const QPixmap &pixmap, QMacCompatGLenum target = GL_TEXTURE_2D);
+
+ void drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+
+ void deleteTexture(QMacCompatGLuint texture_id);
+#endif
+
+ QSize size() const;
+ Qt::HANDLE handle() const;
+ QImage toImage() const;
+
+ QPaintEngine *paintEngine() const;
+ QGLFormat format() const;
+
+ static bool hasOpenGLPbuffers();
+
+protected:
+ int metric(PaintDeviceMetric metric) const;
+ int devType() const { return QInternal::Pbuffer; }
+
+private:
+ Q_DISABLE_COPY(QGLPixelBuffer)
+ QScopedPointer<QGLPixelBufferPrivate> d_ptr;
+ friend class QGLDrawable;
+ friend class QGLWindowSurface;
+ friend class QGLPaintDevice;
+ friend class QGLPBufferGLPaintDevice;
+ friend class QGLContextPrivate;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGLPIXELBUFFER_H
diff --git a/src/opengl/qglpixelbuffer_egl.cpp b/src/opengl/qglpixelbuffer_egl.cpp
new file mode 100644
index 0000000000..c0e6f4eae6
--- /dev/null
+++ b/src/opengl/qglpixelbuffer_egl.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+#include "qglpixelbuffer.h"
+#include "qglpixelbuffer_p.h"
+#include "qgl_egl_p.h"
+
+#include <qimage.h>
+#include <private/qgl_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef EGL_BIND_TO_TEXTURE_RGBA
+#define QGL_RENDER_TEXTURE 1
+#else
+#define QGL_RENDER_TEXTURE 0
+#endif
+
+bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget)
+{
+ // Create the EGL context.
+ ctx = new QEglContext();
+ ctx->setApi(QEgl::OpenGL);
+
+ // Find the shared context.
+ QEglContext *shareContext = 0;
+ if (shareWidget && shareWidget->d_func()->glcx)
+ shareContext = shareWidget->d_func()->glcx->d_func()->eglContext;
+
+ // Choose an appropriate configuration. We use the best format
+ // we can find, even if it is greater than the requested format.
+ // We try for a pbuffer that is capable of texture rendering if possible.
+ textureFormat = EGL_NONE;
+ if (shareContext) {
+ // Use the same configuration as the widget we are sharing with.
+ ctx->setConfig(shareContext->config());
+#if QGL_RENDER_TEXTURE
+ if (ctx->configAttrib(EGL_BIND_TO_TEXTURE_RGBA) == EGL_TRUE)
+ textureFormat = EGL_TEXTURE_RGBA;
+ else if (ctx->configAttrib(EGL_BIND_TO_TEXTURE_RGB) == EGL_TRUE)
+ textureFormat = EGL_TEXTURE_RGB;
+#endif
+ } else {
+ QEglProperties configProps;
+ qt_eglproperties_set_glformat(configProps, f);
+ configProps.setDeviceType(QInternal::Pbuffer);
+ configProps.setRenderableType(ctx->api());
+ bool ok = false;
+#if QGL_RENDER_TEXTURE
+ textureFormat = EGL_TEXTURE_RGBA;
+ configProps.setValue(EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE);
+ ok = ctx->chooseConfig(configProps, QEgl::BestPixelFormat);
+ if (!ok) {
+ // Try again with RGB texture rendering.
+ textureFormat = EGL_TEXTURE_RGB;
+ configProps.removeValue(EGL_BIND_TO_TEXTURE_RGBA);
+ configProps.setValue(EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE);
+ ok = ctx->chooseConfig(configProps, QEgl::BestPixelFormat);
+ if (!ok) {
+ // One last try for a pbuffer with no texture rendering.
+ configProps.removeValue(EGL_BIND_TO_TEXTURE_RGB);
+ textureFormat = EGL_NONE;
+ }
+ }
+#endif
+ if (!ok) {
+ if (!ctx->chooseConfig(configProps, QEgl::BestPixelFormat)) {
+ delete ctx;
+ ctx = 0;
+ return false;
+ }
+ }
+ }
+
+ // Retrieve the actual format properties.
+ qt_glformat_from_eglconfig(format, ctx->config());
+
+ // Create the attributes needed for the pbuffer.
+ QEglProperties attribs;
+ attribs.setValue(EGL_WIDTH, size.width());
+ attribs.setValue(EGL_HEIGHT, size.height());
+#if QGL_RENDER_TEXTURE
+ if (textureFormat != EGL_NONE) {
+ attribs.setValue(EGL_TEXTURE_FORMAT, textureFormat);
+ attribs.setValue(EGL_TEXTURE_TARGET, EGL_TEXTURE_2D);
+ }
+#endif
+
+ // Create the pbuffer surface.
+ pbuf = eglCreatePbufferSurface(ctx->display(), ctx->config(), attribs.properties());
+#if QGL_RENDER_TEXTURE
+ if (pbuf == EGL_NO_SURFACE && textureFormat != EGL_NONE) {
+ // Try again with texture rendering disabled.
+ textureFormat = EGL_NONE;
+ attribs.removeValue(EGL_TEXTURE_FORMAT);
+ attribs.removeValue(EGL_TEXTURE_TARGET);
+ pbuf = eglCreatePbufferSurface(ctx->display(), ctx->config(), attribs.properties());
+ }
+#endif
+ if (pbuf == EGL_NO_SURFACE) {
+ qWarning() << "QGLPixelBufferPrivate::init(): Unable to create EGL pbuffer surface:" << QEgl::errorString();
+ return false;
+ }
+
+ // Create a new context for the configuration.
+ if (!ctx->createContext(shareContext)) {
+ delete ctx;
+ ctx = 0;
+ return false;
+ }
+
+ return true;
+}
+
+bool QGLPixelBufferPrivate::cleanup()
+{
+ // No need to destroy "pbuf" here - it is done in QGLContext::reset().
+ return true;
+}
+
+bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id)
+{
+#if QGL_RENDER_TEXTURE
+ Q_D(QGLPixelBuffer);
+ if (d->invalid || d->textureFormat == EGL_NONE || !d->ctx)
+ return false;
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ return eglBindTexImage(d->ctx->display(), d->pbuf, EGL_BACK_BUFFER);
+#else
+ Q_UNUSED(texture_id);
+ return false;
+#endif
+}
+
+void QGLPixelBuffer::releaseFromDynamicTexture()
+{
+#if QGL_RENDER_TEXTURE
+ Q_D(QGLPixelBuffer);
+ if (d->invalid || d->textureFormat == EGL_NONE || !d->ctx)
+ return;
+ eglReleaseTexImage(d->ctx->display(), d->pbuf, EGL_BACK_BUFFER);
+#endif
+}
+
+
+GLuint QGLPixelBuffer::generateDynamicTexture() const
+{
+#if QGL_RENDER_TEXTURE
+ Q_D(const QGLPixelBuffer);
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ if (d->textureFormat == EGL_TEXTURE_RGB)
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, d->req_size.width(), d->req_size.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
+ else
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, d->req_size.width(), d->req_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ return texture;
+#else
+ return 0;
+#endif
+}
+
+bool QGLPixelBuffer::hasOpenGLPbuffers()
+{
+ // See if we have at least 1 configuration that matches the default format.
+ EGLDisplay dpy = QEgl::display();
+ if (dpy == EGL_NO_DISPLAY)
+ return false;
+ QEglProperties configProps;
+ qt_eglproperties_set_glformat(configProps, QGLFormat::defaultFormat());
+ configProps.setDeviceType(QInternal::Pbuffer);
+ configProps.setRenderableType(QEgl::OpenGL);
+ do {
+ EGLConfig cfg = 0;
+ EGLint matching = 0;
+ if (eglChooseConfig(dpy, configProps.properties(),
+ &cfg, 1, &matching) && matching > 0)
+ return true;
+ } while (configProps.reduceConfiguration());
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixelbuffer_mac.mm b/src/opengl/qglpixelbuffer_mac.mm
new file mode 100644
index 0000000000..b793ce5dab
--- /dev/null
+++ b/src/opengl/qglpixelbuffer_mac.mm
@@ -0,0 +1,331 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglpixelbuffer.h"
+#include "qglpixelbuffer_p.h"
+
+#ifndef QT_MAC_USE_COCOA
+#include <AGL/agl.h>
+#endif
+
+#include <qimage.h>
+#include <private/qgl_p.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef GL_TEXTURE_RECTANGLE_EXT
+#define GL_TEXTURE_RECTANGLE_EXT 0x84F5
+#endif
+
+static int nearest_gl_texture_size(int v)
+{
+ int n = 0, last = 0;
+ for (int s = 0; s < 32; ++s) {
+ if (((v>>s) & 1) == 1) {
+ ++n;
+ last = s;
+ }
+ }
+ if (n > 1)
+ return 1 << (last+1);
+ return 1 << last;
+}
+
+bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget)
+{
+#ifdef QT_MAC_USE_COCOA
+ Q_Q(QGLPixelBuffer);
+ // create a dummy context
+ QGLContext context(f, q);
+ context.create(shareWidget ? shareWidget->context() : 0);
+
+ if (context.isSharing())
+ share_ctx = shareWidget->context()->d_func()->cx;
+
+ // steal the NSOpenGLContext and update the format
+ ctx = context.d_func()->cx;
+ context.d_func()->cx = 0;
+ // d->cx will be set to ctx later in
+ // QGLPixelBufferPrivate::common_init, so we need to retain it:
+ [static_cast<NSOpenGLContext *>(ctx) retain];
+
+ format = context.format();
+
+ GLenum target = GL_TEXTURE_2D;
+
+ if ((QGLExtensions::glExtensions() & QGLExtensions::TextureRectangle)
+ && (size.width() != nearest_gl_texture_size(size.width())
+ || size.height() != nearest_gl_texture_size(size.height())))
+ {
+ target = GL_TEXTURE_RECTANGLE_EXT;
+ }
+
+ pbuf = [[NSOpenGLPixelBuffer alloc] initWithTextureTarget:target
+ textureInternalFormat:GL_RGBA
+ textureMaxMipMapLevel:0
+ pixelsWide:size.width()
+ pixelsHigh:size.height()];
+ if (!pbuf) {
+ qWarning("QGLPixelBuffer: Cannot create a pbuffer");
+ return false;
+ }
+
+ [static_cast<NSOpenGLContext *>(ctx) setPixelBuffer:static_cast<NSOpenGLPixelBuffer *>(pbuf)
+ cubeMapFace:0
+ mipMapLevel:0
+ currentVirtualScreen:0];
+ return true;
+#else
+ GLint attribs[40], i=0;
+ attribs[i++] = AGL_RGBA;
+ attribs[i++] = AGL_BUFFER_SIZE;
+ attribs[i++] = 32;
+ attribs[i++] = AGL_LEVEL;
+ attribs[i++] = f.plane();
+ if (f.redBufferSize() != -1) {
+ attribs[i++] = AGL_RED_SIZE;
+ attribs[i++] = f.redBufferSize();
+ }
+ if (f.greenBufferSize() != -1) {
+ attribs[i++] = AGL_GREEN_SIZE;
+ attribs[i++] = f.greenBufferSize();
+ }
+ if (f.blueBufferSize() != -1) {
+ attribs[i++] = AGL_BLUE_SIZE;
+ attribs[i++] = f.blueBufferSize();
+ }
+ if (f.stereo())
+ attribs[i++] = AGL_STEREO;
+ if (f.alpha()) {
+ attribs[i++] = AGL_ALPHA_SIZE;
+ attribs[i++] = f.alphaBufferSize() == -1 ? 8 : f.alphaBufferSize();
+ }
+ if (f.stencil()) {
+ attribs[i++] = AGL_STENCIL_SIZE;
+ attribs[i++] = f.stencilBufferSize() == -1 ? 8 : f.stencilBufferSize();
+ }
+ if (f.depth()) {
+ attribs[i++] = AGL_DEPTH_SIZE;
+ attribs[i++] = f.depthBufferSize() == -1 ? 32 : f.depthBufferSize();
+ }
+ if (f.accum()) {
+ attribs[i++] = AGL_ACCUM_RED_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 16 : f.accumBufferSize();
+ attribs[i++] = AGL_ACCUM_BLUE_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 16 : f.accumBufferSize();
+ attribs[i++] = AGL_ACCUM_GREEN_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 16 : f.accumBufferSize();
+ attribs[i++] = AGL_ACCUM_ALPHA_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 16 : f.accumBufferSize();
+ }
+
+ if (f.sampleBuffers()) {
+ attribs[i++] = AGL_SAMPLE_BUFFERS_ARB;
+ attribs[i++] = 1;
+ attribs[i++] = AGL_SAMPLES_ARB;
+ attribs[i++] = f.samples() == -1 ? 4 : f.samples();
+ }
+ attribs[i] = AGL_NONE;
+
+ AGLPixelFormat format = aglChoosePixelFormat(0, 0, attribs);
+ if (!format) {
+ qWarning("QGLPixelBuffer: Unable to find a pixel format (AGL error %d).",
+ (int) aglGetError());
+ return false;
+ }
+
+ GLint res;
+ aglDescribePixelFormat(format, AGL_LEVEL, &res);
+ this->format.setPlane(res);
+ aglDescribePixelFormat(format, AGL_DOUBLEBUFFER, &res);
+ this->format.setDoubleBuffer(res);
+ aglDescribePixelFormat(format, AGL_DEPTH_SIZE, &res);
+ this->format.setDepth(res);
+ if (this->format.depth())
+ this->format.setDepthBufferSize(res);
+ aglDescribePixelFormat(format, AGL_RGBA, &res);
+ this->format.setRgba(res);
+ aglDescribePixelFormat(format, AGL_RED_SIZE, &res);
+ this->format.setRedBufferSize(res);
+ aglDescribePixelFormat(format, AGL_GREEN_SIZE, &res);
+ this->format.setGreenBufferSize(res);
+ aglDescribePixelFormat(format, AGL_BLUE_SIZE, &res);
+ this->format.setBlueBufferSize(res);
+ aglDescribePixelFormat(format, AGL_ALPHA_SIZE, &res);
+ this->format.setAlpha(res);
+ if (this->format.alpha())
+ this->format.setAlphaBufferSize(res);
+ aglDescribePixelFormat(format, AGL_ACCUM_RED_SIZE, &res);
+ this->format.setAccum(res);
+ if (this->format.accum())
+ this->format.setAccumBufferSize(res);
+ aglDescribePixelFormat(format, AGL_STENCIL_SIZE, &res);
+ this->format.setStencil(res);
+ if (this->format.stencil())
+ this->format.setStencilBufferSize(res);
+ aglDescribePixelFormat(format, AGL_STEREO, &res);
+ this->format.setStereo(res);
+ aglDescribePixelFormat(format, AGL_SAMPLE_BUFFERS_ARB, &res);
+ this->format.setSampleBuffers(res);
+ if (this->format.sampleBuffers()) {
+ aglDescribePixelFormat(format, AGL_SAMPLES_ARB, &res);
+ this->format.setSamples(res);
+ }
+
+ AGLContext share = 0;
+ if (shareWidget)
+ share = share_ctx = static_cast<AGLContext>(shareWidget->d_func()->glcx->d_func()->cx);
+ ctx = aglCreateContext(format, share);
+ if (!ctx) {
+ qWarning("QGLPixelBuffer: Unable to create a context (AGL error %d).",
+ (int) aglGetError());
+ return false;
+ }
+
+ GLenum target = GL_TEXTURE_2D;
+
+ if ((QGLExtensions::glExtensions() & QGLExtensions::TextureRectangle)
+ && (size.width() != nearest_gl_texture_size(size.width())
+ || size.height() != nearest_gl_texture_size(size.height())))
+ {
+ target = GL_TEXTURE_RECTANGLE_EXT;
+ }
+
+ if (!aglCreatePBuffer(size.width(), size.height(), target, GL_RGBA, 0, &pbuf)) {
+ qWarning("QGLPixelBuffer: Unable to create a pbuffer (AGL error %d).",
+ (int) aglGetError());
+ return false;
+ }
+
+ if (!aglSetPBuffer(ctx, pbuf, 0, 0, 0)) {
+ qWarning("QGLPixelBuffer: Unable to set pbuffer (AGL error %d).",
+ (int) aglGetError());
+ return false;
+ }
+
+ aglDestroyPixelFormat(format);
+ return true;
+
+#endif
+}
+
+bool QGLPixelBufferPrivate::cleanup()
+{
+#ifdef QT_MAC_USE_COCOA
+ [static_cast<NSOpenGLPixelBuffer *>(pbuf) release];
+ pbuf = 0;
+ [static_cast<NSOpenGLContext *>(ctx) release];
+ ctx = 0;
+#else
+ aglDestroyPBuffer(pbuf);
+#endif
+ return true;
+}
+
+bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id)
+{
+ Q_D(QGLPixelBuffer);
+ if (d->invalid || !d->share_ctx)
+ return false;
+
+#ifdef QT_MAC_USE_COCOA
+ NSOpenGLContext *oldContext = [NSOpenGLContext currentContext];
+ if (d->share_ctx != oldContext)
+ [static_cast<NSOpenGLContext *>(d->share_ctx) makeCurrentContext];
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ [static_cast<NSOpenGLContext *>(d->share_ctx)
+ setTextureImageToPixelBuffer:static_cast<NSOpenGLPixelBuffer *>(d->pbuf)
+ colorBuffer:GL_FRONT];
+ if (oldContext && oldContext != d->share_ctx)
+ [oldContext makeCurrentContext];
+ return true;
+#else
+ aglSetCurrentContext(d->share_ctx);
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ aglTexImagePBuffer(d->share_ctx, d->pbuf, GL_FRONT);
+ return true;
+#endif
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+bool QGLPixelBuffer::bindToDynamicTexture(QMacCompatGLuint texture_id)
+{
+ return bindToDynamicTexture(GLuint(texture_id));
+}
+#endif
+
+void QGLPixelBuffer::releaseFromDynamicTexture()
+{
+}
+
+GLuint QGLPixelBuffer::generateDynamicTexture() const
+{
+#ifdef QT_MAC_USE_COCOA
+ Q_D(const QGLPixelBuffer);
+ NSOpenGLContext *oldContext = [NSOpenGLContext currentContext];
+ if (d->share_ctx != oldContext)
+ [static_cast<NSOpenGLContext *>(d->share_ctx) makeCurrentContext];
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ if (oldContext && oldContext != d->share_ctx)
+ [oldContext makeCurrentContext];
+ return texture;
+#else
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ return texture;
+#endif
+}
+
+bool QGLPixelBuffer::hasOpenGLPbuffers()
+{
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixelbuffer_p.h b/src/opengl/qglpixelbuffer_p.h
new file mode 100644
index 0000000000..65bbd26b78
--- /dev/null
+++ b/src/opengl/qglpixelbuffer_p.h
@@ -0,0 +1,209 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLPIXELBUFFER_P_H
+#define QGLPIXELBUFFER_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 "QtOpenGL/qglpixelbuffer.h"
+#include <private/qgl_p.h>
+#include <private/qglpaintdevice_p.h>
+
+#if defined(Q_WS_X11) && defined(QT_NO_EGL)
+#include <GL/glx.h>
+
+// The below is needed to for compilation on HPUX, due to broken GLX
+// headers. Some of the systems define GLX_VERSION_1_3 without
+// defining the GLXFBConfig structure, which is wrong.
+#if defined (Q_OS_HPUX) && defined(QT_DEFINE_GLXFBCONFIG_STRUCT)
+typedef unsigned long GLXPbuffer;
+
+struct GLXFBConfig {
+ int visualType;
+ int transparentType;
+ /* colors are floats scaled to ints */
+ int transparentRed, transparentGreen, transparentBlue, transparentAlpha;
+ int transparentIndex;
+
+ int visualCaveat;
+
+ int associatedVisualId;
+ int screen;
+
+ int drawableType;
+ int renderType;
+
+ int maxPbufferWidth, maxPbufferHeight, maxPbufferPixels;
+ int optimalPbufferWidth, optimalPbufferHeight; /* for SGIX_pbuffer */
+
+ int visualSelectGroup; /* visuals grouped by select priority */
+
+ unsigned int id;
+
+ GLboolean rgbMode;
+ GLboolean colorIndexMode;
+ GLboolean doubleBufferMode;
+ GLboolean stereoMode;
+ GLboolean haveAccumBuffer;
+ GLboolean haveDepthBuffer;
+ GLboolean haveStencilBuffer;
+
+ /* The number of bits present in various buffers */
+ GLint accumRedBits, accumGreenBits, accumBlueBits, accumAlphaBits;
+ GLint depthBits;
+ GLint stencilBits;
+ GLint indexBits;
+ GLint redBits, greenBits, blueBits, alphaBits;
+ GLuint redMask, greenMask, blueMask, alphaMask;
+
+ GLuint multiSampleSize; /* Number of samples per pixel (0 if no ms) */
+
+ GLuint nMultiSampleBuffers; /* Number of available ms buffers */
+ GLint maxAuxBuffers;
+
+ /* frame buffer level */
+ GLint level;
+
+ /* color ranges (for SGI_color_range) */
+ GLboolean extendedRange;
+ GLdouble minRed, maxRed;
+ GLdouble minGreen, maxGreen;
+ GLdouble minBlue, maxBlue;
+ GLdouble minAlpha, maxAlpha;
+};
+
+#endif // Q_OS_HPUX
+
+#elif defined(Q_WS_WIN)
+DECLARE_HANDLE(HPBUFFERARB);
+#elif !defined(QT_NO_EGL)
+#include <QtGui/private/qegl_p.h>
+#endif
+QT_END_INCLUDE_NAMESPACE
+
+class QEglContext;
+
+
+class QGLPBufferGLPaintDevice : public QGLPaintDevice
+{
+public:
+ virtual QPaintEngine* paintEngine() const {return pbuf->paintEngine();}
+ virtual QSize size() const {return pbuf->size();}
+ virtual QGLContext* context() const;
+ virtual void endPaint();
+ void setPBuffer(QGLPixelBuffer* pb);
+private:
+ QGLPixelBuffer* pbuf;
+};
+
+class QGLPixelBufferPrivate {
+ Q_DECLARE_PUBLIC(QGLPixelBuffer)
+public:
+ QGLPixelBufferPrivate(QGLPixelBuffer *q) : q_ptr(q), invalid(true), qctx(0), pbuf(0), ctx(0)
+ {
+#ifdef Q_WS_WIN
+ dc = 0;
+#elif defined(Q_WS_MACX)
+ share_ctx = 0;
+#endif
+ }
+ bool init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget);
+ void common_init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget);
+ bool cleanup();
+
+ QGLPixelBuffer *q_ptr;
+ bool invalid;
+ QGLContext *qctx;
+ QGLPBufferGLPaintDevice glDevice;
+ QGLFormat format;
+
+ QGLFormat req_format;
+ QPointer<QGLWidget> req_shareWidget;
+ QSize req_size;
+
+#if defined(Q_WS_X11) && defined(QT_NO_EGL)
+ GLXPbuffer pbuf;
+ GLXContext ctx;
+#elif defined(Q_WS_WIN)
+ HDC dc;
+ bool has_render_texture :1;
+#if !defined(QT_OPENGL_ES)
+ HPBUFFERARB pbuf;
+ HGLRC ctx;
+#endif
+#elif defined(Q_WS_MACX)
+# ifdef QT_MAC_USE_COCOA
+ void *pbuf;
+ void *ctx;
+ void *share_ctx;
+# else
+ AGLPbuffer pbuf;
+ AGLContext ctx;
+ AGLContext share_ctx;
+# endif
+#endif
+#ifndef QT_NO_EGL
+ EGLSurface pbuf;
+ QEglContext *ctx;
+ int textureFormat;
+#elif defined(Q_WS_QPA)
+ //stubs
+ void *pbuf;
+ void *ctx;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QGLPIXELBUFFER_P_H
diff --git a/src/opengl/qglpixelbuffer_stub.cpp b/src/opengl/qglpixelbuffer_stub.cpp
new file mode 100644
index 0000000000..98203fda26
--- /dev/null
+++ b/src/opengl/qglpixelbuffer_stub.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+#include "qglpixelbuffer.h"
+#include "qglpixelbuffer_p.h"
+
+#include <qimage.h>
+#include <private/qgl_p.h>
+
+QT_BEGIN_NAMESPACE
+
+bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget)
+{
+ Q_UNUSED(size);
+ Q_UNUSED(f);
+ Q_UNUSED(shareWidget);
+ return false;
+}
+
+bool QGLPixelBufferPrivate::cleanup()
+{
+ return false;
+}
+
+bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id)
+{
+ Q_UNUSED(texture_id);
+ return false;
+}
+
+void QGLPixelBuffer::releaseFromDynamicTexture()
+{
+}
+
+GLuint QGLPixelBuffer::generateDynamicTexture() const
+{
+ return 0;
+}
+
+bool QGLPixelBuffer::hasOpenGLPbuffers()
+{
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixelbuffer_win.cpp b/src/opengl/qglpixelbuffer_win.cpp
new file mode 100644
index 0000000000..a75ac47e03
--- /dev/null
+++ b/src/opengl/qglpixelbuffer_win.cpp
@@ -0,0 +1,403 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglpixelbuffer.h>
+#include <qgl.h>
+#include <private/qgl_p.h>
+
+#include <private/qglpixelbuffer_p.h>
+
+#include <qimage.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/* WGL_WGLEXT_PROTOTYPES */
+typedef const char * (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc);
+typedef HPBUFFERARB (WINAPI * PFNWGLCREATEPBUFFERARBPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList);
+typedef HDC (WINAPI * PFNWGLGETPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer);
+typedef int (WINAPI * PFNWGLRELEASEPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer, HDC hDC);
+typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFERARBPROC) (HPBUFFERARB hPbuffer);
+typedef BOOL (WINAPI * PFNWGLQUERYPBUFFERARBPROC) (HPBUFFERARB hPbuffer, int iAttribute, int *piValue);
+typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues);
+typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBFVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues);
+typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
+typedef BOOL (WINAPI * PFNWGLBINDTEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer);
+typedef BOOL (WINAPI * PFNWGLRELEASETEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer);
+typedef BOOL (WINAPI * PFNWGLSETPBUFFERATTRIBARBPROC) (HPBUFFERARB hPbuffer, const int * piAttribList);
+
+#ifndef WGL_ARB_pbuffer
+#define WGL_DRAW_TO_PBUFFER_ARB 0x202D
+#define WGL_MAX_PBUFFER_PIXELS_ARB 0x202E
+#define WGL_MAX_PBUFFER_WIDTH_ARB 0x202F
+#define WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030
+#define WGL_PBUFFER_LARGEST_ARB 0x2033
+#define WGL_PBUFFER_WIDTH_ARB 0x2034
+#define WGL_PBUFFER_HEIGHT_ARB 0x2035
+#define WGL_PBUFFER_LOST_ARB 0x2036
+#endif
+
+#ifndef WGL_ARB_pixel_format
+#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
+#define WGL_DRAW_TO_WINDOW_ARB 0x2001
+#define WGL_DRAW_TO_BITMAP_ARB 0x2002
+#define WGL_ACCELERATION_ARB 0x2003
+#define WGL_NEED_PALETTE_ARB 0x2004
+#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
+#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
+#define WGL_SWAP_METHOD_ARB 0x2007
+#define WGL_NUMBER_OVERLAYS_ARB 0x2008
+#define WGL_NUMBER_UNDERLAYS_ARB 0x2009
+#define WGL_TRANSPARENT_ARB 0x200A
+#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
+#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
+#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
+#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
+#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
+#define WGL_SHARE_DEPTH_ARB 0x200C
+#define WGL_SHARE_STENCIL_ARB 0x200D
+#define WGL_SHARE_ACCUM_ARB 0x200E
+#define WGL_SUPPORT_GDI_ARB 0x200F
+#define WGL_SUPPORT_OPENGL_ARB 0x2010
+#define WGL_DOUBLE_BUFFER_ARB 0x2011
+#define WGL_STEREO_ARB 0x2012
+#define WGL_PIXEL_TYPE_ARB 0x2013
+#define WGL_COLOR_BITS_ARB 0x2014
+#define WGL_RED_BITS_ARB 0x2015
+#define WGL_RED_SHIFT_ARB 0x2016
+#define WGL_GREEN_BITS_ARB 0x2017
+#define WGL_GREEN_SHIFT_ARB 0x2018
+#define WGL_BLUE_BITS_ARB 0x2019
+#define WGL_BLUE_SHIFT_ARB 0x201A
+#define WGL_ALPHA_BITS_ARB 0x201B
+#define WGL_ALPHA_SHIFT_ARB 0x201C
+#define WGL_ACCUM_BITS_ARB 0x201D
+#define WGL_ACCUM_RED_BITS_ARB 0x201E
+#define WGL_ACCUM_GREEN_BITS_ARB 0x201F
+#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
+#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
+#define WGL_DEPTH_BITS_ARB 0x2022
+#define WGL_STENCIL_BITS_ARB 0x2023
+#define WGL_AUX_BUFFERS_ARB 0x2024
+#define WGL_NO_ACCELERATION_ARB 0x2025
+#define WGL_GENERIC_ACCELERATION_ARB 0x2026
+#define WGL_FULL_ACCELERATION_ARB 0x2027
+#define WGL_SWAP_EXCHANGE_ARB 0x2028
+#define WGL_SWAP_COPY_ARB 0x2029
+#define WGL_SWAP_UNDEFINED_ARB 0x202A
+#define WGL_TYPE_RGBA_ARB 0x202B
+#define WGL_TYPE_COLORINDEX_ARB 0x202C
+#endif
+
+#ifndef WGL_ARB_render_texture
+#define WGL_BIND_TO_TEXTURE_RGB_ARB 0x2070
+#define WGL_BIND_TO_TEXTURE_RGBA_ARB 0x2071
+#define WGL_TEXTURE_FORMAT_ARB 0x2072
+#define WGL_TEXTURE_TARGET_ARB 0x2073
+#define WGL_MIPMAP_TEXTURE_ARB 0x2074
+#define WGL_TEXTURE_RGB_ARB 0x2075
+#define WGL_TEXTURE_RGBA_ARB 0x2076
+#define WGL_NO_TEXTURE_ARB 0x2077
+#define WGL_TEXTURE_CUBE_MAP_ARB 0x2078
+#define WGL_TEXTURE_1D_ARB 0x2079
+#define WGL_TEXTURE_2D_ARB 0x207A
+#define WGL_MIPMAP_LEVEL_ARB 0x207B
+#define WGL_CUBE_MAP_FACE_ARB 0x207C
+#define WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x207D
+#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x207E
+#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x207F
+#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x2080
+#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x2081
+#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x2082
+#define WGL_FRONT_LEFT_ARB 0x2083
+#define WGL_FRONT_RIGHT_ARB 0x2084
+#define WGL_BACK_LEFT_ARB 0x2085
+#define WGL_BACK_RIGHT_ARB 0x2086
+#define WGL_AUX0_ARB 0x2087
+#define WGL_AUX1_ARB 0x2088
+#define WGL_AUX2_ARB 0x2089
+#define WGL_AUX3_ARB 0x208A
+#define WGL_AUX4_ARB 0x208B
+#define WGL_AUX5_ARB 0x208C
+#define WGL_AUX6_ARB 0x208D
+#define WGL_AUX7_ARB 0x208E
+#define WGL_AUX8_ARB 0x208F
+#define WGL_AUX9_ARB 0x2090
+#endif
+
+#ifndef WGL_FLOAT_COMPONENTS_NV
+#define WGL_FLOAT_COMPONENTS_NV 0x20B0
+#endif
+
+#ifndef WGL_ARB_multisample
+#define WGL_SAMPLE_BUFFERS_ARB 0x2041
+#define WGL_SAMPLES_ARB 0x2042
+#endif
+
+#ifndef GL_SAMPLES_ARB
+#define GL_SAMPLES_ARB 0x80A9
+#endif
+
+QGLFormat pfiToQGLFormat(HDC hdc, int pfi);
+
+static void qt_format_to_attrib_list(bool has_render_texture, const QGLFormat &f, int attribs[])
+{
+ int i = 0;
+ attribs[i++] = WGL_SUPPORT_OPENGL_ARB;
+ attribs[i++] = TRUE;
+ attribs[i++] = WGL_DRAW_TO_PBUFFER_ARB;
+ attribs[i++] = TRUE;
+
+ if (has_render_texture) {
+ attribs[i++] = WGL_BIND_TO_TEXTURE_RGBA_ARB;
+ attribs[i++] = TRUE;
+ }
+
+ attribs[i++] = WGL_COLOR_BITS_ARB;
+ attribs[i++] = 32;
+ attribs[i++] = WGL_DOUBLE_BUFFER_ARB;
+ attribs[i++] = FALSE;
+
+ if (f.stereo()) {
+ attribs[i++] = WGL_STEREO_ARB;
+ attribs[i++] = TRUE;
+ }
+ if (f.depth()) {
+ attribs[i++] = WGL_DEPTH_BITS_ARB;
+ attribs[i++] = f.depthBufferSize() == -1 ? 24 : f.depthBufferSize();
+ }
+ if (f.redBufferSize() != -1) {
+ attribs[i++] = WGL_RED_BITS_ARB;
+ attribs[i++] = f.redBufferSize();
+ }
+ if (f.greenBufferSize() != -1) {
+ attribs[i++] = WGL_GREEN_BITS_ARB;
+ attribs[i++] = f.greenBufferSize();
+ }
+ if (f.blueBufferSize() != -1) {
+ attribs[i++] = WGL_BLUE_BITS_ARB;
+ attribs[i++] = f.blueBufferSize();
+ }
+ if (f.alpha()) {
+ attribs[i++] = WGL_ALPHA_BITS_ARB;
+ attribs[i++] = f.alphaBufferSize() == -1 ? 8 : f.alphaBufferSize();
+ }
+ if (f.accum()) {
+ attribs[i++] = WGL_ACCUM_BITS_ARB;
+ attribs[i++] = f.accumBufferSize() == -1 ? 16 : f.accumBufferSize();
+ }
+ if (f.stencil()) {
+ attribs[i++] = WGL_STENCIL_BITS_ARB;
+ attribs[i++] = f.stencilBufferSize() == -1 ? 8 : f.stencilBufferSize();
+ }
+ if ((f.redBufferSize() > 8 || f.greenBufferSize() > 8
+ || f.blueBufferSize() > 8 || f.alphaBufferSize() > 8)
+ && (QGLExtensions::glExtensions() & QGLExtensions::NVFloatBuffer))
+ {
+ attribs[i++] = WGL_FLOAT_COMPONENTS_NV;
+ attribs[i++] = TRUE;
+ }
+ if (f.sampleBuffers()) {
+ attribs[i++] = WGL_SAMPLE_BUFFERS_ARB;
+ attribs[i++] = 1;
+ attribs[i++] = WGL_SAMPLES_ARB;
+ attribs[i++] = f.samples() == -1 ? 16 : f.samples();
+ }
+ attribs[i] = 0;
+}
+
+bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget)
+{
+ QGLTemporaryContext tempContext;
+
+ PFNWGLCREATEPBUFFERARBPROC wglCreatePbufferARB =
+ (PFNWGLCREATEPBUFFERARBPROC) wglGetProcAddress("wglCreatePbufferARB");
+ PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDCARB =
+ (PFNWGLGETPBUFFERDCARBPROC) wglGetProcAddress("wglGetPbufferDCARB");
+ PFNWGLQUERYPBUFFERARBPROC wglQueryPbufferARB =
+ (PFNWGLQUERYPBUFFERARBPROC) wglGetProcAddress("wglQueryPbufferARB");
+ PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB =
+ (PFNWGLCHOOSEPIXELFORMATARBPROC) wglGetProcAddress("wglChoosePixelFormatARB");
+
+ if (!wglCreatePbufferARB) // assumes that if one can be resolved, all of them can
+ return false;
+
+ dc = wglGetCurrentDC();
+ Q_ASSERT(dc);
+ has_render_texture = false;
+
+ // sample buffers doesn't work in conjunction with the render_texture extension
+ if (!f.sampleBuffers()) {
+ PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB =
+ (PFNWGLGETEXTENSIONSSTRINGARBPROC) wglGetProcAddress("wglGetExtensionsStringARB");
+
+ if (wglGetExtensionsStringARB) {
+ QString extensions(QLatin1String(wglGetExtensionsStringARB(dc)));
+ has_render_texture = extensions.contains(QLatin1String("WGL_ARB_render_texture"));
+ }
+ }
+
+ int attribs[40];
+ qt_format_to_attrib_list(has_render_texture, f, attribs);
+
+ // Find pbuffer capable pixel format.
+ unsigned int num_formats = 0;
+ int pixel_format;
+ wglChoosePixelFormatARB(dc, attribs, 0, 1, &pixel_format, &num_formats);
+
+ // some GL implementations don't support pbuffers with accum
+ // buffers, so try that before we give up
+ if (num_formats == 0 && f.accum()) {
+ QGLFormat tmp = f;
+ tmp.setAccum(false);
+ qt_format_to_attrib_list(has_render_texture, tmp, attribs);
+ wglChoosePixelFormatARB(dc, attribs, 0, 1, &pixel_format, &num_formats);
+ }
+
+ if (num_formats == 0) {
+ qWarning("QGLPixelBuffer: Unable to find a pixel format with pbuffer - giving up.");
+ return false;
+ }
+ format = pfiToQGLFormat(dc, pixel_format);
+
+ // NB! The below ONLY works if the width/height are powers of 2.
+ // Set some pBuffer attributes so that we can use this pBuffer as
+ // a 2D RGBA texture target.
+ int pb_attribs[] = {WGL_TEXTURE_FORMAT_ARB, WGL_TEXTURE_RGBA_ARB,
+ WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_2D_ARB, 0};
+
+ pbuf = wglCreatePbufferARB(dc, pixel_format, size.width(), size.height(),
+ has_render_texture ? pb_attribs : 0);
+ if (!pbuf) {
+ // try again without the render_texture extension
+ pbuf = wglCreatePbufferARB(dc, pixel_format, size.width(), size.height(), 0);
+ has_render_texture = false;
+ if (!pbuf) {
+ qWarning("QGLPixelBuffer: Unable to create pbuffer [w=%d, h=%d] - giving up.", size.width(), size.height());
+ return false;
+ }
+ }
+
+ dc = wglGetPbufferDCARB(pbuf);
+ ctx = wglCreateContext(dc);
+ if (!dc || !ctx) {
+ qWarning("QGLPixelBuffer: Unable to create pbuffer context - giving up.");
+ return false;
+ }
+
+ // Explicitly disable the render_texture extension if we have a
+ // multi-sampled pbuffer context. This seems to be a problem only with
+ // ATI cards if multi-sampling is forced globally in the driver.
+ wglMakeCurrent(dc, ctx);
+ GLint samples = 0;
+ glGetIntegerv(GL_SAMPLES_ARB, &samples);
+ if (has_render_texture && samples != 0)
+ has_render_texture = false;
+
+ HGLRC share_ctx = shareWidget ? shareWidget->d_func()->glcx->d_func()->rc : 0;
+ if (share_ctx && !wglShareLists(share_ctx, ctx))
+ qWarning("QGLPixelBuffer: Unable to share display lists - with share widget.");
+
+ int width, height;
+ wglQueryPbufferARB(pbuf, WGL_PBUFFER_WIDTH_ARB, &width);
+ wglQueryPbufferARB(pbuf, WGL_PBUFFER_HEIGHT_ARB, &height);
+ return true;
+}
+
+bool QGLPixelBufferPrivate::cleanup()
+{
+ PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDCARB =
+ (PFNWGLRELEASEPBUFFERDCARBPROC) wglGetProcAddress("wglReleasePbufferDCARB");
+ PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbufferARB =
+ (PFNWGLDESTROYPBUFFERARBPROC) wglGetProcAddress("wglDestroyPbufferARB");
+ if (!invalid && wglReleasePbufferDCARB && wglDestroyPbufferARB) {
+ wglReleasePbufferDCARB(pbuf, dc);
+ wglDestroyPbufferARB(pbuf);
+ }
+ return true;
+}
+
+bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id)
+{
+ Q_D(QGLPixelBuffer);
+ if (d->invalid || !d->has_render_texture)
+ return false;
+ PFNWGLBINDTEXIMAGEARBPROC wglBindTexImageARB =
+ (PFNWGLBINDTEXIMAGEARBPROC) wglGetProcAddress("wglBindTexImageARB");
+ if (wglBindTexImageARB) {
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ return wglBindTexImageARB(d->pbuf, WGL_FRONT_LEFT_ARB);
+ }
+ return false;
+}
+
+void QGLPixelBuffer::releaseFromDynamicTexture()
+{
+ Q_D(QGLPixelBuffer);
+ if (d->invalid || !d->has_render_texture)
+ return;
+ PFNWGLRELEASETEXIMAGEARBPROC wglReleaseTexImageARB =
+ (PFNWGLRELEASETEXIMAGEARBPROC) wglGetProcAddress("wglReleaseTexImageARB");
+ if (wglReleaseTexImageARB)
+ wglReleaseTexImageARB(d->pbuf, WGL_FRONT_LEFT_ARB);
+}
+
+bool QGLPixelBuffer::hasOpenGLPbuffers()
+{
+ bool ret = false;
+ QGLTemporaryContext *tmpContext = 0;
+ if (!QGLContext::currentContext())
+ tmpContext = new QGLTemporaryContext;
+ PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB =
+ (PFNWGLGETEXTENSIONSSTRINGARBPROC) wglGetProcAddress("wglGetExtensionsStringARB");
+ if (wglGetExtensionsStringARB) {
+ QString extensions(QLatin1String(wglGetExtensionsStringARB(wglGetCurrentDC())));
+ if (extensions.contains(QLatin1String("WGL_ARB_pbuffer"))
+ && extensions.contains(QLatin1String("WGL_ARB_pixel_format"))) {
+ ret = true;
+ }
+ }
+ if (tmpContext)
+ delete tmpContext;
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixelbuffer_x11.cpp b/src/opengl/qglpixelbuffer_x11.cpp
new file mode 100644
index 0000000000..e76d79233a
--- /dev/null
+++ b/src/opengl/qglpixelbuffer_x11.cpp
@@ -0,0 +1,290 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qlibrary.h>
+#include <qdebug.h>
+#include <private/qgl_p.h>
+#include <private/qt_x11_p.h>
+#include <private/qpaintengine_opengl_p.h>
+
+#include <qx11info_x11.h>
+#include <GL/glx.h>
+#include <qimage.h>
+
+#include "qglpixelbuffer.h"
+#include "qglpixelbuffer_p.h"
+
+#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
+#include <dlfcn.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef GLX_VERSION_1_3
+#define GLX_RGBA_BIT 0x00000002
+#define GLX_PBUFFER_BIT 0x00000004
+#define GLX_DRAWABLE_TYPE 0x8010
+#define GLX_RENDER_TYPE 0x8011
+#define GLX_RGBA_TYPE 0x8014
+#define GLX_PBUFFER_HEIGHT 0x8040
+#define GLX_PBUFFER_WIDTH 0x8041
+#endif
+
+#ifndef GLX_ARB_multisample
+#define GLX_SAMPLE_BUFFERS_ARB 100000
+#define GLX_SAMPLES_ARB 100001
+#endif
+
+typedef GLXFBConfig* (*_glXChooseFBConfig) (Display *dpy, int screen, const int *attrib_list, int *nelements);
+typedef int (*_glXGetFBConfigAttrib) (Display *dpy, GLXFBConfig config, int attribute, int *value);
+typedef GLXPbuffer (*_glXCreatePbuffer) (Display *dpy, GLXFBConfig config, const int *attrib_list);
+typedef void (*_glXDestroyPbuffer) (Display *dpy, GLXPbuffer pbuf);
+typedef GLXContext (*_glXCreateNewContext) (Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
+typedef Bool (*_glXMakeContextCurrent) (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
+
+static _glXChooseFBConfig qt_glXChooseFBConfig = 0;
+static _glXCreateNewContext qt_glXCreateNewContext = 0;
+static _glXCreatePbuffer qt_glXCreatePbuffer = 0;
+static _glXDestroyPbuffer qt_glXDestroyPbuffer = 0;
+static _glXGetFBConfigAttrib qt_glXGetFBConfigAttrib = 0;
+static _glXMakeContextCurrent qt_glXMakeContextCurrent = 0;
+
+#define glXChooseFBConfig qt_glXChooseFBConfig
+#define glXCreateNewContext qt_glXCreateNewContext
+#define glXCreatePbuffer qt_glXCreatePbuffer
+#define glXDestroyPbuffer qt_glXDestroyPbuffer
+#define glXGetFBConfigAttrib qt_glXGetFBConfigAttrib
+#define glXMakeContextCurrent qt_glXMakeContextCurrent
+
+extern void* qglx_getProcAddress(const char* procName); // in qgl_x11.cpp
+
+static bool qt_resolve_pbuffer_extensions()
+{
+ static int resolved = false;
+ if (resolved && qt_glXMakeContextCurrent)
+ return true;
+ else if (resolved)
+ return false;
+
+ qt_glXChooseFBConfig = (_glXChooseFBConfig) qglx_getProcAddress("glXChooseFBConfig");
+ qt_glXCreateNewContext = (_glXCreateNewContext) qglx_getProcAddress("glXCreateNewContext");
+ qt_glXCreatePbuffer = (_glXCreatePbuffer) qglx_getProcAddress("glXCreatePbuffer");
+ qt_glXDestroyPbuffer = (_glXDestroyPbuffer) qglx_getProcAddress("glXDestroyPbuffer");
+ qt_glXGetFBConfigAttrib = (_glXGetFBConfigAttrib) qglx_getProcAddress("glXGetFBConfigAttrib");
+ qt_glXMakeContextCurrent = (_glXMakeContextCurrent) qglx_getProcAddress("glXMakeContextCurrent");
+
+ resolved = qt_glXMakeContextCurrent ? true : false;
+ return resolved;
+}
+
+static void qt_format_to_attrib_list(const QGLFormat &f, int attribs[])
+{
+ int i = 0;
+ attribs[i++] = GLX_RENDER_TYPE;
+ attribs[i++] = GLX_RGBA_BIT;
+ attribs[i++] = GLX_DRAWABLE_TYPE;
+ attribs[i++] = GLX_PBUFFER_BIT;
+ attribs[i++] = GLX_RED_SIZE;
+ attribs[i++] = f.redBufferSize() == -1 ? 1 : f.redBufferSize();
+ attribs[i++] = GLX_GREEN_SIZE;
+ attribs[i++] = f.greenBufferSize() == -1 ? 1 : f.greenBufferSize();
+ attribs[i++] = GLX_BLUE_SIZE;
+ attribs[i++] = f.blueBufferSize() == -1 ? 1 : f.blueBufferSize();
+ if (f.doubleBuffer()) {
+ attribs[i++] = GLX_DOUBLEBUFFER;
+ attribs[i++] = true;
+ }
+ if (f.depth()) {
+ attribs[i++] = GLX_DEPTH_SIZE;
+ attribs[i++] = f.depthBufferSize() == -1 ? 1 : f.depthBufferSize();
+ }
+ if (f.stereo()) {
+ attribs[i++] = GLX_STEREO;
+ attribs[i++] = true;
+ }
+ if (f.stencil()) {
+ attribs[i++] = GLX_STENCIL_SIZE;
+ attribs[i++] = f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize();
+ }
+ if (f.alpha()) {
+ attribs[i++] = GLX_ALPHA_SIZE;
+ attribs[i++] = f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize();
+ }
+ if (f.accum()) {
+ attribs[i++] = GLX_ACCUM_RED_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ attribs[i++] = GLX_ACCUM_GREEN_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ attribs[i++] = GLX_ACCUM_BLUE_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ if (f.alpha()) {
+ attribs[i++] = GLX_ACCUM_ALPHA_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ }
+ }
+ if (f.sampleBuffers()) {
+ attribs[i++] = GLX_SAMPLE_BUFFERS_ARB;
+ attribs[i++] = 1;
+ attribs[i++] = GLX_SAMPLES_ARB;
+ attribs[i++] = f.samples() == -1 ? 4 : f.samples();
+ }
+
+ attribs[i] = XNone;
+}
+
+bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget)
+{
+ if (!qt_resolve_pbuffer_extensions()) {
+ qWarning("QGLPixelBuffer: pbuffers are not supported on this system.");
+ return false;
+ }
+
+ int attribs[40];
+ int num_configs = 0;
+
+ qt_format_to_attrib_list(f, attribs);
+
+ int screen = X11->defaultScreen;
+ if (shareWidget)
+ screen = shareWidget->x11Info().screen();
+
+ GLXFBConfig *configs = glXChooseFBConfig(X11->display, screen, attribs, &num_configs);
+ if (configs && num_configs) {
+ int res;
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_LEVEL, &res);
+ format.setPlane(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_DOUBLEBUFFER, &res);
+ format.setDoubleBuffer(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_DEPTH_SIZE, &res);
+ format.setDepth(res);
+ if (format.depth())
+ format.setDepthBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_RGBA, &res);
+ format.setRgba(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_RED_SIZE, &res);
+ format.setRedBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_GREEN_SIZE, &res);
+ format.setGreenBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_BLUE_SIZE, &res);
+ format.setBlueBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_ALPHA_SIZE, &res);
+ format.setAlpha(res);
+ if (format.alpha())
+ format.setAlphaBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_ACCUM_RED_SIZE, &res);
+ format.setAccum(res);
+ if (format.accum())
+ format.setAccumBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_STENCIL_SIZE, &res);
+ format.setStencil(res);
+ if (format.stencil())
+ format.setStencilBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_STEREO, &res);
+ format.setStereo(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_SAMPLE_BUFFERS_ARB, &res);
+ format.setSampleBuffers(res);
+ if (format.sampleBuffers()) {
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_SAMPLES_ARB, &res);
+ format.setSamples(res);
+ }
+
+ int pb_attribs[] = {GLX_PBUFFER_WIDTH, size.width(), GLX_PBUFFER_HEIGHT, size.height(), XNone};
+ GLXContext shareContext = 0;
+ if (shareWidget && shareWidget->d_func()->glcx)
+ shareContext = (GLXContext) shareWidget->d_func()->glcx->d_func()->cx;
+
+ pbuf = glXCreatePbuffer(QX11Info::display(), configs[0], pb_attribs);
+ ctx = glXCreateNewContext(QX11Info::display(), configs[0], GLX_RGBA_TYPE, shareContext, true);
+
+ XFree(configs);
+ if (!pbuf || !ctx) {
+ qWarning("QGLPixelBuffer: Unable to create a pbuffer/context - giving up.");
+ return false;
+ }
+ return true;
+ } else {
+ qWarning("QGLPixelBuffer: Unable to find a context/format match - giving up.");
+ return false;
+ }
+}
+
+bool QGLPixelBufferPrivate::cleanup()
+{
+ glXDestroyPbuffer(QX11Info::display(), pbuf);
+ return true;
+}
+
+bool QGLPixelBuffer::bindToDynamicTexture(GLuint)
+{
+ return false;
+}
+
+void QGLPixelBuffer::releaseFromDynamicTexture()
+{
+}
+
+bool QGLPixelBuffer::hasOpenGLPbuffers()
+{
+ bool ret = qt_resolve_pbuffer_extensions();
+
+ if (!ret)
+ return false;
+
+ int attribs[40];
+ int num_configs = 0;
+
+ qt_format_to_attrib_list(QGLFormat::defaultFormat(), attribs);
+
+ GLXFBConfig *configs = glXChooseFBConfig(X11->display, X11->defaultScreen, attribs, &num_configs);
+ GLXPbuffer pbuf = 0;
+ GLXContext ctx = 0;
+
+ if (configs && num_configs) {
+ int pb_attribs[] = {GLX_PBUFFER_WIDTH, 128, GLX_PBUFFER_HEIGHT, 128, XNone};
+ pbuf = glXCreatePbuffer(X11->display, configs[0], pb_attribs);
+ ctx = glXCreateNewContext(X11->display, configs[0], GLX_RGBA_TYPE, 0, true);
+ XFree(configs);
+ glXDestroyContext(X11->display, ctx);
+ glXDestroyPbuffer(X11->display, pbuf);
+ }
+ return pbuf && ctx;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp
new file mode 100644
index 0000000000..aa075617bb
--- /dev/null
+++ b/src/opengl/qglpixmapfilter.cpp
@@ -0,0 +1,621 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qpixmapfilter_p.h"
+#include "private/qpixmapdata_gl_p.h"
+#include "private/qpaintengineex_opengl2_p.h"
+#include "private/qglengineshadermanager_p.h"
+#include "private/qpixmapdata_p.h"
+#include "private/qimagepixmapcleanuphooks_p.h"
+#include "qglpixmapfilter_p.h"
+#include "qgraphicssystem_gl_p.h"
+#include "qpaintengine_opengl_p.h"
+#include "qcache.h"
+
+#include "qglframebufferobject.h"
+#include "qglshaderprogram.h"
+#include "qgl_p.h"
+
+#include "private/qapplication_p.h"
+#include "private/qdrawhelper_p.h"
+#include "private/qmemrotate_p.h"
+#include "private/qmath_p.h"
+#include "qmath.h"
+
+QT_BEGIN_NAMESPACE
+
+// qpixmapfilter.cpp
+Q_GUI_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0);
+Q_GUI_EXPORT QImage qt_halfScaled(const QImage &source);
+
+void QGLPixmapFilterBase::bindTexture(const QPixmap &src) const
+{
+ const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, QGLContext::BindOptions(QGLContext::DefaultBindOption | QGLContext::MemoryManagedBindOption));
+}
+
+void QGLPixmapFilterBase::drawImpl(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF& source) const
+{
+ processGL(painter, pos, src, source);
+}
+
+class QGLPixmapColorizeFilter: public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapColorizeFilter>
+{
+public:
+ QGLPixmapColorizeFilter();
+
+ void setUniforms(QGLShaderProgram *program);
+
+protected:
+ bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &pixmap, const QRectF &srcRect) const;
+};
+
+class QGLPixmapConvolutionFilter: public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapConvolutionFilter>
+{
+public:
+ QGLPixmapConvolutionFilter();
+ ~QGLPixmapConvolutionFilter();
+
+ void setUniforms(QGLShaderProgram *program);
+
+protected:
+ bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
+
+private:
+ QByteArray generateConvolutionShader() const;
+
+ mutable QSize m_srcSize;
+ mutable int m_prevKernelSize;
+};
+
+class QGLPixmapBlurFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapBlurFilter>
+{
+public:
+ QGLPixmapBlurFilter();
+
+protected:
+ bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
+};
+
+class QGLPixmapDropShadowFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapDropShadowFilter>
+{
+public:
+ QGLPixmapDropShadowFilter();
+
+ void setUniforms(QGLShaderProgram *program);
+
+protected:
+ bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
+};
+
+extern const QGLContext *qt_gl_share_context();
+
+QPixmapFilter *QGL2PaintEngineEx::pixmapFilter(int type, const QPixmapFilter *prototype)
+{
+ Q_D(QGL2PaintEngineEx);
+ switch (type) {
+ case QPixmapFilter::ColorizeFilter:
+ if (!d->colorizeFilter)
+ d->colorizeFilter.reset(new QGLPixmapColorizeFilter);
+ return d->colorizeFilter.data();
+
+ case QPixmapFilter::BlurFilter: {
+ if (!d->blurFilter)
+ d->blurFilter.reset(new QGLPixmapBlurFilter());
+ return d->blurFilter.data();
+ }
+
+ case QPixmapFilter::DropShadowFilter: {
+ if (!d->dropShadowFilter)
+ d->dropShadowFilter.reset(new QGLPixmapDropShadowFilter());
+ return d->dropShadowFilter.data();
+ }
+
+ case QPixmapFilter::ConvolutionFilter:
+ if (!d->convolutionFilter)
+ d->convolutionFilter.reset(new QGLPixmapConvolutionFilter);
+ return d->convolutionFilter.data();
+
+ default: break;
+ }
+ return QPaintEngineEx::pixmapFilter(type, prototype);
+}
+
+static const char *qt_gl_colorize_filter =
+ "uniform lowp vec4 colorizeColor;"
+ "uniform lowp float colorizeStrength;"
+ "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords)"
+ "{"
+ " lowp vec4 srcPixel = texture2D(src, srcCoords);"
+ " lowp float gray = dot(srcPixel.rgb, vec3(0.212671, 0.715160, 0.072169));"
+ " lowp vec3 colorized = 1.0-((1.0-gray)*(1.0-colorizeColor.rgb));"
+ " return vec4(mix(srcPixel.rgb, colorized * srcPixel.a, colorizeStrength), srcPixel.a);"
+ "}";
+
+QGLPixmapColorizeFilter::QGLPixmapColorizeFilter()
+{
+ setSource(qt_gl_colorize_filter);
+}
+
+bool QGLPixmapColorizeFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const
+{
+ QGLPixmapColorizeFilter *filter = const_cast<QGLPixmapColorizeFilter *>(this);
+
+ filter->setOnPainter(painter);
+ painter->drawPixmap(pos, src);
+ filter->removeFromPainter(painter);
+
+ return true;
+}
+
+void QGLPixmapColorizeFilter::setUniforms(QGLShaderProgram *program)
+{
+ program->setUniformValue("colorizeColor", color());
+ program->setUniformValue("colorizeStrength", float(strength()));
+}
+
+void QGLPixmapConvolutionFilter::setUniforms(QGLShaderProgram *program)
+{
+ const qreal *kernel = convolutionKernel();
+ int kernelWidth = columns();
+ int kernelHeight = rows();
+ int kernelSize = kernelWidth * kernelHeight;
+
+ QVarLengthArray<GLfloat> matrix(kernelSize);
+ QVarLengthArray<GLfloat> offset(kernelSize * 2);
+
+ for(int i = 0; i < kernelSize; ++i)
+ matrix[i] = kernel[i];
+
+ for(int y = 0; y < kernelHeight; ++y) {
+ for(int x = 0; x < kernelWidth; ++x) {
+ offset[(y * kernelWidth + x) * 2] = x - (kernelWidth / 2);
+ offset[(y * kernelWidth + x) * 2 + 1] = (kernelHeight / 2) - y;
+ }
+ }
+
+ const qreal iw = 1.0 / m_srcSize.width();
+ const qreal ih = 1.0 / m_srcSize.height();
+ program->setUniformValue("inv_texture_size", iw, ih);
+ program->setUniformValueArray("matrix", matrix.constData(), kernelSize, 1);
+ program->setUniformValueArray("offset", offset.constData(), kernelSize, 2);
+}
+
+// generates convolution filter code for arbitrary sized kernel
+QByteArray QGLPixmapConvolutionFilter::generateConvolutionShader() const {
+ QByteArray code;
+ int kernelWidth = columns();
+ int kernelHeight = rows();
+ int kernelSize = kernelWidth * kernelHeight;
+ code.append("uniform highp vec2 inv_texture_size;\n"
+ "uniform mediump float matrix[");
+ code.append(QByteArray::number(kernelSize));
+ code.append("];\n"
+ "uniform highp vec2 offset[");
+ code.append(QByteArray::number(kernelSize));
+ code.append("];\n");
+ code.append("lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {\n");
+
+ code.append(" int i = 0;\n"
+ " lowp vec4 sum = vec4(0.0);\n"
+ " for (i = 0; i < ");
+ code.append(QByteArray::number(kernelSize));
+ code.append("; i++) {\n"
+ " sum += matrix[i] * texture2D(src,srcCoords+inv_texture_size*offset[i]);\n"
+ " }\n"
+ " return sum;\n"
+ "}");
+ return code;
+}
+
+QGLPixmapConvolutionFilter::QGLPixmapConvolutionFilter()
+ : m_prevKernelSize(-1)
+{
+}
+
+QGLPixmapConvolutionFilter::~QGLPixmapConvolutionFilter()
+{
+}
+
+bool QGLPixmapConvolutionFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const
+{
+ QGLPixmapConvolutionFilter *filter = const_cast<QGLPixmapConvolutionFilter *>(this);
+
+ m_srcSize = src.size();
+
+ int kernelSize = rows() * columns();
+ if (m_prevKernelSize == -1 || m_prevKernelSize != kernelSize) {
+ filter->setSource(generateConvolutionShader());
+ m_prevKernelSize = kernelSize;
+ }
+
+ filter->setOnPainter(painter);
+ painter->drawPixmap(pos, src, srcRect);
+ filter->removeFromPainter(painter);
+
+ return true;
+}
+
+QGLPixmapBlurFilter::QGLPixmapBlurFilter()
+{
+}
+
+class QGLBlurTextureInfo
+{
+public:
+ QGLBlurTextureInfo(const QImage &image, GLuint tex, qreal r)
+ : m_texture(tex)
+ , m_radius(r)
+ {
+ m_paddedImage << image;
+ }
+
+ ~QGLBlurTextureInfo()
+ {
+ glDeleteTextures(1, &m_texture);
+ }
+
+ QImage paddedImage(int scaleLevel = 0) const;
+ GLuint texture() const { return m_texture; }
+ qreal radius() const { return m_radius; }
+
+private:
+ mutable QList<QImage> m_paddedImage;
+ GLuint m_texture;
+ qreal m_radius;
+};
+
+QImage QGLBlurTextureInfo::paddedImage(int scaleLevel) const
+{
+ for (int i = m_paddedImage.size() - 1; i <= scaleLevel; ++i)
+ m_paddedImage << qt_halfScaled(m_paddedImage.at(i));
+
+ return m_paddedImage.at(scaleLevel);
+}
+
+class QGLBlurTextureCache : public QObject
+{
+public:
+ static QGLBlurTextureCache *cacheForContext(const QGLContext *context);
+
+ QGLBlurTextureCache(const QGLContext *);
+ ~QGLBlurTextureCache();
+
+ QGLBlurTextureInfo *takeBlurTextureInfo(const QPixmap &pixmap);
+ bool hasBlurTextureInfo(quint64 cacheKey) const;
+ void insertBlurTextureInfo(const QPixmap &pixmap, QGLBlurTextureInfo *info);
+ void clearBlurTextureInfo(quint64 cacheKey);
+
+ void timerEvent(QTimerEvent *event);
+
+private:
+ static void pixmapDestroyed(QPixmapData *pixmap);
+
+ QCache<quint64, QGLBlurTextureInfo > cache;
+
+ static QList<QGLBlurTextureCache *> blurTextureCaches;
+
+ int timerId;
+};
+
+QList<QGLBlurTextureCache *> QGLBlurTextureCache::blurTextureCaches;
+Q_GLOBAL_STATIC(QGLContextGroupResource<QGLBlurTextureCache>, qt_blur_texture_caches)
+
+QGLBlurTextureCache::QGLBlurTextureCache(const QGLContext *)
+ : timerId(0)
+{
+ cache.setMaxCost(4 * 1024 * 1024);
+ blurTextureCaches.append(this);
+}
+
+QGLBlurTextureCache::~QGLBlurTextureCache()
+{
+ blurTextureCaches.removeAt(blurTextureCaches.indexOf(this));
+}
+
+void QGLBlurTextureCache::timerEvent(QTimerEvent *)
+{
+ killTimer(timerId);
+ timerId = 0;
+
+ cache.clear();
+}
+
+QGLBlurTextureCache *QGLBlurTextureCache::cacheForContext(const QGLContext *context)
+{
+ return qt_blur_texture_caches()->value(context);
+}
+
+QGLBlurTextureInfo *QGLBlurTextureCache::takeBlurTextureInfo(const QPixmap &pixmap)
+{
+ return cache.take(pixmap.cacheKey());
+}
+
+void QGLBlurTextureCache::clearBlurTextureInfo(quint64 cacheKey)
+{
+ cache.remove(cacheKey);
+}
+
+bool QGLBlurTextureCache::hasBlurTextureInfo(quint64 cacheKey) const
+{
+ return cache.contains(cacheKey);
+}
+
+void QGLBlurTextureCache::insertBlurTextureInfo(const QPixmap &pixmap, QGLBlurTextureInfo *info)
+{
+ static bool hookAdded = false;
+ if (!hookAdded) {
+ QImagePixmapCleanupHooks::instance()->addPixmapDataDestructionHook(pixmapDestroyed);
+ QImagePixmapCleanupHooks::instance()->addPixmapDataModificationHook(pixmapDestroyed);
+ hookAdded = true;
+ }
+
+ QImagePixmapCleanupHooks::enableCleanupHooks(pixmap);
+ cache.insert(pixmap.cacheKey(), info, pixmap.width() * pixmap.height());
+
+ if (timerId)
+ killTimer(timerId);
+
+ timerId = startTimer(8000);
+}
+
+void QGLBlurTextureCache::pixmapDestroyed(QPixmapData *pmd)
+{
+ foreach (QGLBlurTextureCache *cache, blurTextureCaches) {
+ if (cache->hasBlurTextureInfo(pmd->cacheKey()))
+ cache->clearBlurTextureInfo(pmd->cacheKey());
+ }
+}
+
+static const int qAnimatedBlurLevelIncrement = 16;
+static const int qMaxBlurHalfScaleLevel = 1;
+
+static GLuint generateBlurTexture(const QSize &size, GLenum format = GL_RGBA)
+{
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, format, size.width(), size.height(), 0, format,
+ GL_UNSIGNED_BYTE, 0);
+ return texture;
+}
+
+static inline uint nextMultiple(uint x, uint multiplier)
+{
+ uint mod = x % multiplier;
+ if (mod == 0)
+ return x;
+ return x + multiplier - mod;
+}
+
+Q_GUI_EXPORT void qt_memrotate90_gl(const quint32 *src, int srcWidth, int srcHeight, int srcStride,
+ quint32 *dest, int dstStride);
+
+bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const
+{
+ if (radius() < 1) {
+ painter->drawPixmap(pos, src);
+ return true;
+ }
+
+ qreal actualRadius = radius();
+
+ QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
+
+ QGLBlurTextureCache *blurTextureCache = QGLBlurTextureCache::cacheForContext(ctx);
+ QGLBlurTextureInfo *info = 0;
+ int padding = nextMultiple(qCeil(actualRadius), qAnimatedBlurLevelIncrement);
+ QRect targetRect = src.rect().adjusted(-padding, -padding, padding, padding);
+
+ // pad so that we'll be able to half-scale qMaxBlurHalfScaleLevel times
+ targetRect.setWidth((targetRect.width() + (qMaxBlurHalfScaleLevel-1)) & ~(qMaxBlurHalfScaleLevel-1));
+ targetRect.setHeight((targetRect.height() + (qMaxBlurHalfScaleLevel-1)) & ~(qMaxBlurHalfScaleLevel-1));
+
+ QSize textureSize;
+
+ info = blurTextureCache->takeBlurTextureInfo(src);
+ if (!info || info->radius() < actualRadius) {
+ QSize paddedSize = targetRect.size() / 2;
+
+ QImage padded(paddedSize.height(), paddedSize.width(), QImage::Format_ARGB32_Premultiplied);
+ padded.fill(0);
+
+ if (info) {
+ int oldPadding = qRound(info->radius());
+
+ QPainter p(&padded);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.drawImage((padding - oldPadding) / 2, (padding - oldPadding) / 2, info->paddedImage());
+ p.end();
+ } else {
+ // TODO: combine byteswapping and memrotating into one by declaring
+ // custom GL_RGBA pixel type and qt_colorConvert template for it
+ QImage prepadded = qt_halfScaled(src.toImage()).convertToFormat(QImage::Format_ARGB32_Premultiplied);
+
+ // byte-swap and memrotates in one go
+ qt_memrotate90_gl(reinterpret_cast<const quint32*>(prepadded.bits()),
+ prepadded.width(), prepadded.height(), prepadded.bytesPerLine(),
+ reinterpret_cast<quint32*>(padded.scanLine(padding / 2)) + padding / 2,
+ padded.bytesPerLine());
+ }
+
+ delete info;
+ info = new QGLBlurTextureInfo(padded, generateBlurTexture(paddedSize), padding);
+
+ textureSize = paddedSize;
+ } else {
+ textureSize = QSize(info->paddedImage().height(), info->paddedImage().width());
+ }
+
+ actualRadius *= qreal(0.5);
+ int level = 1;
+ for (; level < qMaxBlurHalfScaleLevel; ++level) {
+ if (actualRadius <= 16)
+ break;
+ actualRadius *= qreal(0.5);
+ }
+
+ const int s = (1 << level);
+
+ int prepadding = qRound(info->radius());
+ padding = qMin(prepadding, qCeil(actualRadius) << level);
+ targetRect = src.rect().adjusted(-padding, -padding, padding, padding);
+
+ targetRect.setWidth(targetRect.width() & ~(s-1));
+ targetRect.setHeight(targetRect.height() & ~(s-1));
+
+ int paddingDelta = (prepadding - padding) >> level;
+
+ QRect subRect(paddingDelta, paddingDelta, targetRect.width() >> level, targetRect.height() >> level);
+ QImage sourceImage = info->paddedImage(level-1);
+
+ QImage subImage(subRect.height(), subRect.width(), QImage::Format_ARGB32_Premultiplied);
+ qt_rectcopy((QRgb *)subImage.bits(), ((QRgb *)sourceImage.scanLine(paddingDelta)) + paddingDelta,
+ 0, 0, subRect.height(), subRect.width(), subImage.bytesPerLine(), sourceImage.bytesPerLine());
+
+ GLuint texture = info->texture();
+
+ qt_blurImage(subImage, actualRadius, blurHints() & QGraphicsBlurEffect::QualityHint, 1);
+
+ // subtract one pixel off the end to prevent the bilinear sampling from sampling uninitialized data
+ QRect textureSubRect = subImage.rect().adjusted(0, 0, -1, -1);
+ QRectF targetRectF = QRectF(targetRect).adjusted(0, 0, -targetRect.width() / qreal(textureSize.width()), -targetRect.height() / qreal(textureSize.height()));
+
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subImage.width(), subImage.height(), GL_RGBA,
+ GL_UNSIGNED_BYTE, const_cast<const QImage &>(subImage).bits());
+
+ QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+
+ // texture is flipped on the y-axis
+ targetRectF = QRectF(targetRectF.x(), targetRectF.bottom(), targetRectF.width(), -targetRectF.height());
+ engine->drawTexture(targetRectF.translated(pos), texture, textureSize, textureSubRect);
+
+ blurTextureCache->insertBlurTextureInfo(src, info);
+
+ return true;
+}
+
+static const char *qt_gl_drop_shadow_filter =
+ "uniform lowp vec4 shadowColor;"
+ "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords)"
+ "{"
+ " return shadowColor * texture2D(src, srcCoords.yx).a;"
+ "}";
+
+
+QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter()
+{
+ setSource(qt_gl_drop_shadow_filter);
+}
+
+bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const
+{
+ QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this);
+
+ qreal r = blurRadius();
+ QRectF targetRectUnaligned = QRectF(src.rect()).translated(pos + offset()).adjusted(-r, -r, r, r);
+ QRect targetRect = targetRectUnaligned.toAlignedRect();
+
+ // ensure even dimensions (going to divide by two)
+ targetRect.setWidth((targetRect.width() + 1) & ~1);
+ targetRect.setHeight((targetRect.height() + 1) & ~1);
+
+ QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
+ QGLBlurTextureCache *blurTextureCache = QGLBlurTextureCache::cacheForContext(ctx);
+
+ QGLBlurTextureInfo *info = blurTextureCache->takeBlurTextureInfo(src);
+ if (!info || info->radius() != r) {
+ QImage half = qt_halfScaled(src.toImage().alphaChannel());
+
+ qreal rx = r + targetRect.left() - targetRectUnaligned.left();
+ qreal ry = r + targetRect.top() - targetRectUnaligned.top();
+
+ QImage image = QImage(targetRect.size() / 2, QImage::Format_Indexed8);
+ image.setColorTable(half.colorTable());
+ image.fill(0);
+ int dx = qRound(rx * qreal(0.5));
+ int dy = qRound(ry * qreal(0.5));
+ qt_rectcopy(image.bits(), half.bits(), dx, dy,
+ half.width(), half.height(),
+ image.bytesPerLine(), half.bytesPerLine());
+
+ qt_blurImage(image, r * qreal(0.5), false, 1);
+
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, image.width(), image.height(),
+ 0, GL_ALPHA, GL_UNSIGNED_BYTE, image.bits());
+
+ info = new QGLBlurTextureInfo(image, texture, r);
+ }
+
+ GLuint texture = info->texture();
+
+ filter->setOnPainter(painter);
+
+ QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+
+ engine->drawTexture(targetRect, texture, info->paddedImage().size(), info->paddedImage().rect());
+
+ filter->removeFromPainter(painter);
+
+ // Now draw the actual pixmap over the top.
+ painter->drawPixmap(pos, src, srcRect);
+
+ blurTextureCache->insertBlurTextureInfo(src, info);
+
+ return true;
+}
+
+void QGLPixmapDropShadowFilter::setUniforms(QGLShaderProgram *program)
+{
+ QColor col = color();
+ qreal alpha = col.alphaF();
+ program->setUniformValue("shadowColor", col.redF() * alpha,
+ col.greenF() * alpha,
+ col.blueF() * alpha,
+ alpha);
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixmapfilter_p.h b/src/opengl/qglpixmapfilter_p.h
new file mode 100644
index 0000000000..9eab9a750a
--- /dev/null
+++ b/src/opengl/qglpixmapfilter_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLPIXMAPFILTER_P_H
+#define QGLPIXMAPFILTER_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 <private/qpixmapfilter_p.h>
+
+#include <QtOpenGL/qgl.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QGLPixelBuffer;
+
+QT_MODULE(OpenGL)
+
+class QGLPixmapFilterBase
+{
+public:
+ virtual ~QGLPixmapFilterBase() {}
+protected:
+ void bindTexture(const QPixmap &src) const;
+ void drawImpl(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect = QRectF()) const;
+
+ virtual bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const = 0;
+};
+
+template <typename Filter>
+class QGLPixmapFilter : public Filter, public QGLPixmapFilterBase
+{
+public:
+ void draw(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect = QRectF()) const {
+ const QRectF source = srcRect.isNull() ? QRectF(src.rect()) : srcRect;
+ if (painter)
+ drawImpl(painter, pos, src, source);
+ }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGLPIXMAPFILTER_P_H
diff --git a/src/opengl/qglscreen_qws.cpp b/src/opengl/qglscreen_qws.cpp
new file mode 100644
index 0000000000..badb581844
--- /dev/null
+++ b/src/opengl/qglscreen_qws.cpp
@@ -0,0 +1,242 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGLScreen>
+#include <QGLContext>
+#include <QGLWidget>
+#include "private/qglwindowsurface_qws_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QGLScreenPrivate
+{
+public:
+ QGLScreen::Options options;
+ QGLScreenSurfaceFunctions *functions;
+};
+
+/*!
+ \internal
+ \preliminary
+ \class QGLScreen
+
+ \brief This class encapsulates an OpenGL screen driver.
+*/
+
+QGLScreen::QGLScreen(int displayId)
+ : QScreen(displayId, GLClass), d_ptr(new QGLScreenPrivate)
+{
+ d_ptr->options = NoOptions;
+ d_ptr->functions = new QGLScreenSurfaceFunctions();
+}
+
+QGLScreen::~QGLScreen()
+{
+ delete d_ptr->functions;
+ delete d_ptr;
+}
+
+/*!
+ \since 4.3
+ \obsolete
+
+ Initializes the \a context and sets up the QGLWindowSurface of the
+ QWidget of \a context based on the parameters of \a context and
+ based on its own requirements. The format() of \a context needs
+ to be updated with the actual parameters of the OpenGLES drawable
+ that was set up.
+
+ \a shareContext is used in the same way as for QGLContext. It is
+ the context with which \a context shares display lists and texture
+ ids etc. The window surface must be set up so that this sharing
+ works.
+
+ Returns true in case of success and false if it is not possible to
+ create the necessary OpenGLES drawable/context.
+
+ Since 4.4.2, this function will be not be called if options()
+ indicates that a native window or pixmap drawable can be created
+ via the functions in the surfaceFunctions() object.
+
+ This function is obsolete in Qt 4.5 and higher. Use surfaceFunctions()
+ instead.
+
+ \sa options(), surfaceFunctions()
+*/
+bool
+QGLScreen::chooseContext(QGLContext *context, const QGLContext *shareContext)
+{
+ Q_UNUSED(context);
+ Q_UNUSED(shareContext);
+ return false;
+}
+
+/*!
+ \enum QGLScreen::Option
+ This enum defines options that can be set on QGLScreen instances.
+
+ \value NoOptions There are no special options on the screen. This is the default.
+ \value NativeWindows Native windows can be created with QGLScreenSurfaceFunctions::createNativeWindow().
+ \value NativePixmaps Native pixmaps can be created with QGLScreenSurfaceFunctions::createNativePixmap().
+ \value NativeImages Native images can be created with QGLScreenSurfaceFunctions::createNativeImage().
+ \value Overlays The screen supports GL overlays.
+*/
+
+/*!
+ \since 4.4.2
+
+ Returns the options associated with this QGLScreen.
+
+ \sa setOptions()
+*/
+QGLScreen::Options QGLScreen::options() const
+{
+ return d_ptr->options;
+}
+
+/*!
+ \since 4.4.2
+
+ Sets the options associated with this QGLScreen to \a value.
+
+ \sa options()
+*/
+void QGLScreen::setOptions(QGLScreen::Options value)
+{
+ d_ptr->options = value;
+}
+
+/*!
+ \since 4.4.2
+
+ Returns the surface functions object for this QGLScreen.
+
+ \sa setSurfaceFunctions()
+*/
+QGLScreenSurfaceFunctions *QGLScreen::surfaceFunctions() const
+{
+ return d_ptr->functions;
+}
+
+/*!
+ \since 4.4.2
+
+ Sets the surface functions object for this QGLScreen to \a functions.
+ The QGLScreen will take over ownership of \a functions and delete
+ it when the QGLScreen is deleted.
+
+ \sa setSurfaceFunctions()
+*/
+void QGLScreen::setSurfaceFunctions(QGLScreenSurfaceFunctions *functions)
+{
+ if (functions && functions != d_ptr->functions) {
+ delete d_ptr->functions;
+ d_ptr->functions = functions;
+ }
+}
+
+/*!
+ \internal
+ \preliminary
+ \class QGLScreenSurfaceFunctions
+ \brief The QGLScreenSurfaceFunctions class encapsulates the functions for creating native windows and pixmaps for OpenGL ES.
+*/
+
+/*!
+ \since 4.4.2
+
+ Creates a native OpenGLES drawable for the surface of \a widget and
+ returns it in \a native. Returns true if the OpenGLES drawable could
+ be created, or false if windows are not supported.
+
+ This function will be called if the NativeWindows option is set on
+ the screen.
+
+ \sa createNativePixmap(), createNativeImage(), QGLScreen::options()
+*/
+bool QGLScreenSurfaceFunctions::createNativeWindow(QWidget *widget, EGLNativeWindowType *native)
+{
+ Q_UNUSED(widget);
+ Q_UNUSED(native);
+ return false;
+}
+
+/*!
+ \since 4.4.2
+
+ Creates a native OpenGLES drawable for directly rendering into
+ \a pixmap and returns it in \a native. Returns true if the OpenGLES
+ drawable could be created, or false if direct rendering into pixmaps
+ is not supported.
+
+ This function will be called if the NativePixmaps option is set on
+ the screen.
+
+ \sa createNativeWindow(), createNativeImage(), QGLScreen::options()
+*/
+bool QGLScreenSurfaceFunctions::createNativePixmap(QPixmap *pixmap, EGLNativePixmapType *native)
+{
+ Q_UNUSED(pixmap);
+ Q_UNUSED(native);
+ return false;
+}
+
+/*!
+ \since 4.4.2
+
+ Creates a native OpenGLES drawable for directly rendering into
+ \a image and returns it in \a native. Returns true if the OpenGLES
+ drawable could be created, or false if direct rendering into images
+ is not supported.
+
+ This function will be called if the NativeImages option is set on
+ the screen.
+
+ \sa createNativeWindow(), createNativePixmap(), QGLScreen::options()
+*/
+bool QGLScreenSurfaceFunctions::createNativeImage(QImage *image, EGLNativePixmapType *native)
+{
+ Q_UNUSED(image);
+ Q_UNUSED(native);
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglscreen_qws.h b/src/opengl/qglscreen_qws.h
new file mode 100644
index 0000000000..1fefc1ce6e
--- /dev/null
+++ b/src/opengl/qglscreen_qws.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCREENEGL_P_H
+#define QSCREENEGL_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 QScreenEGL class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/QScreen>
+#include <QtOpenGL/qgl.h>
+#if defined(QT_OPENGL_ES_2)
+#include <EGL/egl.h>
+#else
+#include <GLES/egl.h>
+#endif
+#if !defined(EGL_VERSION_1_3) && !defined(QEGL_NATIVE_TYPES_DEFINED)
+#undef EGLNativeWindowType
+#undef EGLNativePixmapType
+#undef EGLNativeDisplayType
+typedef NativeWindowType EGLNativeWindowType;
+typedef NativePixmapType EGLNativePixmapType;
+typedef NativeDisplayType EGLNativeDisplayType;
+#define QEGL_NATIVE_TYPES_DEFINED 1
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLScreenPrivate;
+
+class Q_OPENGL_EXPORT QGLScreenSurfaceFunctions
+{
+public:
+ virtual bool createNativeWindow(QWidget *widget, EGLNativeWindowType *native);
+ virtual bool createNativePixmap(QPixmap *pixmap, EGLNativePixmapType *native);
+ virtual bool createNativeImage(QImage *image, EGLNativePixmapType *native);
+};
+
+class Q_OPENGL_EXPORT QGLScreen : public QScreen
+{
+ Q_DECLARE_PRIVATE(QGLScreen)
+public:
+ QGLScreen(int displayId);
+ virtual ~QGLScreen();
+
+ enum Option
+ {
+ NoOptions = 0,
+ NativeWindows = 1,
+ NativePixmaps = 2,
+ NativeImages = 4,
+ Overlays = 8
+ };
+ Q_DECLARE_FLAGS(Options, Option)
+
+ QGLScreen::Options options() const;
+
+ virtual bool chooseContext(QGLContext *context, const QGLContext *shareContext);
+ virtual bool hasOpenGL() = 0;
+
+ QGLScreenSurfaceFunctions *surfaceFunctions() const;
+
+protected:
+ void setOptions(QGLScreen::Options value);
+ void setSurfaceFunctions(QGLScreenSurfaceFunctions *functions);
+
+private:
+ QGLScreenPrivate *d_ptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGLScreen::Options)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSCREENEGL_P_H
diff --git a/src/opengl/qglshaderprogram.cpp b/src/opengl/qglshaderprogram.cpp
new file mode 100644
index 0000000000..4598bffabb
--- /dev/null
+++ b/src/opengl/qglshaderprogram.cpp
@@ -0,0 +1,3354 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglshaderprogram.h"
+#include "qglextensions_p.h"
+#include "qgl_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
+
+#if !defined(QT_OPENGL_ES_1)
+
+/*!
+ \class QGLShaderProgram
+ \brief The QGLShaderProgram class allows OpenGL shader programs to be linked and used.
+ \since 4.6
+ \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).
+
+ QGLShader and QGLShaderProgram 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 QGLContext by calling
+ QGLShaderProgram::bind():
+
+ \snippet doc/src/snippets/code/src_opengl_qglshaderprogram.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 QGLShaderProgram 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_qglshaderprogram.cpp 1
+
+ With the above shader program active, we can draw a green triangle
+ as follows:
+
+ \snippet doc/src/snippets/code/src_opengl_qglshaderprogram.cpp 2
+
+ \section1 Binary shaders and programs
+
+ Binary shaders may be specified using \c{glShaderBinary()} on
+ the return value from QGLShader::shaderId(). The QGLShader 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 QGLShader
+*/
+
+/*!
+ \class QGLShader
+ \brief The QGLShader 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).
+
+ QGLShader and QGLShaderProgram shelter the programmer from the details of
+ compiling and linking vertex and fragment shaders.
+
+ \sa QGLShaderProgram
+*/
+
+/*!
+ \enum QGLShader::ShaderTypeBit
+ This enum specifies the type of QGLShader 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 QGLShaderPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QGLShader)
+public:
+ QGLShaderPrivate(const QGLContext *context, QGLShader::ShaderType type)
+ : shaderGuard(context)
+ , shaderType(type)
+ , compiled(false)
+ {
+ }
+ ~QGLShaderPrivate();
+
+ QGLSharedResourceGuard shaderGuard;
+ QGLShader::ShaderType shaderType;
+ bool compiled;
+ QString log;
+
+ bool create();
+ bool compile(QGLShader *q);
+ void deleteShader();
+};
+
+#define ctx shaderGuard.context()
+
+QGLShaderPrivate::~QGLShaderPrivate()
+{
+ if (shaderGuard.id()) {
+ QGLShareContextScope scope(shaderGuard.context());
+ glDeleteShader(shaderGuard.id());
+ }
+}
+
+bool QGLShaderPrivate::create()
+{
+ const QGLContext *context = shaderGuard.context();
+ if (!context)
+ return false;
+ if (qt_resolve_glsl_extensions(const_cast<QGLContext *>(context))) {
+ GLuint shader;
+ if (shaderType == QGLShader::Vertex)
+ shader = glCreateShader(GL_VERTEX_SHADER);
+ else if (shaderType == QGLShader::Geometry)
+ shader = glCreateShader(GL_GEOMETRY_SHADER_EXT);
+ else
+ shader = glCreateShader(GL_FRAGMENT_SHADER);
+ if (!shader) {
+ qWarning() << "QGLShader: could not create shader";
+ return false;
+ }
+ shaderGuard.setId(shader);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool QGLShaderPrivate::compile(QGLShader *q)
+{
+ GLuint shader = shaderGuard.id();
+ if (!shader)
+ return false;
+ glCompileShader(shader);
+ GLint value = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &value);
+ compiled = (value != 0);
+ value = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &value);
+ if (!compiled && value > 1) {
+ char *logbuf = new char [value];
+ GLint len;
+ 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 == QGLShader::Fragment)
+ type = types[0];
+ else if (shaderType == QGLShader::Vertex)
+ type = types[1];
+ else if (shaderType == QGLShader::Geometry)
+ type = types[2];
+
+ if (name.isEmpty())
+ qWarning("QGLShader::compile(%s): %s", type, qPrintable(log));
+ else
+ qWarning("QGLShader::compile(%s)[%s]: %s", type, qPrintable(name), qPrintable(log));
+
+ delete [] logbuf;
+ }
+ return compiled;
+}
+
+void QGLShaderPrivate::deleteShader()
+{
+ if (shaderGuard.id()) {
+ glDeleteShader(shaderGuard.id());
+ shaderGuard.setId(0);
+ }
+}
+
+#undef ctx
+#define ctx d->shaderGuard.context()
+
+/*!
+ Constructs a new QGLShader object of the specified \a type
+ and attaches it to \a parent. If shader programs are not supported,
+ QGLShaderProgram::hasOpenGLShaderPrograms() will return false.
+
+ This constructor is normally followed by a call to compileSourceCode()
+ or compileSourceFile().
+
+ The shader will be associated with the current QGLContext.
+
+ \sa compileSourceCode(), compileSourceFile()
+*/
+QGLShader::QGLShader(QGLShader::ShaderType type, QObject *parent)
+ : QObject(*new QGLShaderPrivate(QGLContext::currentContext(), type), parent)
+{
+ Q_D(QGLShader);
+ d->create();
+}
+
+/*!
+ Constructs a new QGLShader object of the specified \a type
+ and attaches it to \a parent. If shader programs are not supported,
+ then QGLShaderProgram::hasOpenGLShaderPrograms() will return false.
+
+ This constructor is normally followed by a call to compileSourceCode()
+ or compileSourceFile().
+
+ The shader will be associated with \a context.
+
+ \sa compileSourceCode(), compileSourceFile()
+*/
+QGLShader::QGLShader(QGLShader::ShaderType type, const QGLContext *context, QObject *parent)
+ : QObject(*new QGLShaderPrivate(context ? context : QGLContext::currentContext(), type), parent)
+{
+ Q_D(QGLShader);
+#ifndef QT_NO_DEBUG
+ if (context && !QGLContext::areSharing(context, QGLContext::currentContext())) {
+ qWarning("QGLShader::QGLShader: \'context\' must be the current context or sharing with it.");
+ return;
+ }
+#endif
+ d->create();
+}
+
+/*!
+ Deletes this shader. If the shader has been attached to a
+ QGLShaderProgram object, then the actual shader will stay around
+ until the QGLShaderProgram is destroyed.
+*/
+QGLShader::~QGLShader()
+{
+}
+
+/*!
+ Returns the type of this shader.
+*/
+QGLShader::ShaderType QGLShader::shaderType() const
+{
+ Q_D(const QGLShader);
+ 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.
+#ifndef QT_OPENGL_ES
+#define QGL_DEFINE_QUALIFIERS 1
+static const char qualifierDefines[] =
+ "#define lowp\n"
+ "#define mediump\n"
+ "#define highp\n";
+#endif
+
+// The "highp" qualifier doesn't exist in fragment shaders
+// on all ES platforms. When it doesn't exist, use "mediump".
+#ifdef QT_OPENGL_ES
+#define QGL_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 QGLShader::compileSourceCode(const char *source)
+{
+ Q_D(QGLShader);
+ if (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 QGL_DEFINE_QUALIFIERS
+ src.append(qualifierDefines);
+ srclen.append(GLint(sizeof(qualifierDefines) - 1));
+#endif
+#ifdef QGL_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)));
+ 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 QGLShader::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 QGLShader::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 QGLShader::compileSourceFile(const QString& fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly)) {
+ qWarning() << "QGLShader: 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 QGLShader::sourceCode() const
+{
+ Q_D(const QGLShader);
+ GLuint shader = d->shaderGuard.id();
+ if (!shader)
+ return QByteArray();
+ GLint size = 0;
+ glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &size);
+ if (size <= 0)
+ return QByteArray();
+ GLint len = 0;
+ char *source = new char [size];
+ 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 QGLShader::isCompiled() const
+{
+ Q_D(const QGLShader);
+ return d->compiled;
+}
+
+/*!
+ Returns the errors and warnings that occurred during the last compile.
+
+ \sa compileSourceCode(), compileSourceFile()
+*/
+QString QGLShader::log() const
+{
+ Q_D(const QGLShader);
+ return d->log;
+}
+
+/*!
+ Returns the OpenGL identifier associated with this shader.
+
+ \sa QGLShaderProgram::programId()
+*/
+GLuint QGLShader::shaderId() const
+{
+ Q_D(const QGLShader);
+ return d->shaderGuard.id();
+}
+
+
+
+
+
+#undef ctx
+#define ctx programGuard.context()
+
+class QGLShaderProgramPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QGLShaderProgram)
+public:
+ QGLShaderProgramPrivate(const QGLContext *context)
+ : programGuard(context)
+ , linked(false)
+ , inited(false)
+ , removingShaders(false)
+ , geometryVertexCount(64)
+ , geometryInputType(0)
+ , geometryOutputType(0)
+ {
+ }
+ ~QGLShaderProgramPrivate();
+
+ QGLSharedResourceGuard programGuard;
+ bool linked;
+ bool inited;
+ bool removingShaders;
+
+ int geometryVertexCount;
+ GLenum geometryInputType;
+ GLenum geometryOutputType;
+
+ QString log;
+ QList<QGLShader *> shaders;
+ QList<QGLShader *> anonShaders;
+
+ bool hasShader(QGLShader::ShaderType type) const;
+};
+
+QGLShaderProgramPrivate::~QGLShaderProgramPrivate()
+{
+ if (programGuard.id()) {
+ QGLShareContextScope scope(programGuard.context());
+ glDeleteProgram(programGuard.id());
+ }
+}
+
+bool QGLShaderProgramPrivate::hasShader(QGLShader::ShaderType type) const
+{
+ foreach (QGLShader *shader, shaders) {
+ if (shader->shaderType() == type)
+ return true;
+ }
+ return false;
+}
+
+#undef ctx
+#define ctx d->programGuard.context()
+
+/*!
+ 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 QGLContext.
+
+ \sa addShader()
+*/
+QGLShaderProgram::QGLShaderProgram(QObject *parent)
+ : QObject(*new QGLShaderProgramPrivate(QGLContext::currentContext()), parent)
+{
+}
+
+/*!
+ 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 \a context.
+
+ \sa addShader()
+*/
+QGLShaderProgram::QGLShaderProgram(const QGLContext *context, QObject *parent)
+ : QObject(*new QGLShaderProgramPrivate(context), parent)
+{
+}
+
+/*!
+ Deletes this shader program.
+*/
+QGLShaderProgram::~QGLShaderProgram()
+{
+}
+
+bool QGLShaderProgram::init()
+{
+ Q_D(QGLShaderProgram);
+ if (d->programGuard.id() || d->inited)
+ return true;
+ d->inited = true;
+ const QGLContext *context = d->programGuard.context();
+ if (!context) {
+ context = QGLContext::currentContext();
+ d->programGuard.setContext(context);
+ }
+
+ if (!context)
+ return false;
+ if (qt_resolve_glsl_extensions(const_cast<QGLContext *>(context))) {
+ GLuint program = glCreateProgram();
+ if (!program) {
+ qWarning() << "QGLShaderProgram: could not create shader program";
+ return false;
+ }
+ d->programGuard.setId(program);
+ return true;
+ } else {
+ qWarning() << "QGLShaderProgram: shader programs are not supported";
+ return false;
+ }
+}
+
+/*!
+ 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 QGLShaderProgram instance
+ is deleted. This allows the caller to add the same shader
+ to multiple shader programs.
+
+ \sa addShaderFromSourceCode(), addShaderFromSourceFile()
+ \sa removeShader(), link(), removeAllShaders()
+*/
+bool QGLShaderProgram::addShader(QGLShader *shader)
+{
+ Q_D(QGLShaderProgram);
+ if (!init())
+ return false;
+ if (d->shaders.contains(shader))
+ return true; // Already added to this shader program.
+ if (d->programGuard.id() && shader) {
+ if (!QGLContext::areSharing(shader->d_func()->shaderGuard.context(),
+ d->programGuard.context())) {
+ qWarning("QGLShaderProgram::addShader: Program and shader are not associated with same context.");
+ return false;
+ }
+ if (!shader->d_func()->shaderGuard.id())
+ return false;
+ 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 QGLShader first.
+
+ \sa addShader(), addShaderFromSourceFile()
+ \sa removeShader(), link(), log(), removeAllShaders()
+*/
+bool QGLShaderProgram::addShaderFromSourceCode(QGLShader::ShaderType type, const char *source)
+{
+ Q_D(QGLShaderProgram);
+ if (!init())
+ return false;
+ QGLShader *shader = new QGLShader(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 QGLShader first.
+
+ \sa addShader(), addShaderFromSourceFile()
+ \sa removeShader(), link(), log(), removeAllShaders()
+*/
+bool QGLShaderProgram::addShaderFromSourceCode(QGLShader::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 QGLShader first.
+
+ \sa addShader(), addShaderFromSourceFile()
+ \sa removeShader(), link(), log(), removeAllShaders()
+*/
+bool QGLShaderProgram::addShaderFromSourceCode(QGLShader::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 QGLShader first.
+
+ \sa addShader(), addShaderFromSourceCode()
+*/
+bool QGLShaderProgram::addShaderFromSourceFile
+ (QGLShader::ShaderType type, const QString& fileName)
+{
+ Q_D(QGLShaderProgram);
+ if (!init())
+ return false;
+ QGLShader *shader = new QGLShader(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.
+
+ \sa addShader(), link(), removeAllShaders()
+*/
+void QGLShaderProgram::removeShader(QGLShader *shader)
+{
+ Q_D(QGLShaderProgram);
+ if (d->programGuard.id() && shader && shader->d_func()->shaderGuard.id()) {
+ QGLShareContextScope scope(d->programGuard.context());
+ 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<QGLShader *> QGLShaderProgram::shaders() const
+{
+ Q_D(const QGLShaderProgram);
+ return d->shaders;
+}
+
+/*!
+ Removes all of the shaders that were added to this program previously.
+ The QGLShader objects for the shaders will not be deleted if they
+ were constructed externally. QGLShader objects that are constructed
+ internally by QGLShaderProgram will be deleted.
+
+ \sa addShader(), removeShader()
+*/
+void QGLShaderProgram::removeAllShaders()
+{
+ Q_D(QGLShaderProgram);
+ d->removingShaders = true;
+ foreach (QGLShader *shader, d->shaders) {
+ if (d->programGuard.id() && shader && shader->d_func()->shaderGuard.id())
+ glDetachShader(d->programGuard.id(), shader->d_func()->shaderGuard.id());
+ }
+ foreach (QGLShader *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 QGLShaderProgram::link()
+{
+ Q_D(QGLShaderProgram);
+ GLuint program = d->programGuard.id();
+ 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;
+ glGetProgramiv(program, GL_LINK_STATUS, &value);
+ d->linked = (value != 0);
+ if (d->linked)
+ return true;
+ }
+
+ // Set up the geometry shader parameters
+ if (glProgramParameteriEXT) {
+ foreach (QGLShader *shader, d->shaders) {
+ if (shader->shaderType() & QGLShader::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;
+ }
+ }
+ }
+
+ glLinkProgram(program);
+ value = 0;
+ glGetProgramiv(program, GL_LINK_STATUS, &value);
+ d->linked = (value != 0);
+ value = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &value);
+ d->log = QString();
+ if (value > 1) {
+ char *logbuf = new char [value];
+ GLint len;
+ glGetProgramInfoLog(program, value, &len, logbuf);
+ d->log = QString::fromLatin1(logbuf);
+ QString name = objectName();
+ if (name.isEmpty())
+ qWarning() << "QGLShader::link:" << d->log;
+ else
+ qWarning() << "QGLShader::link[" << name << "]:" << d->log;
+ delete [] logbuf;
+ }
+ return d->linked;
+}
+
+/*!
+ Returns true if this shader program has been linked; false otherwise.
+
+ \sa link()
+*/
+bool QGLShaderProgram::isLinked() const
+{
+ Q_D(const QGLShaderProgram);
+ return d->linked;
+}
+
+/*!
+ Returns the errors and warnings that occurred during the last link()
+ or addShader() with explicitly specified source code.
+
+ \sa link()
+*/
+QString QGLShaderProgram::log() const
+{
+ Q_D(const QGLShaderProgram);
+ return d->log;
+}
+
+/*!
+ Binds this shader program to the active QGLContext 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 QGLShaderProgram::bind()
+{
+ Q_D(QGLShaderProgram);
+ GLuint program = d->programGuard.id();
+ if (!program)
+ return false;
+ if (!d->linked && !link())
+ return false;
+#ifndef QT_NO_DEBUG
+ if (!QGLContext::areSharing(d->programGuard.context(), QGLContext::currentContext())) {
+ qWarning("QGLShaderProgram::bind: program is not valid in the current context.");
+ return false;
+ }
+#endif
+ glUseProgram(program);
+ return true;
+}
+
+#undef ctx
+#define ctx QGLContext::currentContext()
+
+/*!
+ Releases the active shader program from the current QGLContext.
+ This is equivalent to calling \c{glUseProgram(0)}.
+
+ \sa bind()
+*/
+void QGLShaderProgram::release()
+{
+#ifndef QT_NO_DEBUG
+ Q_D(QGLShaderProgram);
+ if (!QGLContext::areSharing(d->programGuard.context(), QGLContext::currentContext()))
+ qWarning("QGLShaderProgram::release: program is not valid in the current context.");
+#endif
+#if defined(QT_OPENGL_ES_2)
+ glUseProgram(0);
+#else
+ if (glUseProgram)
+ glUseProgram(0);
+#endif
+}
+
+#undef ctx
+#define ctx d->programGuard.context()
+
+/*!
+ Returns the OpenGL identifier associated with this shader program.
+
+ \sa QGLShader::shaderId()
+*/
+GLuint QGLShaderProgram::programId() const
+{
+ Q_D(const QGLShaderProgram);
+ GLuint id = d->programGuard.id();
+ 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<QGLShaderProgram *>(this)->init())
+ return 0;
+ return d->programGuard.id();
+}
+
+/*!
+ 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 QGLShaderProgram::bindAttributeLocation(const char *name, int location)
+{
+ Q_D(QGLShaderProgram);
+ if (!init())
+ return;
+ 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 QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::attributeLocation(const char *name) const
+{
+ Q_D(const QGLShaderProgram);
+ if (d->linked) {
+ return glGetAttribLocation(d->programGuard.id(), name);
+ } else {
+ qWarning() << "QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::setAttributeValue(int location, GLfloat value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ glVertexAttrib1fv(location, &value);
+}
+
+/*!
+ \overload
+
+ Sets the attribute called \a name in the current context to \a value.
+
+ \sa setUniformValue()
+*/
+void QGLShaderProgram::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 QGLShaderProgram::setAttributeValue(int location, GLfloat x, GLfloat y)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ GLfloat values[2] = {x, y};
+ 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 QGLShaderProgram::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 QGLShaderProgram::setAttributeValue
+ (int location, GLfloat x, GLfloat y, GLfloat z)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ GLfloat values[3] = {x, y, z};
+ 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 QGLShaderProgram::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 QGLShaderProgram::setAttributeValue
+ (int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ GLfloat values[4] = {x, y, z, w};
+ 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 QGLShaderProgram::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 QGLShaderProgram::setAttributeValue(int location, const QVector2D& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ glVertexAttrib2fv(location, reinterpret_cast<const GLfloat *>(&value));
+}
+
+/*!
+ \overload
+
+ Sets the attribute called \a name in the current context to \a value.
+
+ \sa setUniformValue()
+*/
+void QGLShaderProgram::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 QGLShaderProgram::setAttributeValue(int location, const QVector3D& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ glVertexAttrib3fv(location, reinterpret_cast<const GLfloat *>(&value));
+}
+
+/*!
+ \overload
+
+ Sets the attribute called \a name in the current context to \a value.
+
+ \sa setUniformValue()
+*/
+void QGLShaderProgram::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 QGLShaderProgram::setAttributeValue(int location, const QVector4D& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ glVertexAttrib4fv(location, reinterpret_cast<const GLfloat *>(&value));
+}
+
+/*!
+ \overload
+
+ Sets the attribute called \a name in the current context to \a value.
+
+ \sa setUniformValue()
+*/
+void QGLShaderProgram::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 QGLShaderProgram::setAttributeValue(int location, const QColor& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ GLfloat values[4] = {GLfloat(value.redF()), GLfloat(value.greenF()),
+ GLfloat(value.blueF()), GLfloat(value.alphaF())};
+ glVertexAttrib4fv(location, values);
+ }
+}
+
+/*!
+ \overload
+
+ Sets the attribute called \a name in the current context to \a value.
+
+ \sa setUniformValue()
+*/
+void QGLShaderProgram::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 QGLShaderProgram::setAttributeValue
+ (int location, const GLfloat *values, int columns, int rows)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (rows < 1 || rows > 4) {
+ qWarning() << "QGLShaderProgram::setAttributeValue: rows" << rows << "not supported";
+ return;
+ }
+ if (location != -1) {
+ while (columns-- > 0) {
+ if (rows == 1)
+ glVertexAttrib1fv(location, values);
+ else if (rows == 2)
+ glVertexAttrib2fv(location, values);
+ else if (rows == 3)
+ glVertexAttrib3fv(location, values);
+ else
+ 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 QGLShaderProgram::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 QGLShaderProgram::setAttributeArray
+ (int location, const GLfloat *values, int tupleSize, int stride)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ 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 QGLShaderProgram::setAttributeArray
+ (int location, const QVector2D *values, int stride)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ 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 QGLShaderProgram::setAttributeArray
+ (int location, const QVector3D *values, int stride)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ 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 QGLShaderProgram::setAttributeArray
+ (int location, const QVector4D *values, int stride)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ 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 QGLShaderProgram::setAttributeArray
+ (int location, GLenum type, const void *values, int tupleSize, int stride)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ 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 QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::setAttributeBuffer
+ (int location, GLenum type, int offset, int tupleSize, int stride)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ 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 QGLShaderProgram::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 QGLShaderProgram::enableAttributeArray(int location)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::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 QGLShaderProgram::disableAttributeArray(int location)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::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 QGLShaderProgram::uniformLocation(const char *name) const
+{
+ Q_D(const QGLShaderProgram);
+ Q_UNUSED(d);
+ if (d->linked) {
+ return glGetUniformLocation(d->programGuard.id(), name);
+ } else {
+ qWarning() << "QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, GLfloat value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ glUniform1fv(location, 1, &value);
+}
+
+/*!
+ \overload
+
+ Sets the uniform variable called \a name in the current context
+ to \a value.
+
+ \sa setAttributeValue()
+*/
+void QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, GLint value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ glUniform1i(location, value);
+}
+
+/*!
+ \overload
+
+ Sets the uniform variable called \a name in the current context
+ to \a value.
+
+ \sa setAttributeValue()
+*/
+void QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, GLuint value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, GLfloat x, GLfloat y)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ GLfloat values[2] = {x, y};
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue
+ (int location, GLfloat x, GLfloat y, GLfloat z)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ GLfloat values[3] = {x, y, z};
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue
+ (int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ GLfloat values[4] = {x, y, z, w};
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QVector2D& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QVector3D& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QVector4D& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QColor& color)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ GLfloat values[4] = {GLfloat(color.redF()), GLfloat(color.greenF()),
+ GLfloat(color.blueF()), GLfloat(color.alphaF())};
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QPoint& point)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ GLfloat values[4] = {GLfloat(point.x()), GLfloat(point.y())};
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QPointF& point)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ GLfloat values[4] = {GLfloat(point.x()), GLfloat(point.y())};
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QSize& size)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ GLfloat values[4] = {GLfloat(size.width()), GLfloat(size.height())};
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QSizeF& size)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ GLfloat values[4] = {GLfloat(size.width()), GLfloat(size.height())};
+ 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 QGLShaderProgram::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); \
+ }
+#if !defined(QT_OPENGL_ES_2)
+#define setUniformGenericMatrix(func,colfunc,location,value,cols,rows) \
+ if (location == -1) \
+ return; \
+ if (sizeof(qreal) == sizeof(GLfloat)) { \
+ const GLfloat *data = reinterpret_cast<const GLfloat *> \
+ (value.constData()); \
+ if (func) \
+ func(location, 1, GL_FALSE, data); \
+ else \
+ 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]; \
+ if (func) \
+ func(location, 1, GL_FALSE, mat); \
+ else \
+ colfunc(location, cols, mat); \
+ }
+#else
+#define setUniformGenericMatrix(func,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); \
+ }
+#endif
+
+/*!
+ Sets the uniform variable at \a location in the current context
+ to a 2x2 matrix \a value.
+
+ \sa setAttributeValue()
+*/
+void QGLShaderProgram::setUniformValue(int location, const QMatrix2x2& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformMatrix(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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QMatrix2x3& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformGenericMatrix
+ (glUniformMatrix2x3fv, 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QMatrix2x4& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformGenericMatrix
+ (glUniformMatrix2x4fv, 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QMatrix3x2& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformGenericMatrix
+ (glUniformMatrix3x2fv, 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QMatrix3x3& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformMatrix(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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QMatrix3x4& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformGenericMatrix
+ (glUniformMatrix3x4fv, 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QMatrix4x2& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformGenericMatrix
+ (glUniformMatrix4x2fv, 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QMatrix4x3& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformGenericMatrix
+ (glUniformMatrix4x3fv, 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QMatrix4x4& value)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformMatrix(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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const GLfloat value[2][2])
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::setUniformValue(int location, const GLfloat value[3][3])
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::setUniformValue(int location, const GLfloat value[4][4])
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::setUniformValue(int location, const QTransform& value)
+{
+ Q_D(QGLShaderProgram);
+ 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())}
+ };
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const GLint *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const GLuint *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const GLfloat *values, int count, int tupleSize)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ if (tupleSize == 1)
+ glUniform1fv(location, count, values);
+ else if (tupleSize == 2)
+ glUniform2fv(location, count, values);
+ else if (tupleSize == 3)
+ glUniform3fv(location, count, values);
+ else if (tupleSize == 4)
+ glUniform4fv(location, count, values);
+ else
+ qWarning() << "QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const QVector2D *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const QVector3D *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const QVector4D *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ 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 QGLShaderProgram::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()); \
+ }
+#if !defined(QT_OPENGL_ES_2)
+#define setUniformGenericMatrixArray(func,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()); \
+ if (func) \
+ func(location, count, GL_FALSE, data); \
+ else \
+ 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]; \
+ } \
+ } \
+ if (func) \
+ func(location, count, GL_FALSE, temp.constData()); \
+ else \
+ colfunc(location, count * cols, temp.constData()); \
+ }
+#else
+#define setUniformGenericMatrixArray(func,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()); \
+ }
+#endif
+
+/*!
+ 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 QGLShaderProgram::setUniformValueArray(int location, const QMatrix2x2 *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformMatrixArray
+ (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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const QMatrix2x3 *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformGenericMatrixArray
+ (glUniformMatrix2x3fv, 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const QMatrix2x4 *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformGenericMatrixArray
+ (glUniformMatrix2x4fv, 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const QMatrix3x2 *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformGenericMatrixArray
+ (glUniformMatrix3x2fv, 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const QMatrix3x3 *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformMatrixArray
+ (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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const QMatrix3x4 *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformGenericMatrixArray
+ (glUniformMatrix3x4fv, 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const QMatrix4x2 *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformGenericMatrixArray
+ (glUniformMatrix4x2fv, 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const QMatrix4x3 *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformGenericMatrixArray
+ (glUniformMatrix4x3fv, 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 QGLShaderProgram::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 QGLShaderProgram::setUniformValueArray(int location, const QMatrix4x4 *values, int count)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ setUniformMatrixArray
+ (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 QGLShaderProgram::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 QGLShaderProgram::maxGeometryOutputVertices() const
+{
+ GLint n;
+ 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 QGLShaderProgram::setGeometryOutputVertexCount(int count)
+{
+#ifndef QT_NO_DEBUG
+ int max = maxGeometryOutputVertices();
+ if (count > max) {
+ qWarning("QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::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 QGLShaderProgram::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 QGLContext::currentContext() is used.
+*/
+bool QGLShaderProgram::hasOpenGLShaderPrograms(const QGLContext *context)
+{
+#if !defined(QT_OPENGL_ES_2)
+ if (!context)
+ context = QGLContext::currentContext();
+ if (!context)
+ return false;
+ return qt_resolve_glsl_extensions(const_cast<QGLContext *>(context));
+#else
+ Q_UNUSED(context);
+ return true;
+#endif
+}
+
+/*!
+ \internal
+*/
+void QGLShaderProgram::shaderDestroyed()
+{
+ Q_D(QGLShaderProgram);
+ QGLShader *shader = qobject_cast<QGLShader *>(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 QGLContext::currentContext() is used.
+
+ \since 4.7
+*/
+bool QGLShader::hasOpenGLShaders(ShaderType type, const QGLContext *context)
+{
+ if (!context)
+ context = QGLContext::currentContext();
+ if (!context)
+ return false;
+
+ if ((type & ~(Geometry | Vertex | Fragment)) || type == 0)
+ return false;
+
+ bool resolved = qt_resolve_glsl_extensions(const_cast<QGLContext *>(context));
+ if (!resolved)
+ return false;
+
+ if ((type & Geometry) && !QByteArray((const char *) glGetString(GL_EXTENSIONS)).contains("GL_EXT_geometry_shader4"))
+ return false;
+
+ return true;
+}
+
+
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLShaderProgram::setAttributeArray
+ (int location, QMacCompatGLenum type, const void *values, int tupleSize, int stride)
+{
+ setAttributeArray(location, GLenum(type), values, tupleSize, stride);
+}
+
+/*! \internal */
+void QGLShaderProgram::setAttributeArray
+ (const char *name, QMacCompatGLenum type, const void *values, int tupleSize, int stride)
+{
+ setAttributeArray(name, GLenum(type), values, tupleSize, stride);
+}
+
+/*! \internal */
+void QGLShaderProgram::setAttributeBuffer
+ (int location, QMacCompatGLenum type, int offset, int tupleSize, int stride)
+{
+ setAttributeBuffer(location, GLenum(type), offset, tupleSize, stride);
+}
+
+/*! \internal */
+void QGLShaderProgram::setAttributeBuffer
+ (const char *name, QMacCompatGLenum type, int offset, int tupleSize, int stride)
+{
+ setAttributeBuffer(name, GLenum(type), offset, tupleSize, stride);
+}
+
+/*! \internal */
+void QGLShaderProgram::setUniformValue(int location, QMacCompatGLint value)
+{
+ setUniformValue(location, GLint(value));
+}
+
+/*! \internal */
+void QGLShaderProgram::setUniformValue(int location, QMacCompatGLuint value)
+{
+ setUniformValue(location, GLuint(value));
+}
+
+/*! \internal */
+void QGLShaderProgram::setUniformValue(const char *name, QMacCompatGLint value)
+{
+ setUniformValue(name, GLint(value));
+}
+
+/*! \internal */
+void QGLShaderProgram::setUniformValue(const char *name, QMacCompatGLuint value)
+{
+ setUniformValue(name, GLuint(value));
+}
+
+/*! \internal */
+void QGLShaderProgram::setUniformValueArray(int location, const QMacCompatGLint *values, int count)
+{
+ setUniformValueArray(location, (const GLint *)values, count);
+}
+
+/*! \internal */
+void QGLShaderProgram::setUniformValueArray(int location, const QMacCompatGLuint *values, int count)
+{
+ setUniformValueArray(location, (const GLuint *)values, count);
+}
+
+/*! \internal */
+void QGLShaderProgram::setUniformValueArray(const char *name, const QMacCompatGLint *values, int count)
+{
+ setUniformValueArray(name, (const GLint *)values, count);
+}
+
+/*! \internal */
+void QGLShaderProgram::setUniformValueArray(const char *name, const QMacCompatGLuint *values, int count)
+{
+ setUniformValueArray(name, (const GLuint *)values, count);
+}
+#endif
+
+#endif // !defined(QT_OPENGL_ES_1)
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglshaderprogram.h b/src/opengl/qglshaderprogram.h
new file mode 100644
index 0000000000..83a4f04db0
--- /dev/null
+++ b/src/opengl/qglshaderprogram.h
@@ -0,0 +1,344 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLSHADERPROGRAM_H
+#define QGLSHADERPROGRAM_H
+
+#include <QtOpenGL/qgl.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(OpenGL)
+
+#if !defined(QT_OPENGL_ES_1)
+
+class QGLShaderProgram;
+class QGLShaderPrivate;
+
+class Q_OPENGL_EXPORT QGLShader : public QObject
+{
+ Q_OBJECT
+public:
+ enum ShaderTypeBit
+ {
+ Vertex = 0x0001,
+ Fragment = 0x0002,
+ Geometry = 0x0004
+ };
+ Q_DECLARE_FLAGS(ShaderType, ShaderTypeBit)
+
+ explicit QGLShader(QGLShader::ShaderType type, QObject *parent = 0);
+ QGLShader(QGLShader::ShaderType type, const QGLContext *context, QObject *parent = 0);
+ virtual ~QGLShader();
+
+ QGLShader::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, const QGLContext *context = 0);
+
+private:
+ friend class QGLShaderProgram;
+
+ Q_DISABLE_COPY(QGLShader)
+ Q_DECLARE_PRIVATE(QGLShader)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGLShader::ShaderType)
+
+
+class QGLShaderProgramPrivate;
+
+#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_OPENGL_EXPORT QGLShaderProgram : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QGLShaderProgram(QObject *parent = 0);
+ explicit QGLShaderProgram(const QGLContext *context, QObject *parent = 0);
+ virtual ~QGLShaderProgram();
+
+ bool addShader(QGLShader *shader);
+ void removeShader(QGLShader *shader);
+ QList<QGLShader *> shaders() const;
+
+ bool addShaderFromSourceCode(QGLShader::ShaderType type, const char *source);
+ bool addShaderFromSourceCode(QGLShader::ShaderType type, const QByteArray& source);
+ bool addShaderFromSourceCode(QGLShader::ShaderType type, const QString& source);
+ bool addShaderFromSourceFile(QGLShader::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);
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ void setAttributeArray
+ (int location, QMacCompatGLenum type, const void *values, int tupleSize, int stride = 0);
+ void setAttributeArray
+ (const char *name, QMacCompatGLenum type, const void *values, int tupleSize, int stride = 0);
+ void setAttributeBuffer
+ (int location, QMacCompatGLenum type, int offset, int tupleSize, int stride = 0);
+ void setAttributeBuffer
+ (const char *name, QMacCompatGLenum type, int offset, int tupleSize, int stride = 0);
+#endif
+
+ 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;
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ void setUniformValue(int location, QMacCompatGLint value);
+ void setUniformValue(int location, QMacCompatGLuint value);
+ void setUniformValue(const char *name, QMacCompatGLint value);
+ void setUniformValue(const char *name, QMacCompatGLuint value);
+ void setUniformValueArray(int location, const QMacCompatGLint *values, int count);
+ void setUniformValueArray(int location, const QMacCompatGLuint *values, int count);
+ void setUniformValueArray(const char *name, const QMacCompatGLint *values, int count);
+ void setUniformValueArray(const char *name, const QMacCompatGLuint *values, int count);
+#endif
+
+ 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(const QGLContext *context = 0);
+
+private Q_SLOTS:
+ void shaderDestroyed();
+
+private:
+ Q_DISABLE_COPY(QGLShaderProgram)
+ Q_DECLARE_PRIVATE(QGLShaderProgram)
+
+ bool init();
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/opengl/qgltexturepool.cpp b/src/opengl/qgltexturepool.cpp
new file mode 100644
index 0000000000..a5472ece7f
--- /dev/null
+++ b/src/opengl/qgltexturepool.cpp
@@ -0,0 +1,244 @@
+/****************************************************************************
+**
+** 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 QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgltexturepool_p.h"
+#include "qpixmapdata_gl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_OPENGL_EXPORT extern QGLWidget* qt_gl_share_widget();
+
+static QGLTexturePool *qt_gl_texture_pool = 0;
+
+class QGLTexturePoolPrivate
+{
+public:
+ QGLTexturePoolPrivate() : lruFirst(0), lruLast(0) {}
+
+ QGLPixmapData *lruFirst;
+ QGLPixmapData *lruLast;
+};
+
+QGLTexturePool::QGLTexturePool()
+ : d_ptr(new QGLTexturePoolPrivate())
+{
+}
+
+QGLTexturePool::~QGLTexturePool()
+{
+}
+
+QGLTexturePool *QGLTexturePool::instance()
+{
+ if (!qt_gl_texture_pool)
+ qt_gl_texture_pool = new QGLTexturePool();
+ return qt_gl_texture_pool;
+}
+
+GLuint QGLTexturePool::createTextureForPixmap(GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ QGLPixmapData *data)
+{
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(target, texture);
+ do {
+ glTexImage2D(target, level, internalformat, width, height, 0, format, type, 0);
+ GLenum error = glGetError();
+ if (error == GL_NO_ERROR) {
+ if (data)
+ moveToHeadOfLRU(data);
+ return texture;
+ } else if (error != GL_OUT_OF_MEMORY) {
+ qWarning("QGLTexturePool: cannot create temporary texture because of invalid params");
+ return 0;
+ }
+ } while (reclaimSpace(internalformat, width, height, format, type, data));
+ qWarning("QGLTexturePool: cannot reclaim sufficient space for a %dx%d pixmap",
+ width, height);
+ return 0;
+}
+
+bool QGLTexturePool::createPermanentTexture(GLuint texture,
+ GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ const GLvoid *data)
+{
+ glBindTexture(target, texture);
+ do {
+ glTexImage2D(target, level, internalformat, width, height, 0, format, type, data);
+
+ GLenum error = glGetError();
+ if (error == GL_NO_ERROR) {
+ return true;
+ } else if (error != GL_OUT_OF_MEMORY) {
+ qWarning("QGLTexturePool: cannot create permanent texture because of invalid params");
+ return false;
+ }
+ } while (reclaimSpace(internalformat, width, height, format, type, 0));
+ qWarning("QGLTexturePool: cannot reclaim sufficient space for a %dx%d pixmap",
+ width, height);
+ return 0;
+}
+
+void QGLTexturePool::releaseTexture(QGLPixmapData *data, GLuint texture)
+{
+ // Very simple strategy at the moment: just destroy the texture.
+ if (data)
+ removeFromLRU(data);
+
+ QGLWidget *shareWidget = qt_gl_share_widget();
+ if (shareWidget) {
+ QGLShareContextScope ctx(shareWidget->context());
+ glDeleteTextures(1, &texture);
+ }
+}
+
+void QGLTexturePool::useTexture(QGLPixmapData *data)
+{
+ moveToHeadOfLRU(data);
+}
+
+void QGLTexturePool::detachTexture(QGLPixmapData *data)
+{
+ removeFromLRU(data);
+}
+
+bool QGLTexturePool::reclaimSpace(GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ QGLPixmapData *data)
+{
+ Q_UNUSED(internalformat); // For future use in picking the best texture to eject.
+ Q_UNUSED(width);
+ Q_UNUSED(height);
+ Q_UNUSED(format);
+ Q_UNUSED(type);
+
+ bool succeeded = false;
+ bool wasInLRU = false;
+ if (data) {
+ wasInLRU = data->inLRU;
+ moveToHeadOfLRU(data);
+ }
+
+ QGLPixmapData *lrudata = pixmapLRU();
+ if (lrudata && lrudata != data) {
+ lrudata->reclaimTexture();
+ succeeded = true;
+ }
+
+ if (data && !wasInLRU)
+ removeFromLRU(data);
+
+ return succeeded;
+}
+
+void QGLTexturePool::hibernate()
+{
+ Q_D(QGLTexturePool);
+ QGLPixmapData *pd = d->lruLast;
+ while (pd) {
+ QGLPixmapData *prevLRU = pd->prevLRU;
+ pd->inTexturePool = false;
+ pd->inLRU = false;
+ pd->nextLRU = 0;
+ pd->prevLRU = 0;
+ pd->hibernate();
+ pd = prevLRU;
+ }
+ d->lruFirst = 0;
+ d->lruLast = 0;
+}
+
+void QGLTexturePool::moveToHeadOfLRU(QGLPixmapData *data)
+{
+ Q_D(QGLTexturePool);
+ if (data->inLRU) {
+ if (!data->prevLRU)
+ return; // Already at the head of the list.
+ removeFromLRU(data);
+ }
+ data->inLRU = true;
+ data->nextLRU = d->lruFirst;
+ data->prevLRU = 0;
+ if (d->lruFirst)
+ d->lruFirst->prevLRU = data;
+ else
+ d->lruLast = data;
+ d->lruFirst = data;
+}
+
+void QGLTexturePool::removeFromLRU(QGLPixmapData *data)
+{
+ Q_D(QGLTexturePool);
+ if (!data->inLRU)
+ return;
+ if (data->nextLRU)
+ data->nextLRU->prevLRU = data->prevLRU;
+ else
+ d->lruLast = data->prevLRU;
+ if (data->prevLRU)
+ data->prevLRU->nextLRU = data->nextLRU;
+ else
+ d->lruFirst = data->nextLRU;
+ data->inLRU = false;
+}
+
+QGLPixmapData *QGLTexturePool::pixmapLRU()
+{
+ Q_D(QGLTexturePool);
+ return d->lruLast;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgltexturepool_p.h b/src/opengl/qgltexturepool_p.h
new file mode 100644
index 0000000000..8b6f726316
--- /dev/null
+++ b/src/opengl/qgltexturepool_p.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** 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 QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLTEXTUREPOOL_P_H
+#define QGLTEXTUREPOOL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgl.h"
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGLPixmapData;
+class QGLTexturePoolPrivate;
+
+class QGLTexturePool
+{
+public:
+ QGLTexturePool();
+ virtual ~QGLTexturePool();
+
+ static QGLTexturePool *instance();
+
+ // Create a new texture with the specified parameters and associate
+ // it with "data". The QGLPixmapData will be notified when the
+ // texture needs to be reclaimed by the pool.
+ //
+ // This function will call reclaimSpace() when texture creation fails.
+ GLuint createTextureForPixmap(GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ QGLPixmapData *data);
+
+ // Create a permanent texture with the specified parameters.
+ // If there is insufficient space for the texture,
+ // then this function will call reclaimSpace() and try again.
+ //
+ // The caller is responsible for calling glDeleteTextures()
+ // when it no longer needs the texture, as the texture is not
+ // recorded in the texture pool.
+ bool createPermanentTexture(GLuint texture,
+ GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ const GLvoid *data);
+
+ // Release a texture that is no longer required.
+ void releaseTexture(QGLPixmapData *data, GLuint texture);
+
+ // Notify the pool that a QGLPixmapData object is using
+ // an texture again. This allows the pool to move the texture
+ // within a least-recently-used list of QGLPixmapData objects.
+ void useTexture(QGLPixmapData *data);
+
+ // Notify the pool that the texture associated with a
+ // QGLPixmapData is being detached from the pool. The caller
+ // will become responsible for calling glDeleteTextures().
+ void detachTexture(QGLPixmapData *data);
+
+ // Reclaim space for an image allocation with the specified parameters.
+ // Returns true if space was reclaimed, or false if there is no
+ // further space that can be reclaimed. The "data" parameter
+ // indicates the pixmap that is trying to obtain space which should
+ // not itself be reclaimed.
+ bool reclaimSpace(GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ QGLPixmapData *data);
+
+ // Hibernate the image pool because the context is about to be
+ // destroyed. All textures left in the pool should be released.
+ void hibernate();
+
+protected:
+ // Helper functions for managing the LRU list of QGLPixmapData objects.
+ void moveToHeadOfLRU(QGLPixmapData *data);
+ void removeFromLRU(QGLPixmapData *data);
+ QGLPixmapData *pixmapLRU();
+
+private:
+ QScopedPointer<QGLTexturePoolPrivate> d_ptr;
+
+ Q_DECLARE_PRIVATE(QGLTexturePool)
+ Q_DISABLE_COPY(QGLTexturePool)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/opengl/qglwindowsurface_qws.cpp b/src/opengl/qglwindowsurface_qws.cpp
new file mode 100644
index 0000000000..8d1d733d79
--- /dev/null
+++ b/src/opengl/qglwindowsurface_qws.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/QPaintDevice>
+#include <QtGui/QWidget>
+#include <QtOpenGL/QGLWidget>
+#include "private/qglwindowsurface_qws_p.h"
+#include "private/qpaintengine_opengl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWSGLWindowSurface
+ \since 4.3
+ \ingroup qws
+ \preliminary
+
+ \brief The QWSGLWindowSurface class provides the drawing area for top-level
+ windows with Qt for Embedded Linux on EGL/OpenGL ES. It also provides the
+ drawing area for \l{QGLWidget}s whether they are top-level windows or
+ children of another QWidget.
+
+ Note that this class is only available in Qt for Embedded Linux and only
+ available if Qt is configured with OpenGL support.
+*/
+
+class QWSGLWindowSurfacePrivate
+{
+public:
+ QWSGLWindowSurfacePrivate() :
+ qglContext(0), ownsContext(false) {}
+
+ QGLContext *qglContext;
+ bool ownsContext;
+};
+
+/*!
+ Constructs an empty QWSGLWindowSurface for the given top-level \a window.
+ The window surface is later initialized from chooseContext() and resources for it
+ is typically allocated in setGeometry().
+*/
+QWSGLWindowSurface::QWSGLWindowSurface(QWidget *window)
+ : QWSWindowSurface(window),
+ d_ptr(new QWSGLWindowSurfacePrivate)
+{
+}
+
+/*!
+ Constructs an empty QWSGLWindowSurface.
+*/
+QWSGLWindowSurface::QWSGLWindowSurface()
+ : d_ptr(new QWSGLWindowSurfacePrivate)
+{
+}
+
+/*!
+ Destroys the QWSGLWindowSurface object and frees any
+ allocated resources.
+ */
+QWSGLWindowSurface::~QWSGLWindowSurface()
+{
+ Q_D(QWSGLWindowSurface);
+ if (d->ownsContext)
+ delete d->qglContext;
+ delete d;
+}
+
+/*!
+ Returns the QGLContext of the window surface.
+*/
+QGLContext *QWSGLWindowSurface::context() const
+{
+ Q_D(const QWSGLWindowSurface);
+ if (!d->qglContext) {
+ QWSGLWindowSurface *that = const_cast<QWSGLWindowSurface*>(this);
+ that->setContext(new QGLContext(QGLFormat::defaultFormat()));
+ that->d_func()->ownsContext = true;
+ }
+ return d->qglContext;
+}
+
+/*!
+ Sets the QGLContext for this window surface to \a context.
+*/
+void QWSGLWindowSurface::setContext(QGLContext *context)
+{
+ Q_D(QWSGLWindowSurface);
+ if (d->ownsContext) {
+ delete d->qglContext;
+ d->ownsContext = false;
+ }
+ d->qglContext = context;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglwindowsurface_qws_p.h b/src/opengl/qglwindowsurface_qws_p.h
new file mode 100644
index 0000000000..41d77e8e2d
--- /dev/null
+++ b/src/opengl/qglwindowsurface_qws_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLWINDOWSURFACE_QWS_P_H
+#define QGLWINDOWSURFACE_QWS_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 QWSGLWindowSurface class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+#include <QPaintDevice>
+#include "private/qwindowsurface_qws_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+class QPoint;
+class QRegion;
+class QSize;
+class QWidget;
+class QGLContext;
+
+class QWSGLWindowSurfacePrivate;
+
+class Q_OPENGL_EXPORT QWSGLWindowSurface : public QWSWindowSurface
+{
+ Q_DECLARE_PRIVATE(QWSGLWindowSurface)
+
+public:
+ QWSGLWindowSurface(QWidget *widget);
+ QWSGLWindowSurface();
+ ~QWSGLWindowSurface();
+
+ QGLContext *context() const;
+ void setContext(QGLContext *context);
+
+private:
+ QWSGLWindowSurfacePrivate *d_ptr;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QGLWINDOWSURFACE_QWS_P_H
diff --git a/src/opengl/qgraphicsshadereffect.cpp b/src/opengl/qgraphicsshadereffect.cpp
new file mode 100644
index 0000000000..6983d9057e
--- /dev/null
+++ b/src/opengl/qgraphicsshadereffect.cpp
@@ -0,0 +1,314 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicsshadereffect_p.h"
+#if !defined(QT_OPENGL_ES_1)
+#include "qglshaderprogram.h"
+#include "gl2paintengineex/qglcustomshaderstage_p.h"
+#define QGL_HAVE_CUSTOM_SHADERS 1
+#endif
+#include <QtGui/qpainter.h>
+#include <QtGui/qgraphicsitem.h>
+#include <QtGui/private/qgraphicseffect_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*#
+ \class QGraphicsShaderEffect
+ \brief The QGraphicsShaderEffect class is the base class for creating
+ custom GLSL shader effects in a QGraphicsScene.
+ \since 4.6
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ The specific effect is defined by a fragment of GLSL source code
+ supplied to setPixelShaderFragment(). This source code must define a
+ function with the signature
+ \c{lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords)}
+ that returns the source pixel value
+ to use in the paint engine's shader program. The shader fragment
+ is linked with the regular shader code used by the GL2 paint engine
+ to construct a complete QGLShaderProgram.
+
+ The following example shader converts the incoming pixmap to
+ grayscale and then applies a colorize operation using the
+ \c effectColor value:
+
+ \code
+ static char const colorizeShaderCode[] =
+ "uniform lowp vec4 effectColor;\n"
+ "lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords) {\n"
+ " vec4 src = texture2D(imageTexture, textureCoords);\n"
+ " float gray = dot(src.rgb, vec3(0.212671, 0.715160, 0.072169));\n"
+ " vec4 colorize = 1.0-((1.0-gray)*(1.0-effectColor));\n"
+ " return vec4(colorize.rgb, src.a);\n"
+ "}";
+ \endcode
+
+ To use this shader code, it is necessary to define a subclass
+ of QGraphicsShaderEffect as follows:
+
+ \code
+ class ColorizeEffect : public QGraphicsShaderEffect
+ {
+ Q_OBJECT
+ public:
+ ColorizeEffect(QObject *parent = 0)
+ : QGraphicsShaderEffect(parent), color(Qt::black)
+ {
+ setPixelShaderFragment(colorizeShaderCode);
+ }
+
+ QColor effectColor() const { return color; }
+ void setEffectColor(const QColor& c)
+ {
+ color = c;
+ setUniformsDirty();
+ }
+
+ protected:
+ void setUniforms(QGLShaderProgram *program)
+ {
+ program->setUniformValue("effectColor", color);
+ }
+
+ private:
+ QColor color;
+ };
+ \endcode
+
+ The setUniforms() function is called when the effect is about
+ to be used for drawing to give the subclass the opportunity to
+ set effect-specific uniform variables.
+
+ QGraphicsShaderEffect is only supported when the GL2 paint engine
+ is in use. When any other paint engine is in use (GL1, raster, etc),
+ the drawItem() method will draw its item argument directly with
+ no effect applied.
+
+ \sa QGraphicsEffect
+*/
+
+static const char qglslDefaultImageFragmentShader[] = "\
+ lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords) { \
+ return texture2D(imageTexture, textureCoords); \
+ }\n";
+
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+
+class QGLCustomShaderEffectStage : public QGLCustomShaderStage
+{
+public:
+ QGLCustomShaderEffectStage
+ (QGraphicsShaderEffect *e, const QByteArray& source)
+ : QGLCustomShaderStage(),
+ effect(e)
+ {
+ setSource(source);
+ }
+
+ void setUniforms(QGLShaderProgram *program);
+
+ QGraphicsShaderEffect *effect;
+};
+
+void QGLCustomShaderEffectStage::setUniforms(QGLShaderProgram *program)
+{
+ effect->setUniforms(program);
+}
+
+#endif
+
+class QGraphicsShaderEffectPrivate : public QGraphicsEffectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsShaderEffect)
+public:
+ QGraphicsShaderEffectPrivate()
+ : pixelShaderFragment(qglslDefaultImageFragmentShader)
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+ , customShaderStage(0)
+#endif
+ {
+ }
+
+ QByteArray pixelShaderFragment;
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+ QGLCustomShaderEffectStage *customShaderStage;
+#endif
+};
+
+/*#
+ Constructs a shader effect and attaches it to \a parent.
+*/
+QGraphicsShaderEffect::QGraphicsShaderEffect(QObject *parent)
+ : QGraphicsEffect(*new QGraphicsShaderEffectPrivate(), parent)
+{
+}
+
+/*#
+ Destroys this shader effect.
+*/
+QGraphicsShaderEffect::~QGraphicsShaderEffect()
+{
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+ Q_D(QGraphicsShaderEffect);
+ delete d->customShaderStage;
+#endif
+}
+
+/*#
+ Returns the source code for the pixel shader fragment for
+ this shader effect. The default is a shader that copies
+ its incoming pixmap directly to the output with no effect
+ applied.
+
+ \sa setPixelShaderFragment()
+*/
+QByteArray QGraphicsShaderEffect::pixelShaderFragment() const
+{
+ Q_D(const QGraphicsShaderEffect);
+ return d->pixelShaderFragment;
+}
+
+/*#
+ Sets the source code for the pixel shader fragment for
+ this shader effect to \a code.
+
+ The \a code must define a GLSL function with the signature
+ \c{lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords)}
+ that returns the source pixel value to use in the paint engine's
+ shader program. The following is the default pixel shader fragment,
+ which draws a pixmap with no effect applied:
+
+ \code
+ lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords) {
+ return texture2D(imageTexture, textureCoords);
+ }
+ \endcode
+
+ \sa pixelShaderFragment(), setUniforms()
+*/
+void QGraphicsShaderEffect::setPixelShaderFragment(const QByteArray& code)
+{
+ Q_D(QGraphicsShaderEffect);
+ if (d->pixelShaderFragment != code) {
+ d->pixelShaderFragment = code;
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+ delete d->customShaderStage;
+ d->customShaderStage = 0;
+#endif
+ }
+}
+
+/*#
+ \reimp
+*/
+void QGraphicsShaderEffect::draw(QPainter *painter)
+{
+ Q_D(QGraphicsShaderEffect);
+
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+ // Set the custom shader on the paint engine. The setOnPainter()
+ // call may fail if the paint engine is not GL2. In that case,
+ // we fall through to drawing the pixmap normally.
+ if (!d->customShaderStage) {
+ d->customShaderStage = new QGLCustomShaderEffectStage
+ (this, d->pixelShaderFragment);
+ }
+ bool usingShader = d->customShaderStage->setOnPainter(painter);
+
+ QPoint offset;
+ if (sourceIsPixmap()) {
+ // No point in drawing in device coordinates (pixmap will be scaled anyways).
+ const QPixmap pixmap = sourcePixmap(Qt::LogicalCoordinates, &offset);
+ painter->drawPixmap(offset, pixmap);
+ } else {
+ // Draw pixmap in device coordinates to avoid pixmap scaling.
+ const QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset);
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ painter->drawPixmap(offset, pixmap);
+ painter->setWorldTransform(restoreTransform);
+ }
+
+ // Remove the custom shader to return to normal painting operations.
+ if (usingShader)
+ d->customShaderStage->removeFromPainter(painter);
+#else
+ drawSource(painter);
+#endif
+}
+
+/*#
+ Sets the custom uniform variables on this shader effect to
+ be dirty. The setUniforms() function will be called the next
+ time the shader program corresponding to this effect is used.
+
+ This function is typically called by subclasses when an
+ effect-specific parameter is changed by the application.
+
+ \sa setUniforms()
+*/
+void QGraphicsShaderEffect::setUniformsDirty()
+{
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+ Q_D(QGraphicsShaderEffect);
+ if (d->customShaderStage)
+ d->customShaderStage->setUniformsDirty();
+#endif
+}
+
+/*#
+ Sets custom uniform variables on the current GL context when
+ \a program is about to be used by the paint engine.
+
+ This function should be overridden if the shader set with
+ setPixelShaderFragment() has additional parameters beyond
+ those that the paint engine normally sets itself.
+
+ \sa setUniformsDirty()
+*/
+void QGraphicsShaderEffect::setUniforms(QGLShaderProgram *program)
+{
+ Q_UNUSED(program);
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgraphicsshadereffect_p.h b/src/opengl/qgraphicsshadereffect_p.h
new file mode 100644
index 0000000000..3b319fdbf0
--- /dev/null
+++ b/src/opengl/qgraphicsshadereffect_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSHADEREFFECT_P_H
+#define QGRAPHICSSHADEREFFECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qgraphicseffect.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLShaderProgram;
+class QGLCustomShaderEffectStage;
+class QGraphicsShaderEffectPrivate;
+
+class Q_OPENGL_EXPORT QGraphicsShaderEffect : public QGraphicsEffect
+{
+ Q_OBJECT
+public:
+ QGraphicsShaderEffect(QObject *parent = 0);
+ virtual ~QGraphicsShaderEffect();
+
+ QByteArray pixelShaderFragment() const;
+ void setPixelShaderFragment(const QByteArray& code);
+
+protected:
+ void draw(QPainter *painter);
+ void setUniformsDirty();
+ virtual void setUniforms(QGLShaderProgram *program);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsShaderEffect)
+ Q_DISABLE_COPY(QGraphicsShaderEffect)
+
+ friend class QGLCustomShaderEffectStage;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSSHADEREFFECT_P_H
diff --git a/src/opengl/qgraphicssystem_gl.cpp b/src/opengl/qgraphicssystem_gl.cpp
new file mode 100644
index 0000000000..0aa3c2e6c7
--- /dev/null
+++ b/src/opengl/qgraphicssystem_gl.cpp
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicssystem_gl_p.h"
+#include <QGraphicsView>
+
+#include "private/qpixmap_raster_p.h"
+#include "private/qpixmapdata_gl_p.h"
+#include "private/qwindowsurface_gl_p.h"
+#include "private/qgl_p.h"
+#include <private/qwindowsurface_raster_p.h>
+
+#if defined(Q_WS_X11) && !defined(QT_NO_EGL)
+#include "private/qpixmapdata_x11gl_p.h"
+#include "private/qwindowsurface_x11gl_p.h"
+#endif
+
+#if defined(Q_OS_SYMBIAN)
+#include <QtGui/private/qapplication_p.h>
+#endif
+
+#ifdef QGL_USE_TEXTURE_POOL
+#include "private/qgltexturepool_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern QGLWidget *qt_gl_getShareWidget();
+
+QPixmapData *QGLGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const
+{
+ return new QGLPixmapData(type);
+}
+
+QWindowSurface *QGLGraphicsSystem::createWindowSurface(QWidget *widget) const
+{
+#ifdef Q_WS_WIN
+ // On Windows the QGLWindowSurface class can't handle
+ // drop shadows and native effects, e.g. fading a menu in/out using
+ // top level window opacity.
+ if (widget->windowType() == Qt::Popup)
+ return new QRasterWindowSurface(widget);
+#endif
+
+#if defined(Q_WS_X11) && !defined(QT_NO_EGL)
+ if (m_useX11GL && QX11GLPixmapData::hasX11GLPixmaps()) {
+ // If the widget is a QGraphicsView which will be re-drawing the entire
+ // scene each frame anyway, we should use QGLWindowSurface as this may
+ // provide proper buffer flipping, which should be faster than QX11GL's
+ // blitting approach:
+ QGraphicsView* qgv = qobject_cast<QGraphicsView*>(widget);
+ if (qgv && qgv->viewportUpdateMode() == QGraphicsView::FullViewportUpdate)
+ return new QGLWindowSurface(widget);
+ else
+ return new QX11GLWindowSurface(widget);
+ }
+#endif
+
+#if defined(Q_OS_SYMBIAN)
+ if (!QApplicationPrivate::instance()->useTranslucentEGLSurfaces) {
+ QWidgetPrivate *d = qt_widget_private(widget);
+ if (!d->isOpaque && widget->testAttribute(Qt::WA_TranslucentBackground))
+ return d->createDefaultWindowSurface_sys();
+ }
+#endif
+
+ return new QGLWindowSurface(widget);
+}
+#ifdef QGL_USE_TEXTURE_POOL
+void QGLGraphicsSystem::releaseCachedResources()
+{
+ QGLTexturePool::instance()->hibernate();
+}
+#endif
+QT_END_NAMESPACE
+
diff --git a/src/opengl/qgraphicssystem_gl_p.h b/src/opengl/qgraphicssystem_gl_p.h
new file mode 100644
index 0000000000..5829dccea9
--- /dev/null
+++ b/src/opengl/qgraphicssystem_gl_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEM_RASTER_P_H
+#define QGRAPHICSSYSTEM_RASTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qgraphicssystem_p.h"
+
+#include <QMap>
+
+QT_BEGIN_NAMESPACE
+
+class Q_OPENGL_EXPORT QGLGraphicsSystem : public QGraphicsSystem
+{
+public:
+ QGLGraphicsSystem(bool useX11GL);
+
+ QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
+ QWindowSurface *createWindowSurface(QWidget *widget) const;
+
+#ifdef QGL_USE_TEXTURE_POOL
+ void releaseCachedResources();
+#endif
+private:
+ bool m_useX11GL;
+};
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/opengl/qpaintengine_opengl.cpp b/src/opengl/qpaintengine_opengl.cpp
new file mode 100644
index 0000000000..9da811a9d0
--- /dev/null
+++ b/src/opengl/qpaintengine_opengl.cpp
@@ -0,0 +1,5680 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+#include <private/qfontengine_p.h>
+#include <qmath.h>
+#include <private/qmath_p.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qpaintengine_p.h>
+#include "qapplication.h"
+#include "qbrush.h"
+#include "qgl.h"
+#include <private/qgl_p.h>
+#include <private/qglpaintdevice_p.h>
+#include <private/qpainter_p.h>
+#include "qmap.h"
+#include <private/qpaintengine_opengl_p.h>
+#include <private/qdatabuffer_p.h>
+#include "qpen.h"
+#include "qvarlengtharray.h"
+#include <private/qpainter_p.h>
+#include <private/qglpixelbuffer_p.h>
+#include <private/qbezier_p.h>
+#include <qglframebufferobject.h>
+#include <private/qstatictext_p.h>
+
+#include "private/qtessellator_p.h"
+
+#include "util/fragmentprograms_p.h"
+
+#ifdef Q_WS_QWS
+#include "private/qglwindowsurface_qws_p.h"
+#include "qwsmanager_qws.h"
+#include "private/qwsmanager_p.h"
+#endif
+
+#define QGL_FUNC_CONTEXT QGLContext *ctx = const_cast<QGLContext *>(device->context());
+
+#include <stdlib.h>
+#include "qpaintengine_opengl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert); //in qbrush.cpp
+#ifdef QT_MAC_USE_COCOA
+extern void *qt_current_nsopengl_context(); // qgl_mac.mm
+#endif
+
+#define QREAL_MAX 9e100
+#define QREAL_MIN -9e100
+
+extern int qt_next_power_of_two(int v);
+
+#define DISABLE_DEBUG_ONCE
+
+//#define DEBUG_DISPLAY_MASK_TEXTURE
+
+#ifdef DISABLE_DEBUG_ONCE
+#define DEBUG_OVERRIDE(state) ;
+#define DEBUG_ONCE_STR(str) ;
+#define DEBUG_ONCE if (0)
+#else
+static int DEBUG_OVERRIDE_FLAG = 0;
+static bool DEBUG_TEMP_FLAG;
+#define DEBUG_OVERRIDE(state) { state ? ++DEBUG_OVERRIDE_FLAG : --DEBUG_OVERRIDE_FLAG; }
+#define DEBUG_ONCE if ((DEBUG_TEMP_FLAG = DEBUG_OVERRIDE_FLAG) && 0) ; else for (static int DEBUG_ONCE_FLAG = false; !DEBUG_ONCE_FLAG || DEBUG_TEMP_FLAG; DEBUG_ONCE_FLAG = true, DEBUG_TEMP_FLAG = false)
+#define DEBUG_ONCE_STR(str) DEBUG_ONCE qDebug() << (str);
+#endif
+
+#ifdef Q_WS_X11
+static bool qt_nvidiaFboNeedsFinish = false;
+#endif
+
+static inline void qt_glColor4ubv(unsigned char *col)
+{
+ glColor4f(col[0]/255.0f, col[1]/255.0f, col[2]/255.0f, col[3]/255.0f);
+}
+
+struct QT_PointF {
+ qreal x;
+ qreal y;
+};
+
+struct QGLTrapezoid
+{
+ QGLTrapezoid()
+ {}
+
+ QGLTrapezoid(qreal top_, qreal bottom_, qreal topLeftX_, qreal topRightX_, qreal bottomLeftX_, qreal bottomRightX_)
+ : top(top_),
+ bottom(bottom_),
+ topLeftX(topLeftX_),
+ topRightX(topRightX_),
+ bottomLeftX(bottomLeftX_),
+ bottomRightX(bottomRightX_)
+ {}
+
+ const QGLTrapezoid translated(const QPointF &delta) const;
+
+ qreal top;
+ qreal bottom;
+ qreal topLeftX;
+ qreal topRightX;
+ qreal bottomLeftX;
+ qreal bottomRightX;
+};
+
+const QGLTrapezoid QGLTrapezoid::translated(const QPointF &delta) const
+{
+ QGLTrapezoid trap(*this);
+ trap.top += delta.y();
+ trap.bottom += delta.y();
+ trap.topLeftX += delta.x();
+ trap.topRightX += delta.x();
+ trap.bottomLeftX += delta.x();
+ trap.bottomRightX += delta.x();
+ return trap;
+}
+
+
+class QOpenGLImmediateModeTessellator;
+class QGLMaskGenerator;
+class QGLOffscreen;
+
+class QGLMaskTextureCache
+{
+public:
+ void setOffscreenSize(const QSize &offscreenSize);
+ void setDrawableSize(const QSize &drawableSize);
+
+ struct CacheLocation {
+ QRect rect;
+ int channel;
+
+ QRect screen_rect;
+ };
+
+ struct CacheInfo {
+ inline CacheInfo(const QPainterPath &p, const QTransform &m, qreal w = -1) :
+ path(p), matrix(m), stroke_width(w), age(0) {}
+
+ QPainterPath path;
+ QTransform matrix;
+ qreal stroke_width;
+
+ CacheLocation loc;
+
+ int age;
+ };
+
+ struct QuadTreeNode {
+ quint64 key;
+
+ int largest_available_block;
+ int largest_used_block;
+ };
+
+ CacheLocation getMask(QGLMaskGenerator &maskGenerator, QOpenGLPaintEnginePrivate *engine);
+
+ typedef QMultiHash<quint64, CacheInfo> QGLTextureCacheHash;
+
+ enum {block_size = 64};
+
+ // throw out keys that are too old
+ void maintainCache();
+ void clearCache();
+
+private:
+ quint64 hash(const QPainterPath &p, const QTransform &m, qreal w);
+
+ void createMask(quint64 key, CacheInfo &info, QGLMaskGenerator &maskGenerator);
+
+ QSize offscreenSize;
+ QSize drawableSize;
+
+ QGLTextureCacheHash cache;
+
+ QVector<QuadTreeNode> occupied_quadtree[4];
+
+ void quadtreeUpdate(int channel, int node, int current_block_size);
+ void quadtreeAllocate(quint64 key, const QSize &size, QRect *rect, int *channel);
+
+ bool quadtreeFindAvailableLocation(const QSize &size, QRect *rect, int *channel);
+ void quadtreeFindExistingLocation(const QSize &size, QRect *rect, int *channel);
+
+ void quadtreeInsert(int channel, quint64 key, const QRect &rect, int node = 0);
+ void quadtreeClear(int channel, const QRect &rect, int node = 0);
+
+ int quadtreeBlocksize(int node);
+ QPoint quadtreeLocation(int node);
+
+ QOpenGLPaintEnginePrivate *engine;
+};
+
+Q_GLOBAL_STATIC(QGLMaskTextureCache, qt_mask_texture_cache)
+
+class QGLOffscreen : public QObject
+{
+ Q_OBJECT
+public:
+ QGLOffscreen()
+ : QObject(),
+ offscreen(0),
+ ctx(0),
+ mask_dim(0),
+ activated(false),
+ bound(false)
+ {
+ connect(QGLSignalProxy::instance(),
+ SIGNAL(aboutToDestroyContext(const QGLContext*)),
+ SLOT(cleanupGLContextRefs(const QGLContext*)));
+ }
+
+ inline void setDevice(QPaintDevice *pdev);
+
+ void begin();
+ void end();
+
+ inline void bind();
+ inline void release();
+
+ inline bool isBound() const;
+
+ inline QSize drawableSize() const;
+ inline QSize offscreenSize() const;
+
+ inline GLuint offscreenTexture() const;
+
+ QGLContext *context() const;
+
+ static bool isSupported();
+
+ inline void initialize();
+
+ inline bool isValid() const;
+
+public Q_SLOTS:
+ void cleanupGLContextRefs(const QGLContext *context) {
+ if (context == ctx) {
+ delete offscreen;
+ ctx = 0;
+ offscreen = 0;
+ mask_dim = 0;
+ }
+ }
+
+private:
+ QGLPaintDevice* device;
+
+ QGLFramebufferObject *offscreen;
+ QGLContext *ctx;
+
+ // dimensions of mask texture (square)
+ int mask_dim;
+ QSize last_failed_size;
+
+ bool drawable_fbo;
+
+ bool activated;
+ bool initialized;
+
+ bool bound;
+};
+
+inline void QGLOffscreen::setDevice(QPaintDevice *pdev)
+{
+ if (pdev->devType() == QInternal::OpenGL)
+ device = static_cast<QGLPaintDevice*>(pdev);
+ else
+ device = QGLPaintDevice::getDevice(pdev);
+
+ if (!device)
+ return;
+
+ drawable_fbo = (pdev->devType() == QInternal::FramebufferObject);
+}
+
+void QGLOffscreen::begin()
+{
+#ifndef QT_OPENGL_ES
+ initialized = false;
+
+ if (activated)
+ initialize();
+#endif
+}
+
+void QGLOffscreen::initialize()
+{
+#ifndef QT_OPENGL_ES
+ if (initialized)
+ return;
+
+ activated = true;
+ initialized = true;
+
+ int dim = qMax(2048, static_cast<int>(qt_next_power_of_two(qMax(device->size().width(), device->size().height()))));
+
+ bool shared_context = QGLContext::areSharing(device->context(), ctx);
+ bool would_fail = last_failed_size.isValid() &&
+ (device->size().width() >= last_failed_size.width() ||
+ device->size().height() >= last_failed_size.height());
+ bool needs_refresh = dim > mask_dim || !shared_context;
+
+ if (needs_refresh && !would_fail) {
+ DEBUG_ONCE qDebug() << "QGLOffscreen::initialize(): creating offscreen of size" << dim;
+ delete offscreen;
+ offscreen = new QGLFramebufferObject(dim, dim, GLenum(GL_TEXTURE_2D));
+ mask_dim = dim;
+
+ if (!offscreen->isValid()) {
+ qWarning("QGLOffscreen: Invalid offscreen fbo (size %dx%d)", mask_dim, mask_dim);
+ delete offscreen;
+ offscreen = 0;
+ mask_dim = 0;
+ last_failed_size = device->size();
+ }
+ }
+
+ qt_mask_texture_cache()->setOffscreenSize(offscreenSize());
+ qt_mask_texture_cache()->setDrawableSize(device->size());
+ ctx = device->context();
+#endif
+}
+
+inline bool QGLOffscreen::isValid() const
+{
+ return offscreen;
+}
+
+void QGLOffscreen::end()
+{
+ if (bound)
+ release();
+#ifdef DEBUG_DISPLAY_MASK_TEXTURE
+ glReadBuffer(GL_BACK);
+ glDrawBuffer(GL_BACK);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glColor4f(1, 1, 1, 1);
+ glDisable(GL_DEPTH_TEST);
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, offscreen->texture());
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.0, 1.0); glVertex2f(0.0, 0.0);
+ glTexCoord2f(1.0, 1.0); glVertex2f(drawable.size().width(), 0.0);
+ glTexCoord2f(1.0, 0.0); glVertex2f(drawable.size().width(), drawable.size().height());
+ glTexCoord2f(0.0, 0.0); glVertex2f(0.0, drawable.size().height());
+ glEnd();
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+#endif
+}
+
+inline void QGLOffscreen::bind()
+{
+#ifndef QT_OPENGL_ES
+ Q_ASSERT(initialized);
+
+ if (!offscreen || bound)
+ return;
+
+ DEBUG_ONCE qDebug() << "QGLOffscreen: binding offscreen";
+ offscreen->bind();
+
+ bound = true;
+
+ glViewport(0, 0, offscreenSize().width(), offscreenSize().height());
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, offscreenSize().width(), offscreenSize().height(), 0, -999999, 999999);
+ glMatrixMode(GL_MODELVIEW);
+#endif
+}
+
+inline void QGLOffscreen::release()
+{
+#ifndef QT_OPENGL_ES
+ if (!offscreen || !bound)
+ return;
+
+#ifdef Q_WS_X11
+ // workaround for bug in nvidia driver versions 9x.xx
+ if (qt_nvidiaFboNeedsFinish)
+ glFinish();
+#endif
+
+ DEBUG_ONCE_STR("QGLOffscreen: releasing offscreen");
+
+ if (drawable_fbo)
+ device->ensureActiveTarget(); //###
+ else
+ offscreen->release();
+
+ QSize sz(device->size());
+ glViewport(0, 0, sz.width(), sz.height());
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+#ifndef QT_OPENGL_ES
+ glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
+#else
+ glOrthof(0, sz.width(), sz.height(), 0, -999999, 999999);
+#endif
+ glMatrixMode(GL_MODELVIEW);
+
+ bound = false;
+#endif
+}
+
+inline bool QGLOffscreen::isBound() const
+{
+ return bound;
+}
+
+inline QSize QGLOffscreen::drawableSize() const
+{
+ return device->size();
+}
+
+inline QSize QGLOffscreen::offscreenSize() const
+{
+ return QSize(mask_dim, mask_dim);
+}
+
+inline GLuint QGLOffscreen::offscreenTexture() const
+{
+ return offscreen ? offscreen->texture() : 0;
+}
+
+inline QGLContext *QGLOffscreen::context() const
+{
+ return ctx;
+}
+
+bool QGLOffscreen::isSupported()
+{
+ return (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject); // for fbo
+}
+
+struct QDrawQueueItem
+{
+ QDrawQueueItem(qreal _opacity,
+ QBrush _brush,
+ const QPointF &_brush_origion,
+ QPainter::CompositionMode _composition_mode,
+ const QTransform &_matrix,
+ QGLMaskTextureCache::CacheLocation _location)
+ : opacity(_opacity),
+ brush(_brush),
+ brush_origin(_brush_origion),
+ composition_mode(_composition_mode),
+ matrix(_matrix),
+ location(_location) {}
+ qreal opacity;
+ QBrush brush;
+ QPointF brush_origin;
+ QPainter::CompositionMode composition_mode;
+
+ QTransform matrix;
+ QGLMaskTextureCache::CacheLocation location;
+};
+
+////////// GL program cache: start
+
+struct GLProgram {
+ int brush; // brush index or mask index
+ int mode; // composition mode index
+ bool mask;
+ GLuint program;
+};
+
+typedef QMultiHash<const QGLContext *, GLProgram> QGLProgramHash;
+
+class QGLProgramCache : public QObject
+{
+ Q_OBJECT
+public:
+ QGLProgramCache() {
+ // we have to know when a context is deleted so we can free
+ // any program handles it holds
+ connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)),
+ SLOT(cleanupPrograms(const QGLContext*)));
+
+ }
+ ~QGLProgramCache() {
+ // at this point the cache should contain 0 elements
+ // Q_ASSERT(program.size() == 0);
+ }
+
+ GLuint getProgram(const QGLContext *ctx, int brush, int mode, bool mask_mode)
+ {
+ // 1. see if we have an entry for the ctx context
+ QList<GLProgram> progs = programs.values(ctx);
+ for (int i=0; i<progs.size(); ++i) {
+ const GLProgram &prg = progs.at(i);
+ if (mask_mode) {
+ if (prg.mask && prg.brush == brush)
+ return prg.program;
+ } else {
+ if (!prg.mask && prg.brush == brush && prg.mode == mode)
+ return prg.program;
+ }
+ }
+
+ // 2. try to find a match in a shared context, and update the
+ // hash with the entry found
+ QList<const QGLContext *> contexts = programs.uniqueKeys();
+ for (int i=0; i<contexts.size(); ++i) {
+ const QGLContext *cx = contexts.at(i);
+ if (cx != ctx && QGLContext::areSharing(cx, ctx)) {
+ QList<GLProgram> progs = programs.values(cx);
+ for (int k=0; k<progs.size(); ++k) {
+ const GLProgram &prg = progs.at(k);
+ if (mask_mode) {
+ if (prg.mask && prg.brush == brush) {
+ programs.insert(ctx, prg);
+ return prg.program;
+ }
+ } else {
+ if (!prg.mask && prg.brush == brush && prg.mode == mode) {
+ programs.insert(ctx, prg);
+ return prg.program;
+ }
+ }
+ }
+ }
+ }
+
+ // 3. compile a new program and place it into the cache
+ // NB! assumes ctx is the current GL context
+ GLProgram prg;
+ prg.brush = brush;
+ prg.mode = mode;
+ prg.mask = mask_mode;
+ glGenProgramsARB(1, &prg.program);
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, prg.program);
+ const char *src = mask_mode
+ ? mask_fragment_program_sources[brush]
+ : painter_fragment_program_sources[brush][mode];
+ // necessary for .NET 2002, apparently
+ const GLbyte *gl_src = reinterpret_cast<const GLbyte *>(src);
+
+ while (glGetError() != GL_NO_ERROR) {} // reset error state
+ glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,
+ int(strlen(src)), gl_src);
+ if (glGetError() != GL_NO_ERROR) {
+// qDebug() << "QGLProgramCache: Unable to compile fragment program.";
+ glDeleteProgramsARB(1, &prg.program);
+ return 0;
+ }
+
+// qDebug() << "QGLProgramCache: Creating GL program:" << prg.program << hex << ctx;
+ programs.insert(ctx, prg);
+ return prg.program;
+ }
+
+public Q_SLOTS:
+ void cleanupPrograms(const QGLContext *context)
+ {
+ QGLProgramHash::iterator it = programs.begin();
+ while (it != programs.end()) {
+ if (it.key() == context) {
+ if (!context->isSharing()) {
+ // the ctx variable below is needed for the glDeleteProgramARB call
+ // since it is resolved from our extension system
+ // NB! assumes context is the current GL context
+ const QGLContext *ctx = context;
+ // qDebug() << "QGLProgramHash: Deleting GL program:" << it.value().program << hex << it.key();
+ glDeleteProgramsARB(1, &it.value().program);
+ }
+ it = programs.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+private:
+ QGLProgramHash programs;
+};
+
+Q_GLOBAL_STATIC(QGLProgramCache, qt_gl_program_cache)
+
+////////// GL program cache: end
+
+class QOpenGLPaintEnginePrivate;
+class QGLPrivateCleanup : public QObject
+{
+ Q_OBJECT
+public:
+ QGLPrivateCleanup(QOpenGLPaintEnginePrivate *priv)
+ : p(priv)
+ {
+ connect(QGLSignalProxy::instance(),
+ SIGNAL(aboutToDestroyContext(const QGLContext*)),
+ SLOT(cleanupGLContextRefs(const QGLContext*)));
+ }
+
+public Q_SLOTS:
+ void cleanupGLContextRefs(const QGLContext *context);
+
+private:
+ QOpenGLPaintEnginePrivate *p;
+};
+
+class QOpenGLPaintEnginePrivate : public QPaintEngineExPrivate
+{
+ Q_DECLARE_PUBLIC(QOpenGLPaintEngine)
+public:
+ QOpenGLPaintEnginePrivate()
+ : opacity(1)
+ , composition_mode(QPainter::CompositionMode_SourceOver)
+ , has_fast_pen(false)
+ , use_stencil_method(false)
+ , dirty_drawable_texture(false)
+ , has_stencil_face_ext(false)
+ , use_fragment_programs(false)
+ , high_quality_antialiasing(false)
+ , use_smooth_pixmap_transform(false)
+ , use_emulation(false)
+ , txop(QTransform::TxNone)
+ , inverseScale(1)
+ , moveToCount(0)
+ , last_created_state(0)
+ , shader_ctx(0)
+ , grad_palette(0)
+ , tess_points(0)
+ , drawable_texture(0)
+ , ref_cleaner(this)
+ {}
+
+ inline void setGLPen(const QColor &c) {
+ uint alpha = qRound(c.alpha() * opacity);
+ pen_color[0] = qt_div_255(c.red() * alpha);
+ pen_color[1] = qt_div_255(c.green() * alpha);
+ pen_color[2] = qt_div_255(c.blue() * alpha);
+ pen_color[3] = alpha;
+ }
+
+ inline void setGLBrush(const QColor &c) {
+ uint alpha = qRound(c.alpha() * opacity);
+ brush_color[0] = qt_div_255(c.red() * alpha);
+ brush_color[1] = qt_div_255(c.green() * alpha);
+ brush_color[2] = qt_div_255(c.blue() * alpha);
+ brush_color[3] = alpha;
+ }
+
+ inline void setGradientOps(const QBrush &brush, const QRectF &bounds);
+ void createGradientPaletteTexture(const QGradient& g);
+
+ void updateGradient(const QBrush &brush, const QRectF &bounds);
+
+ inline void lineToStencil(qreal x, qreal y);
+ inline void curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep);
+ void pathToVertexArrays(const QPainterPath &path);
+ void fillVertexArray(Qt::FillRule fillRule);
+ void drawVertexArrays();
+ void fillPath(const QPainterPath &path);
+ void fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
+ Qt::FillRule fill);
+
+ void drawFastRect(const QRectF &rect);
+ void strokePath(const QPainterPath &path, bool use_cache);
+ void strokePathFastPen(const QPainterPath &path, bool needsResolving);
+ void strokeLines(const QPainterPath &path);
+
+ void updateDepthClip();
+ void systemStateChanged();
+
+ void cleanupGLContextRefs(const QGLContext *context) {
+ if (context == shader_ctx)
+ shader_ctx = 0;
+ }
+
+ inline void updateFastPen() {
+ qreal pen_width = cpen.widthF();
+ has_fast_pen =
+ ((pen_width == 0 || (pen_width <= 1 && matrix.type() <= QTransform::TxTranslate))
+ || cpen.isCosmetic())
+ && cpen.style() == Qt::SolidLine
+ && cpen.isSolid();
+
+ }
+
+ void disableClipping();
+ void enableClipping();
+ void ensureDrawableTexture();
+
+ QPen cpen;
+ QBrush cbrush;
+ Qt::BrushStyle brush_style;
+ QPointF brush_origin;
+ Qt::BrushStyle pen_brush_style;
+ qreal opacity;
+ QPainter::CompositionMode composition_mode;
+
+ Qt::BrushStyle current_style;
+
+ uint has_pen : 1;
+ uint has_brush : 1;
+ uint has_fast_pen : 1;
+ uint use_stencil_method : 1;
+ uint dirty_drawable_texture : 1;
+ uint has_stencil_face_ext : 1;
+ uint use_fragment_programs : 1;
+ uint high_quality_antialiasing : 1;
+ uint has_antialiasing : 1;
+ uint has_fast_composition_mode : 1;
+ uint use_smooth_pixmap_transform : 1;
+ uint use_system_clip : 1;
+ uint use_emulation : 1;
+
+ QRegion dirty_stencil;
+
+ void updateUseEmulation();
+
+ QTransform matrix;
+ GLubyte pen_color[4];
+ GLubyte brush_color[4];
+ QTransform::TransformationType txop;
+ QGLPaintDevice* device;
+ QGLOffscreen offscreen;
+
+ qreal inverseScale;
+
+ int moveToCount;
+ QPointF path_start;
+
+ bool isFastRect(const QRectF &r);
+
+ void drawImageAsPath(const QRectF &r, const QImage &img, const QRectF &sr);
+ void drawTiledImageAsPath(const QRectF &r, const QImage &img, qreal sx, qreal sy, const QPointF &offset);
+
+ void drawOffscreenPath(const QPainterPath &path);
+
+ void composite(const QRectF &rect, const QPoint &maskOffset = QPoint());
+ void composite(GLuint primitive, const GLfloat *vertexArray, int vertexCount, const QPoint &maskOffset = QPoint());
+
+ bool createFragmentPrograms();
+ void deleteFragmentPrograms();
+ void updateFragmentProgramData(int locations[]);
+
+ void cacheItemErased(int channel, const QRect &rect);
+
+ void addItem(const QGLMaskTextureCache::CacheLocation &location);
+ void drawItem(const QDrawQueueItem &item);
+ void flushDrawQueue();
+
+ void copyDrawable(const QRectF &rect);
+
+ void updateGLMatrix() const;
+
+ mutable QPainterState *last_created_state;
+
+ QGLContext *shader_ctx;
+ GLuint grad_palette;
+
+ GLuint painter_fragment_programs[num_fragment_brushes][num_fragment_composition_modes];
+ GLuint mask_fragment_programs[num_fragment_masks];
+
+ float inv_matrix_data[3][4];
+ float fmp_data[4];
+ float fmp2_m_radius2_data[4];
+ float angle_data[4];
+ float linear_data[4];
+
+ float porterduff_ab_data[4];
+ float porterduff_xyz_data[4];
+
+ float mask_offset_data[4];
+ float mask_channel_data[4];
+
+ FragmentBrushType fragment_brush;
+ FragmentCompositionModeType fragment_composition_mode;
+
+ void setPorterDuffData(float a, float b, float x, float y, float z);
+ void setInvMatrixData(const QTransform &inv_matrix);
+
+ qreal max_x;
+ qreal max_y;
+ qreal min_x;
+ qreal min_y;
+
+ QDataBuffer<QPointF> tess_points;
+ QVector<int> tess_points_stops;
+
+ GLdouble projection_matrix[4][4];
+
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
+ GLfloat mv_matrix[4][4];
+#else
+ GLdouble mv_matrix[4][4];
+#endif
+
+ QList<QDrawQueueItem> drawQueue;
+
+ GLuint drawable_texture;
+ QSize drawable_texture_size;
+
+ int max_texture_size;
+
+ QGLPrivateCleanup ref_cleaner;
+ friend class QGLMaskTextureCache;
+};
+
+class QOpenGLCoordinateOffset
+{
+public:
+ QOpenGLCoordinateOffset(QOpenGLPaintEnginePrivate *d);
+ ~QOpenGLCoordinateOffset();
+
+ static void enableOffset(QOpenGLPaintEnginePrivate *d);
+ static void disableOffset(QOpenGLPaintEnginePrivate *d);
+
+private:
+ QOpenGLPaintEnginePrivate *d;
+};
+
+QOpenGLCoordinateOffset::QOpenGLCoordinateOffset(QOpenGLPaintEnginePrivate *d_)
+ : d(d_)
+{
+ enableOffset(d);
+}
+
+void QOpenGLCoordinateOffset::enableOffset(QOpenGLPaintEnginePrivate *d)
+{
+ if (!d->has_antialiasing) {
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ d->mv_matrix[3][0] += 0.5;
+ d->mv_matrix[3][1] += 0.5;
+ d->updateGLMatrix();
+ }
+}
+
+QOpenGLCoordinateOffset::~QOpenGLCoordinateOffset()
+{
+ disableOffset(d);
+}
+
+void QOpenGLCoordinateOffset::disableOffset(QOpenGLPaintEnginePrivate *d)
+{
+ if (!d->has_antialiasing) {
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ d->mv_matrix[3][0] -= 0.5;
+ d->mv_matrix[3][1] -= 0.5;
+ }
+}
+
+void QGLPrivateCleanup::cleanupGLContextRefs(const QGLContext *context)
+{
+ p->cleanupGLContextRefs(context);
+}
+
+
+static inline void updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform)
+{
+ 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);
+}
+
+static inline QPainterPath strokeForPath(const QPainterPath &path, const QPen &cpen) {
+ QPainterPathStroker stroker;
+ if (cpen.style() == Qt::CustomDashLine)
+ stroker.setDashPattern(cpen.dashPattern());
+ else
+ stroker.setDashPattern(cpen.style());
+
+ stroker.setCapStyle(cpen.capStyle());
+ stroker.setJoinStyle(cpen.joinStyle());
+ stroker.setMiterLimit(cpen.miterLimit());
+ stroker.setDashOffset(cpen.dashOffset());
+
+ qreal width = cpen.widthF();
+ if (width == 0)
+ stroker.setWidth(1);
+ else
+ stroker.setWidth(width);
+
+ QPainterPath stroke = stroker.createStroke(path);
+ stroke.setFillRule(Qt::WindingFill);
+ return stroke;
+}
+
+class QGLStrokeCache
+{
+ struct CacheInfo
+ {
+ inline CacheInfo(QPainterPath p, QPainterPath sp, QPen stroke_pen) :
+ path(p), stroked_path(sp), pen(stroke_pen) {}
+ QPainterPath path;
+ QPainterPath stroked_path;
+ QPen pen;
+ };
+
+ typedef QMultiHash<quint64, CacheInfo> QGLStrokeTableHash;
+
+public:
+ inline QPainterPath getStrokedPath(const QPainterPath &path, const QPen &pen) {
+ quint64 hash_val = 0;
+
+ for (int i = 0; i < path.elementCount() && i <= 2; i++) {
+ hash_val += quint64(path.elementAt(i).x);
+ hash_val += quint64(path.elementAt(i).y);
+ }
+
+ QGLStrokeTableHash::const_iterator it = cache.constFind(hash_val);
+
+ if (it == cache.constEnd())
+ return addCacheElement(hash_val, path, pen);
+ else {
+ do {
+ const CacheInfo &cache_info = it.value();
+ if (cache_info.path == path && cache_info.pen == pen)
+ return cache_info.stroked_path;
+ ++it;
+ } while (it != cache.constEnd() && it.key() == hash_val);
+ // an exact match for this path was not found, create new cache element
+ return addCacheElement(hash_val, path, pen);
+ }
+ }
+
+protected:
+ inline int maxCacheSize() const { return 500; }
+ QPainterPath addCacheElement(quint64 hash_val, QPainterPath path, const QPen &pen) {
+ if (cache.size() == maxCacheSize()) {
+ int elem_to_remove = qrand() % maxCacheSize();
+ cache.remove(cache.keys()[elem_to_remove]); // may remove more than 1, but OK
+ }
+ QPainterPath stroke = strokeForPath(path, pen);
+ CacheInfo cache_entry(path, stroke, pen);
+ return cache.insert(hash_val, cache_entry).value().stroked_path;
+ }
+
+ QGLStrokeTableHash cache;
+};
+
+Q_GLOBAL_STATIC(QGLStrokeCache, qt_opengl_stroke_cache)
+
+class QGLGradientCache : public QObject
+{
+ Q_OBJECT
+ 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> QGLGradientColorTableHash;
+
+public:
+ QGLGradientCache() : QObject(), buffer_ctx(0)
+ {
+ connect(QGLSignalProxy::instance(),
+ SIGNAL(aboutToDestroyContext(const QGLContext*)),
+ SLOT(cleanupGLContextRefs(const QGLContext*)));
+ }
+
+ inline GLuint getBuffer(const QGradient &gradient, qreal opacity, QGLContext *ctx) {
+ if (buffer_ctx && !QGLContext::areSharing(buffer_ctx, ctx))
+ cleanCache();
+
+ buffer_ctx = ctx;
+
+ quint64 hash_val = 0;
+
+ QGradientStops stops = gradient.stops();
+ for (int i = 0; i < stops.size() && i <= 2; i++)
+ hash_val += stops[i].second.rgba();
+
+ QGLGradientColorTableHash::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);
+ }
+ }
+
+ inline int paletteSize() const { return 1024; }
+
+protected:
+ inline int maxCacheSize() const { return 60; }
+ inline void generateGradientColorTable(const QGradient& g,
+ uint *colorTable,
+ int size, qreal opacity) const;
+ GLuint 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:
+ QGLGradientColorTableHash::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);
+#ifndef QT_OPENGL_ES
+ glBindTexture(GL_TEXTURE_1D, cache_entry.texId);
+ glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, paletteSize(),
+ 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer);
+#else
+ // create 2D one-line texture instead. This requires an impl of manual GL_TEXGEN for all primitives
+#endif
+ return cache.insert(hash_val, cache_entry).value().texId;
+ }
+
+ void cleanCache() {
+ QGLShareContextScope scope(buffer_ctx);
+ QGLGradientColorTableHash::const_iterator it = cache.constBegin();
+ for (; it != cache.constEnd(); ++it) {
+ const CacheInfo &cache_info = it.value();
+ glDeleteTextures(1, &cache_info.texId);
+ }
+ cache.clear();
+ }
+
+ QGLGradientColorTableHash cache;
+
+ QGLContext *buffer_ctx;
+
+public Q_SLOTS:
+ void cleanupGLContextRefs(const QGLContext *context) {
+ if (context == buffer_ctx) {
+ cleanCache();
+ buffer_ctx = 0;
+ }
+ }
+};
+
+static inline uint endianColor(uint c)
+{
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ return c;
+#else
+ return ( (c << 24) & 0xff000000)
+ | ((c >> 24) & 0x000000ff)
+ | ((c << 8) & 0x00ff0000)
+ | ((c >> 8) & 0x0000ff00);
+#endif // Q_BYTE_ORDER
+}
+
+void QGLGradientCache::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();
+
+ 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++] = endianColor(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] = endianColor(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
+ else
+ colorTable[pos] = endianColor(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 = endianColor(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;
+}
+
+#ifndef Q_WS_QWS
+Q_GLOBAL_STATIC(QGLGradientCache, qt_opengl_gradient_cache)
+#endif
+
+void QOpenGLPaintEnginePrivate::createGradientPaletteTexture(const QGradient& g)
+{
+#ifdef QT_OPENGL_ES //###
+ Q_UNUSED(g);
+#else
+ GLuint texId = qt_opengl_gradient_cache()->getBuffer(g, opacity, device->context());
+ glBindTexture(GL_TEXTURE_1D, texId);
+ grad_palette = texId;
+ if (g.spread() == QGradient::RepeatSpread || g.type() == QGradient::ConicalGradient)
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ else if (g.spread() == QGradient::ReflectSpread)
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT_IBM);
+ else
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+#endif
+}
+
+
+inline void QOpenGLPaintEnginePrivate::setGradientOps(const QBrush &brush, const QRectF &bounds)
+{
+ current_style = brush.style();
+
+ if (current_style < Qt::LinearGradientPattern || current_style > Qt::ConicalGradientPattern) {
+ setGLBrush(brush.color());
+ qt_glColor4ubv(brush_color);
+ }
+
+ updateGradient(brush, bounds);
+
+#ifndef QT_OPENGL_ES //### GLES does not have GL_TEXTURE_GEN_ so we are falling back for gradients
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_1D);
+
+ if (current_style == Qt::LinearGradientPattern) {
+ if (high_quality_antialiasing || !has_fast_composition_mode) {
+ fragment_brush = FRAGMENT_PROGRAM_BRUSH_LINEAR;
+ } else {
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_1D);
+ }
+ } else {
+ if (use_fragment_programs) {
+ if (current_style == Qt::RadialGradientPattern)
+ fragment_brush = FRAGMENT_PROGRAM_BRUSH_RADIAL;
+ else if (current_style == Qt::ConicalGradientPattern)
+ fragment_brush = FRAGMENT_PROGRAM_BRUSH_CONICAL;
+ else if (current_style == Qt::SolidPattern)
+ fragment_brush = FRAGMENT_PROGRAM_BRUSH_SOLID;
+ else if (current_style == Qt::TexturePattern && !brush.texture().isQBitmap())
+ fragment_brush = FRAGMENT_PROGRAM_BRUSH_TEXTURE;
+ else
+ fragment_brush = FRAGMENT_PROGRAM_BRUSH_PATTERN;
+ }
+ }
+#endif
+}
+
+QOpenGLPaintEngine::QOpenGLPaintEngine()
+ : QPaintEngineEx(*(new QOpenGLPaintEnginePrivate))
+{
+}
+
+QOpenGLPaintEngine::~QOpenGLPaintEngine()
+{
+}
+
+bool QOpenGLPaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (pdev->devType() == QInternal::OpenGL)
+ d->device = static_cast<QGLPaintDevice*>(pdev);
+ else
+ d->device = QGLPaintDevice::getDevice(pdev);
+
+ if (!d->device)
+ return false;
+
+ d->offscreen.setDevice(pdev);
+ d->has_fast_pen = false;
+ d->inverseScale = 1;
+ d->opacity = 1;
+ d->device->beginPaint();
+ d->matrix = QTransform();
+ d->has_antialiasing = false;
+ d->high_quality_antialiasing = false;
+
+ QSize sz(d->device->size());
+ d->dirty_stencil = QRect(0, 0, sz.width(), sz.height());
+
+ d->use_emulation = false;
+
+ for (int i = 0; i < 4; ++i)
+ for (int j = 0; j < 4; ++j)
+ d->mv_matrix[i][j] = (i == j ? qreal(1) : qreal(0));
+
+ bool has_frag_program = (QGLExtensions::glExtensions() & QGLExtensions::FragmentProgram)
+ && (pdev->devType() != QInternal::Pixmap);
+
+ QGLContext *ctx = const_cast<QGLContext *>(d->device->context());
+ if (!ctx) {
+ qWarning() << "QOpenGLPaintEngine: paint device doesn't have a valid GL context.";
+ return false;
+ }
+
+ if (has_frag_program)
+ has_frag_program = qt_resolve_frag_program_extensions(ctx) && qt_resolve_version_1_3_functions(ctx);
+
+ d->use_stencil_method = d->device->format().stencil()
+ && (QGLExtensions::glExtensions() & QGLExtensions::StencilWrap);
+ if (d->device->format().directRendering()
+ && (d->use_stencil_method && QGLExtensions::glExtensions() & QGLExtensions::StencilTwoSide))
+ d->has_stencil_face_ext = qt_resolve_stencil_face_extension(ctx);
+
+#ifdef Q_WS_X11
+ static bool nvidia_workaround_needs_init = true;
+ if (nvidia_workaround_needs_init) {
+ // nvidia 9x.xx unix drivers contain a bug which requires us to
+ // call glFinish before releasing an fbo to avoid painting
+ // artifacts
+ const QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
+ const int pos = versionString.indexOf("NVIDIA");
+ if (pos >= 0) {
+ const float nvidiaDriverVersion = versionString.mid(pos + strlen("NVIDIA")).toFloat();
+ qt_nvidiaFboNeedsFinish = nvidiaDriverVersion >= 90.0 && nvidiaDriverVersion < 100.0;
+ }
+ nvidia_workaround_needs_init = false;
+ }
+#endif
+
+#ifndef QT_OPENGL_ES
+ if (!ctx->d_ptr->internal_context) {
+ glGetDoublev(GL_PROJECTION_MATRIX, &d->projection_matrix[0][0]);
+ glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
+ glPushAttrib(GL_ALL_ATTRIB_BITS);
+
+ glDisableClientState(GL_EDGE_FLAG_ARRAY);
+ glDisableClientState(GL_INDEX_ARRAY);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ glDisable(GL_TEXTURE_1D);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glPixelTransferi(GL_MAP_COLOR, false);
+ glPixelTransferi(GL_MAP_STENCIL, false);
+ glDisable(GL_TEXTURE_GEN_S);
+
+ glPixelStorei(GL_PACK_SWAP_BYTES, false);
+ glPixelStorei(GL_PACK_LSB_FIRST, false);
+ glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_PACK_SKIP_ROWS, 0);
+ glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
+ glPixelStorei(GL_PACK_ALIGNMENT, 4);
+
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, false);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, false);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+
+ if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2) {
+ glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
+ glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
+ glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
+ glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
+ }
+ }
+#endif
+
+ if (!ctx->d_ptr->internal_context) {
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ glLoadIdentity();
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
+ glDisable(GL_MULTISAMPLE);
+ glDisable(GL_TEXTURE_2D);
+ if (QGLExtensions::glExtensions() & QGLExtensions::TextureRectangle)
+ glDisable(GL_TEXTURE_RECTANGLE_NV);
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_LIGHTING);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ }
+
+ d->offscreen.begin();
+
+ glViewport(0, 0, sz.width(), sz.height()); // XXX (Embedded): We need a solution for GLWidgets that draw in a part or a bigger surface...
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+#ifdef QT_OPENGL_ES
+ glOrthof(0, sz.width(), sz.height(), 0, -999999, 999999);
+#else
+ glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
+#endif
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glEnable(GL_BLEND);
+ d->composition_mode = QPainter::CompositionMode_SourceOver;
+
+#ifdef QT_OPENGL_ES
+ d->max_texture_size = ctx->d_func()->maxTextureSize();
+#else
+ bool shared_ctx = QGLContext::areSharing(d->device->context(), d->shader_ctx);
+
+ if (shared_ctx) {
+ d->max_texture_size = d->shader_ctx->d_func()->maxTextureSize();
+ } else {
+ d->max_texture_size = ctx->d_func()->maxTextureSize();
+
+ if (d->shader_ctx) {
+ d->shader_ctx->makeCurrent();
+ glBindTexture(GL_TEXTURE_1D, 0);
+ glDeleteTextures(1, &d->grad_palette);
+
+ if (has_frag_program && d->use_fragment_programs)
+ glDeleteTextures(1, &d->drawable_texture);
+ ctx->makeCurrent();
+ }
+ d->shader_ctx = d->device->context();
+ glGenTextures(1, &d->grad_palette);
+
+ qt_mask_texture_cache()->clearCache();
+ d->use_fragment_programs = has_frag_program;
+ }
+
+ if (d->use_fragment_programs && (!shared_ctx || sz.width() > d->drawable_texture_size.width()
+ || sz.height() > d->drawable_texture_size.height()))
+ {
+ // delete old texture if size has increased, otherwise it was deleted earlier
+ if (shared_ctx)
+ glDeleteTextures(1, &d->drawable_texture);
+
+ d->dirty_drawable_texture = true;
+ d->drawable_texture_size = QSize(qt_next_power_of_two(sz.width()),
+ qt_next_power_of_two(sz.height()));
+ }
+#endif
+
+ updateClipRegion(QRegion(), Qt::NoClip);
+ penChanged();
+ brushChanged();
+ opacityChanged();
+ compositionModeChanged();
+ renderHintsChanged();
+ transformChanged();
+ return true;
+}
+
+bool QOpenGLPaintEngine::end()
+{
+ Q_D(QOpenGLPaintEngine);
+ d->flushDrawQueue();
+ d->offscreen.end();
+ QGLContext *ctx = const_cast<QGLContext *>(d->device->context());
+ if (!ctx->d_ptr->internal_context) {
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ }
+#ifndef QT_OPENGL_ES
+ if (ctx->d_ptr->internal_context) {
+ glDisable(GL_SCISSOR_TEST);
+ } else {
+ glMatrixMode(GL_PROJECTION);
+ glLoadMatrixd(&d->projection_matrix[0][0]);
+ glPopAttrib();
+ glPopClientAttrib();
+ }
+#endif
+ d->device->endPaint();
+ qt_mask_texture_cache()->maintainCache();
+
+#if defined(Q_WS_X11)
+ // clear out the references we hold for textures bound with the
+ // texture_from_pixmap extension
+ ctx->d_func()->boundPixmaps.clear();
+#endif
+ return true;
+}
+
+void QOpenGLPaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QOpenGLPaintEngine);
+ QPaintEngine::DirtyFlags flags = state.state();
+
+ bool update_fast_pen = false;
+
+ if (flags & DirtyOpacity) {
+ update_fast_pen = true;
+ d->opacity = state.opacity();
+ if (d->opacity > 1.0f)
+ d->opacity = 1.0f;
+ if (d->opacity < 0.f)
+ d->opacity = 0.f;
+ // force update
+ flags |= DirtyPen;
+ flags |= DirtyBrush;
+ }
+
+ if (flags & DirtyTransform) {
+ update_fast_pen = true;
+ updateMatrix(state.transform());
+ // brush setup depends on transform state
+ if (state.brush().style() != Qt::NoBrush)
+ flags |= DirtyBrush;
+ }
+
+ if (flags & DirtyPen) {
+ update_fast_pen = true;
+ updatePen(state.pen());
+ }
+
+ if (flags & (DirtyBrush | DirtyBrushOrigin)) {
+ updateBrush(state.brush(), state.brushOrigin());
+ }
+
+ if (flags & DirtyFont) {
+ updateFont(state.font());
+ }
+
+ if (state.state() & DirtyClipEnabled) {
+ if (state.isClipEnabled())
+ updateClipRegion(painter()->clipRegion(), Qt::ReplaceClip);
+ else
+ updateClipRegion(QRegion(), Qt::NoClip);
+ }
+
+ if (flags & DirtyClipPath) {
+ updateClipRegion(QRegion(state.clipPath().toFillPolygon().toPolygon(),
+ state.clipPath().fillRule()),
+ state.clipOperation());
+ }
+
+ if (flags & DirtyClipRegion) {
+ updateClipRegion(state.clipRegion(), state.clipOperation());
+ }
+
+ if (flags & DirtyHints) {
+ updateRenderHints(state.renderHints());
+ }
+
+ if (flags & DirtyCompositionMode) {
+ updateCompositionMode(state.compositionMode());
+ }
+
+ if (update_fast_pen) {
+ Q_D(QOpenGLPaintEngine);
+ qreal pen_width = d->cpen.widthF();
+ d->has_fast_pen =
+ ((pen_width == 0 || (pen_width <= 1 && d->txop <= QTransform::TxTranslate))
+ || d->cpen.isCosmetic())
+ && d->cpen.style() == Qt::SolidLine
+ && d->cpen.isSolid();
+ }
+}
+
+
+void QOpenGLPaintEnginePrivate::setInvMatrixData(const QTransform &inv_matrix)
+{
+ inv_matrix_data[0][0] = inv_matrix.m11();
+ inv_matrix_data[1][0] = inv_matrix.m21();
+ inv_matrix_data[2][0] = inv_matrix.m31();
+
+ inv_matrix_data[0][1] = inv_matrix.m12();
+ inv_matrix_data[1][1] = inv_matrix.m22();
+ inv_matrix_data[2][1] = inv_matrix.m32();
+
+ inv_matrix_data[0][2] = inv_matrix.m13();
+ inv_matrix_data[1][2] = inv_matrix.m23();
+ inv_matrix_data[2][2] = inv_matrix.m33();
+}
+
+
+void QOpenGLPaintEnginePrivate::updateGradient(const QBrush &brush, const QRectF &)
+{
+#ifdef QT_OPENGL_ES
+ Q_UNUSED(brush);
+#else
+ bool has_mirrored_repeat = QGLExtensions::glExtensions() & QGLExtensions::MirroredRepeat;
+ Qt::BrushStyle style = brush.style();
+
+ QTransform m = brush.transform();
+
+ if (has_mirrored_repeat && style == Qt::LinearGradientPattern) {
+ const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
+ QTransform m = brush.transform();
+ QPointF realStart = g->start();
+ QPointF realFinal = g->finalStop();
+ QPointF start = m.map(realStart);
+ QPointF stop;
+
+ if (qFuzzyCompare(m.m11(), m.m22()) && m.m12() == 0.0 && m.m21() == 0.0) {
+ // It is a simple uniform scale and/or translation
+ stop = m.map(realFinal);
+ } else {
+ // It is not enough to just transform the endpoints.
+ // We have to make sure the _pattern_ is transformed correctly.
+
+ qreal odx = realFinal.x() - realStart.x();
+ qreal ody = realFinal.y() - realStart.y();
+
+ // nx, ny and dx, dy are normal and gradient direction after transform:
+ qreal nx = m.m11()*ody - m.m21()*odx;
+ qreal ny = m.m12()*ody - m.m22()*odx;
+
+ qreal dx = m.m11()*odx + m.m21()*ody;
+ qreal dy = m.m12()*odx + m.m22()*ody;
+
+ qreal lx = 1 / (dx - dy*nx/ny);
+ qreal ly = 1 / (dy - dx*ny/nx);
+ qreal l = 1 / qSqrt(lx*lx+ly*ly);
+
+ stop = start + QPointF(-ny, nx) * l/qSqrt(nx*nx+ny*ny);
+ }
+
+ float tr[4], f;
+ tr[0] = stop.x() - start.x();
+ tr[1] = stop.y() - start.y();
+ f = 1.0 / (tr[0]*tr[0] + tr[1]*tr[1]);
+ tr[0] *= f;
+ tr[1] *= f;
+ tr[2] = 0;
+ tr[3] = -(start.x()*tr[0] + start.y()*tr[1]);
+ brush_color[0] = brush_color[1] = brush_color[2] = brush_color[3] = 255;
+ qt_glColor4ubv(brush_color);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tr);
+ }
+
+ if (use_fragment_programs) {
+ if (style == Qt::RadialGradientPattern) {
+ const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
+ QPointF realCenter = g->center();
+ QPointF realFocal = g->focalPoint();
+ qreal realRadius = g->radius();
+ QTransform translate(1, 0, 0, 1, -realFocal.x(), -realFocal.y());
+ QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
+ QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
+ QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
+
+ setInvMatrixData(inv_matrix);
+
+ fmp_data[0] = realCenter.x() - realFocal.x();
+ fmp_data[1] = realCenter.y() - realFocal.y();
+
+ fmp2_m_radius2_data[0] = -fmp_data[0] * fmp_data[0] - fmp_data[1] * fmp_data[1] + realRadius * realRadius;
+ } else if (style == Qt::ConicalGradientPattern) {
+ const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
+ QPointF realCenter = g->center();
+ QTransform translate(1, 0, 0, 1, -realCenter.x(), -realCenter.y());
+ QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
+ QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
+ QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
+
+ setInvMatrixData(inv_matrix);
+
+ angle_data[0] = -(g->angle() * 2 * Q_PI) / 360.0;
+ } else if (style == Qt::LinearGradientPattern) {
+ const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
+
+ QPointF realStart = g->start();
+ QPointF realFinal = g->finalStop();
+ QTransform translate(1, 0, 0, 1, -realStart.x(), -realStart.y());
+ QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
+ QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
+ QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
+
+ setInvMatrixData(inv_matrix);
+
+ QPointF l = realFinal - realStart;
+
+ linear_data[0] = l.x();
+ linear_data[1] = l.y();
+
+ linear_data[2] = 1.0f / (l.x() * l.x() + l.y() * l.y());
+ } else if (style != Qt::SolidPattern) {
+ QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
+ QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
+ QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted();
+
+ setInvMatrixData(inv_matrix);
+ }
+ }
+
+ if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
+ createGradientPaletteTexture(*brush.gradient());
+ }
+#endif
+}
+
+
+class QOpenGLTessellator : public QTessellator
+{
+public:
+ QOpenGLTessellator() {}
+ ~QOpenGLTessellator() { }
+ QGLTrapezoid toGLTrapezoid(const Trapezoid &trap);
+};
+
+QGLTrapezoid QOpenGLTessellator::toGLTrapezoid(const Trapezoid &trap)
+{
+ QGLTrapezoid t;
+
+ t.top = Q27Dot5ToDouble(trap.top);
+ t.bottom = Q27Dot5ToDouble(trap.bottom);
+
+ Q27Dot5 y = trap.topLeft->y - trap.bottomLeft->y;
+
+ qreal topLeftY = Q27Dot5ToDouble(trap.topLeft->y);
+
+ qreal tx = Q27Dot5ToDouble(trap.topLeft->x);
+ qreal m = (-tx + Q27Dot5ToDouble(trap.bottomLeft->x)) / Q27Dot5ToDouble(y);
+ t.topLeftX = tx + m * (topLeftY - t.top);
+ t.bottomLeftX = tx + m * (topLeftY - t.bottom);
+
+ y = trap.topRight->y - trap.bottomRight->y;
+
+ qreal topRightY = Q27Dot5ToDouble(trap.topRight->y);
+
+ tx = Q27Dot5ToDouble(trap.topRight->x);
+ m = (-tx + Q27Dot5ToDouble(trap.bottomRight->x)) / Q27Dot5ToDouble(y);
+
+ t.topRightX = tx + m * (topRightY - Q27Dot5ToDouble(trap.top));
+ t.bottomRightX = tx + m * (topRightY - Q27Dot5ToDouble(trap.bottom));
+
+ return t;
+}
+
+class QOpenGLImmediateModeTessellator : public QOpenGLTessellator
+{
+public:
+ void addTrap(const Trapezoid &trap);
+ void tessellate(const QPointF *points, int nPoints, bool winding) {
+ trapezoids.reserve(trapezoids.size() + nPoints);
+ setWinding(winding);
+ QTessellator::tessellate(points, nPoints);
+ }
+
+ QVector<QGLTrapezoid> trapezoids;
+};
+
+void QOpenGLImmediateModeTessellator::addTrap(const Trapezoid &trap)
+{
+ trapezoids.append(toGLTrapezoid(trap));
+}
+
+#ifndef QT_OPENGL_ES
+static void drawTrapezoid(const QGLTrapezoid &trap, const qreal offscreenHeight, QGLContext *ctx)
+{
+ qreal minX = qMin(trap.topLeftX, trap.bottomLeftX);
+ qreal maxX = qMax(trap.topRightX, trap.bottomRightX);
+
+ if (qFuzzyCompare(trap.top, trap.bottom) || qFuzzyCompare(minX, maxX) ||
+ (qFuzzyCompare(trap.topLeftX, trap.topRightX) && qFuzzyCompare(trap.bottomLeftX, trap.bottomRightX)))
+ return;
+
+ const qreal xpadding = 1.0;
+ const qreal ypadding = 1.0;
+
+ qreal topDist = offscreenHeight - trap.top;
+ qreal bottomDist = offscreenHeight - trap.bottom;
+
+ qreal reciprocal = bottomDist / (bottomDist - topDist);
+
+ qreal leftB = trap.bottomLeftX + (trap.topLeftX - trap.bottomLeftX) * reciprocal;
+ qreal rightB = trap.bottomRightX + (trap.topRightX - trap.bottomRightX) * reciprocal;
+
+ const bool topZero = qFuzzyIsNull(topDist);
+
+ reciprocal = topZero ? 1.0 / bottomDist : 1.0 / topDist;
+
+ qreal leftA = topZero ? (trap.bottomLeftX - leftB) * reciprocal : (trap.topLeftX - leftB) * reciprocal;
+ qreal rightA = topZero ? (trap.bottomRightX - rightB) * reciprocal : (trap.topRightX - rightB) * reciprocal;
+
+ qreal invLeftA = qFuzzyIsNull(leftA) ? 0.0 : 1.0 / leftA;
+ qreal invRightA = qFuzzyIsNull(rightA) ? 0.0 : 1.0 / rightA;
+
+ // fragment program needs the negative of invRightA as it mirrors the line
+ glTexCoord4f(topDist, bottomDist, invLeftA, -invRightA);
+ glMultiTexCoord4f(GL_TEXTURE1, leftA, leftB, rightA, rightB);
+
+ qreal topY = trap.top - ypadding;
+ qreal bottomY = trap.bottom + ypadding;
+
+ qreal bounds_bottomLeftX = leftA * (offscreenHeight - bottomY) + leftB;
+ qreal bounds_bottomRightX = rightA * (offscreenHeight - bottomY) + rightB;
+ qreal bounds_topLeftX = leftA * (offscreenHeight - topY) + leftB;
+ qreal bounds_topRightX = rightA * (offscreenHeight - topY) + rightB;
+
+ QPointF leftNormal(1, -leftA);
+ leftNormal /= qSqrt(leftNormal.x() * leftNormal.x() + leftNormal.y() * leftNormal.y());
+ QPointF rightNormal(1, -rightA);
+ rightNormal /= qSqrt(rightNormal.x() * rightNormal.x() + rightNormal.y() * rightNormal.y());
+
+ qreal left_padding = xpadding / qAbs(leftNormal.x());
+ qreal right_padding = xpadding / qAbs(rightNormal.x());
+
+ glVertex2d(bounds_topLeftX - left_padding, topY);
+ glVertex2d(bounds_topRightX + right_padding, topY);
+ glVertex2d(bounds_bottomRightX + right_padding, bottomY);
+ glVertex2d(bounds_bottomLeftX - left_padding, bottomY);
+
+ glTexCoord4f(0.0f, 0.0f, 0.0f, 1.0f);
+}
+#endif // !Q_WS_QWS
+
+class QOpenGLTrapezoidToArrayTessellator : public QOpenGLTessellator
+{
+public:
+ QOpenGLTrapezoidToArrayTessellator() : vertices(0), allocated(0), size(0) {}
+ ~QOpenGLTrapezoidToArrayTessellator() { free(vertices); }
+ GLfloat *vertices;
+ int allocated;
+ int size;
+ QRectF bounds;
+ void addTrap(const Trapezoid &trap);
+ void tessellate(const QPointF *points, int nPoints, bool winding) {
+ size = 0;
+ setWinding(winding);
+ bounds = QTessellator::tessellate(points, nPoints);
+ }
+};
+
+void QOpenGLTrapezoidToArrayTessellator::addTrap(const Trapezoid &trap)
+{
+ // On OpenGL ES we convert the trap to 2 triangles
+#ifndef QT_OPENGL_ES
+ if (size > allocated - 8) {
+#else
+ if (size > allocated - 12) {
+#endif
+ allocated = qMax(2*allocated, 512);
+ vertices = (GLfloat *)realloc(vertices, allocated * sizeof(GLfloat));
+ }
+
+ QGLTrapezoid t = toGLTrapezoid(trap);
+
+#ifndef QT_OPENGL_ES
+ vertices[size++] = t.topLeftX;
+ vertices[size++] = t.top;
+ vertices[size++] = t.topRightX;
+ vertices[size++] = t.top;
+ vertices[size++] = t.bottomRightX;
+ vertices[size++] = t.bottom;
+ vertices[size++] = t.bottomLeftX;
+ vertices[size++] = t.bottom;
+#else
+ // First triangle
+ vertices[size++] = t.topLeftX;
+ vertices[size++] = t.top;
+ vertices[size++] = t.topRightX;
+ vertices[size++] = t.top;
+ vertices[size++] = t.bottomRightX;
+ vertices[size++] = t.bottom;
+
+ // Second triangle
+ vertices[size++] = t.bottomLeftX;
+ vertices[size++] = t.bottom;
+ vertices[size++] = t.topLeftX;
+ vertices[size++] = t.top;
+ vertices[size++] = t.bottomRightX;
+ vertices[size++] = t.bottom;
+#endif
+}
+
+
+void QOpenGLPaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
+ Qt::FillRule fill)
+{
+ QOpenGLTrapezoidToArrayTessellator tessellator;
+ tessellator.tessellate(polygonPoints, pointCount, fill == Qt::WindingFill);
+
+ DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon with" << pointCount << "points using fillPolygon_dev";
+
+ setGradientOps(cbrush, tessellator.bounds);
+
+ bool fast_style = current_style == Qt::LinearGradientPattern
+ || current_style == Qt::SolidPattern;
+
+#ifndef QT_OPENGL_ES
+ GLenum geometry_mode = GL_QUADS;
+#else
+ GLenum geometry_mode = GL_TRIANGLES;
+#endif
+
+ if (use_fragment_programs && !(fast_style && has_fast_composition_mode)) {
+ composite(geometry_mode, tessellator.vertices, tessellator.size / 2);
+ } else {
+ glVertexPointer(2, GL_FLOAT, 0, tessellator.vertices);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDrawArrays(geometry_mode, 0, tessellator.size/2);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ }
+}
+
+
+inline void QOpenGLPaintEnginePrivate::lineToStencil(qreal x, qreal y)
+{
+ tess_points.add(QPointF(x, y));
+
+ if (x > max_x)
+ max_x = x;
+ else if (x < min_x)
+ min_x = x;
+ if (y > max_y)
+ max_y = y;
+ else if (y < min_y)
+ min_y = y;
+}
+
+inline void QOpenGLPaintEnginePrivate::curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep)
+{
+ qreal inverseScaleHalf = inverseScale / 2;
+
+ QBezier beziers[32];
+ beziers[0] = QBezier::fromPoints(tess_points.last(), cp1, cp2, ep);
+ QBezier *b = beziers;
+ while (b >= beziers) {
+ // check if we can pop the top bezier curve from the stack
+ qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
+ qreal d;
+ if (l > inverseScale) {
+ d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2) - (b->y4 - b->y1)*(b->x1 - b->x2) )
+ + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3) - (b->y4 - b->y1)*(b->x1 - b->x3) );
+ d /= l;
+ } else {
+ d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
+ qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
+ }
+ if (d < inverseScaleHalf || b == beziers + 31) {
+ // good enough, we pop it off and add the endpoint
+ lineToStencil(b->x4, b->y4);
+ --b;
+ } else {
+ // split, second half of the polygon goes lower into the stack
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+}
+
+
+void QOpenGLPaintEnginePrivate::pathToVertexArrays(const QPainterPath &path)
+{
+ const QPainterPath::Element &first = path.elementAt(0);
+ min_x = max_x = first.x;
+ min_y = max_y = first.y;
+
+ tess_points.reset();
+ tess_points_stops.clear();
+ lineToStencil(first.x, first.y);
+
+ for (int i=1; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ tess_points_stops.append(tess_points.size());
+ lineToStencil(e.x, e.y);
+ break;
+ case QPainterPath::LineToElement:
+ lineToStencil(e.x, e.y);
+ break;
+ case QPainterPath::CurveToElement:
+ curveToStencil(e, path.elementAt(i+1), path.elementAt(i+2));
+ i+=2;
+ break;
+ default:
+ break;
+ }
+ }
+ lineToStencil(first.x, first.y);
+ tess_points_stops.append(tess_points.size());
+}
+
+
+void QOpenGLPaintEnginePrivate::drawVertexArrays()
+{
+ if (tess_points_stops.count() == 0)
+ return;
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, GL_DOUBLE, 0, tess_points.data());
+ int previous_stop = 0;
+ foreach(int stop, tess_points_stops) {
+ glDrawArrays(GL_TRIANGLE_FAN, previous_stop, stop-previous_stop);
+ previous_stop = stop;
+ }
+ glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+void QOpenGLPaintEnginePrivate::fillVertexArray(Qt::FillRule fillRule)
+{
+ Q_Q(QOpenGLPaintEngine);
+
+ QRect rect = dirty_stencil.boundingRect();
+
+ if (use_system_clip)
+ rect = q->systemClip().intersected(dirty_stencil).boundingRect();
+
+ glStencilMask(~0);
+
+ if (!rect.isEmpty()) {
+ disableClipping();
+
+ glEnable(GL_SCISSOR_TEST);
+
+ const int left = rect.left();
+ const int width = rect.width();
+ const int bottom = device->size().height() - (rect.bottom() + 1);
+ const int height = rect.height();
+
+ glScissor(left, bottom, width, height);
+
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ dirty_stencil -= rect;
+
+ glDisable(GL_SCISSOR_TEST);
+
+ enableClipping();
+ }
+
+ // Enable stencil.
+ glEnable(GL_STENCIL_TEST);
+
+ // Disable color writes.
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+
+ GLuint stencilMask = 0;
+
+ if (fillRule == Qt::OddEvenFill) {
+ stencilMask = 1;
+
+ // Enable stencil writes.
+ glStencilMask(stencilMask);
+
+ // Set stencil xor mode.
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
+
+ // Disable stencil func.
+ glStencilFunc(GL_ALWAYS, 0, ~0);
+
+ drawVertexArrays();
+ } else if (fillRule == Qt::WindingFill) {
+ stencilMask = ~0;
+
+ if (has_stencil_face_ext) {
+ QGL_FUNC_CONTEXT;
+ glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
+
+ glActiveStencilFaceEXT(GL_BACK);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
+ glStencilFunc(GL_ALWAYS, 0, ~0);
+
+ glActiveStencilFaceEXT(GL_FRONT);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
+ glStencilFunc(GL_ALWAYS, 0, ~0);
+
+ drawVertexArrays();
+
+ glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
+ } else {
+ glStencilFunc(GL_ALWAYS, 0, ~0);
+ glEnable(GL_CULL_FACE);
+
+ glCullFace(GL_BACK);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
+ drawVertexArrays();
+
+ glCullFace(GL_FRONT);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
+ drawVertexArrays();
+
+ glDisable(GL_CULL_FACE);
+ }
+ }
+
+ // Enable color writes.
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glStencilMask(stencilMask);
+
+ setGradientOps(cbrush, QRectF(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y)));
+
+ bool fast_fill = has_fast_composition_mode && (current_style == Qt::LinearGradientPattern || current_style == Qt::SolidPattern);
+
+ if (use_fragment_programs && !fast_fill) {
+ DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (fragment programs)";
+ QRectF rect(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y));
+
+ // Enable stencil func.
+ glStencilFunc(GL_NOTEQUAL, 0, stencilMask);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ composite(rect);
+ } else {
+ DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (no fragment programs)";
+
+ // Enable stencil func.
+ glStencilFunc(GL_NOTEQUAL, 0, stencilMask);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+#ifndef QT_OPENGL_ES
+ glBegin(GL_QUADS);
+ glVertex2f(min_x, min_y);
+ glVertex2f(max_x, min_y);
+ glVertex2f(max_x, max_y);
+ glVertex2f(min_x, max_y);
+ glEnd();
+#endif
+ }
+
+ // Disable stencil writes.
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ glStencilMask(0);
+ glDisable(GL_STENCIL_TEST);
+}
+
+void QOpenGLPaintEnginePrivate::fillPath(const QPainterPath &path)
+{
+ if (path.isEmpty())
+ return;
+
+ if (use_stencil_method && !high_quality_antialiasing) {
+ pathToVertexArrays(path);
+ fillVertexArray(path.fillRule());
+ return;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ if (high_quality_antialiasing)
+ drawOffscreenPath(path);
+ else {
+ QPolygonF poly = path.toFillPolygon(matrix);
+ fillPolygon_dev(poly.data(), poly.count(),
+ path.fillRule());
+ }
+
+ updateGLMatrix();
+}
+
+
+static inline bool needsEmulation(Qt::BrushStyle style)
+{
+ return !(style == Qt::SolidPattern
+ || (style == Qt::LinearGradientPattern
+ && (QGLExtensions::glExtensions() & QGLExtensions::MirroredRepeat)));
+}
+
+void QOpenGLPaintEnginePrivate::updateUseEmulation()
+{
+ use_emulation = !use_fragment_programs
+ && ((has_pen && needsEmulation(pen_brush_style))
+ || (has_brush && needsEmulation(brush_style)));
+}
+
+void QOpenGLPaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QOpenGLPaintEngine);
+ Qt::PenStyle pen_style = pen.style();
+ d->pen_brush_style = pen.brush().style();
+ d->cpen = pen;
+ d->has_pen = (pen_style != Qt::NoPen) && (d->pen_brush_style != Qt::NoBrush);
+ d->updateUseEmulation();
+
+ if (pen.isCosmetic()) {
+ GLfloat width = pen.widthF() == 0.0f ? 1.0f : pen.widthF();
+ glLineWidth(width);
+ glPointSize(width);
+ }
+
+ if (d->pen_brush_style >= Qt::LinearGradientPattern
+ && d->pen_brush_style <= Qt::ConicalGradientPattern)
+ {
+ d->setGLPen(Qt::white);
+ } else {
+ d->setGLPen(pen.color());
+ }
+
+ d->updateFastPen();
+}
+
+void QOpenGLPaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
+{
+ Q_D(QOpenGLPaintEngine);
+ d->cbrush = brush;
+ d->brush_style = brush.style();
+ d->brush_origin = origin;
+ d->has_brush = (d->brush_style != Qt::NoBrush);
+ d->updateUseEmulation();
+}
+
+void QOpenGLPaintEngine::updateFont(const QFont &)
+{
+}
+
+void QOpenGLPaintEngine::updateMatrix(const QTransform &mtx)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ d->matrix = mtx;
+
+ d->mv_matrix[0][0] = mtx.m11();
+ d->mv_matrix[0][1] = mtx.m12();
+ d->mv_matrix[0][2] = 0;
+ d->mv_matrix[0][3] = mtx.m13();
+
+ d->mv_matrix[1][0] = mtx.m21();
+ d->mv_matrix[1][1] = mtx.m22();
+ d->mv_matrix[1][2] = 0;
+ d->mv_matrix[1][3] = mtx.m23();
+
+ d->mv_matrix[2][0] = 0;
+ d->mv_matrix[2][1] = 0;
+ d->mv_matrix[2][2] = 1;
+ d->mv_matrix[2][3] = 0;
+
+ d->mv_matrix[3][0] = mtx.dx();
+ d->mv_matrix[3][1] = mtx.dy();
+ d->mv_matrix[3][2] = 0;
+ d->mv_matrix[3][3] = mtx.m33();
+
+ d->txop = mtx.type();
+
+ // 1/10000 == 0.0001, so we have good enough res to cover curves
+ // that span the entire widget...
+ d->inverseScale = qMax(1 / qMax( qMax(qAbs(mtx.m11()), qAbs(mtx.m22())),
+ qMax(qAbs(mtx.m12()), qAbs(mtx.m21())) ),
+ qreal(0.0001));
+
+ d->updateGLMatrix();
+ d->updateFastPen();
+}
+
+void QOpenGLPaintEnginePrivate::updateGLMatrix() const
+{
+ glMatrixMode(GL_MODELVIEW);
+#ifndef QT_OPENGL_ES
+ glLoadMatrixd(&mv_matrix[0][0]);
+#else
+ glLoadMatrixf(&mv_matrix[0][0]);
+#endif
+}
+
+void QOpenGLPaintEnginePrivate::disableClipping()
+{
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+}
+
+void QOpenGLPaintEnginePrivate::enableClipping()
+{
+ Q_Q(QOpenGLPaintEngine);
+ if (!q->state()->hasClipping)
+ return;
+
+ if (q->state()->fastClip.isEmpty())
+ glEnable(GL_DEPTH_TEST);
+ else
+ updateDepthClip(); // this will enable the scissor test
+}
+
+void QOpenGLPaintEnginePrivate::updateDepthClip()
+{
+ Q_Q(QOpenGLPaintEngine);
+
+ ++q->state()->depthClipId;
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+
+ if (!q->state()->hasClipping)
+ return;
+
+ QRect fastClip;
+ if (q->state()->clipEnabled) {
+ fastClip = q->state()->fastClip;
+ } else if (use_system_clip && q->systemClip().rects().count() == 1) {
+ fastClip = q->systemClip().rects().at(0);
+ }
+
+ if (!fastClip.isEmpty()) {
+ glEnable(GL_SCISSOR_TEST);
+
+ const int left = fastClip.left();
+ const int width = fastClip.width();
+ const int bottom = device->size().height() - (fastClip.bottom() + 1);
+ const int height = fastClip.height();
+
+ glScissor(left, bottom, width, height);
+ return;
+ }
+
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
+ glClearDepthf(0.0f);
+#else
+ glClearDepth(0.0f);
+#endif
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthMask(GL_TRUE);
+ glClear(GL_DEPTH_BUFFER_BIT);
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ glDepthFunc(GL_ALWAYS);
+
+ const QVector<QRect> rects = q->state()->clipEnabled ? q->state()->clipRegion.rects() : q->systemClip().rects();
+
+ // rectangle count * 2 (triangles) * vertex count * component count (Z omitted)
+ QDataBuffer<GLfloat> clipVertex(rects.size()*2*3*2);
+ for (int i = 0; i < rects.size(); ++i) {
+ GLfloat x = GLfloat(rects.at(i).left());
+ GLfloat w = GLfloat(rects.at(i).width());
+ GLfloat h = GLfloat(rects.at(i).height());
+ GLfloat y = GLfloat(rects.at(i).top());
+
+ // First triangle
+ clipVertex.add(x);
+ clipVertex.add(y);
+
+ clipVertex.add(x);
+ clipVertex.add(y + h);
+
+ clipVertex.add(x + w);
+ clipVertex.add(y);
+
+ // Second triangle
+ clipVertex.add(x);
+ clipVertex.add(y + h);
+
+ clipVertex.add(x + w);
+ clipVertex.add(y + h);
+
+ clipVertex.add (x + w);
+ clipVertex.add(y);
+ }
+
+ if (rects.size()) {
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, clipVertex.data());
+
+ glDrawArrays(GL_TRIANGLES, 0, rects.size()*2*3);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ updateGLMatrix();
+ }
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glDepthMask(GL_FALSE);
+ glDepthFunc(GL_LEQUAL);
+}
+
+void QOpenGLPaintEnginePrivate::systemStateChanged()
+{
+ Q_Q(QOpenGLPaintEngine);
+ if (q->painter()->hasClipping())
+ q->updateClipRegion(q->painter()->clipRegion(), Qt::ReplaceClip);
+ else
+ q->updateClipRegion(QRegion(), Qt::NoClip);
+}
+
+void QOpenGLPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ // clipping is only supported when a stencil or depth buffer is
+ // available
+ if (!d->device->format().depth())
+ return;
+
+ d->use_system_clip = false;
+ QRegion sysClip = systemClip();
+ if (!sysClip.isEmpty()) {
+ if (d->pdev->devType() != QInternal::Widget) {
+ d->use_system_clip = true;
+ } else {
+#ifndef Q_WS_QWS
+ // Only use the system clip if we're currently rendering a widget with a GL painter.
+ if (d->currentClipWidget) {
+ QWidgetPrivate *widgetPrivate = qt_widget_private(d->currentClipWidget->window());
+ d->use_system_clip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter;
+ }
+#endif
+ }
+ }
+
+ d->flushDrawQueue();
+
+ if (op == Qt::NoClip && !d->use_system_clip) {
+ state()->hasClipping = false;
+ state()->clipRegion = QRegion();
+ d->updateDepthClip();
+ return;
+ }
+
+ bool isScreenClip = false;
+ if (!d->use_system_clip) {
+ QVector<QRect> untransformedRects = clipRegion.rects();
+
+ if (untransformedRects.size() == 1) {
+ QPainterPath path;
+ path.addRect(untransformedRects[0]);
+ path = d->matrix.map(path);
+
+ if (path.contains(QRectF(QPointF(), d->device->size())))
+ isScreenClip = true;
+ }
+ }
+
+ QRegion region = isScreenClip ? QRegion() : clipRegion * d->matrix;
+ switch (op) {
+ case Qt::NoClip:
+ if (!d->use_system_clip)
+ break;
+ state()->clipRegion = sysClip;
+ break;
+ case Qt::IntersectClip:
+ if (isScreenClip)
+ return;
+ if (state()->hasClipping) {
+ state()->clipRegion &= region;
+ break;
+ }
+ // fall through
+ case Qt::ReplaceClip:
+ if (d->use_system_clip)
+ state()->clipRegion = region & sysClip;
+ else
+ state()->clipRegion = region;
+ break;
+ case Qt::UniteClip:
+ state()->clipRegion |= region;
+ if (d->use_system_clip)
+ state()->clipRegion &= sysClip;
+ break;
+ default:
+ break;
+ }
+
+ if (isScreenClip) {
+ state()->hasClipping = false;
+ state()->clipRegion = QRegion();
+ } else {
+ state()->hasClipping = op != Qt::NoClip || d->use_system_clip;
+ }
+
+ if (state()->hasClipping && state()->clipRegion.rects().size() == 1)
+ state()->fastClip = state()->clipRegion.rects().at(0);
+ else
+ state()->fastClip = QRect();
+
+ d->updateDepthClip();
+}
+
+void QOpenGLPaintEngine::updateRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ d->flushDrawQueue();
+ d->use_smooth_pixmap_transform = bool(hints & QPainter::SmoothPixmapTransform);
+ if ((hints & QPainter::Antialiasing) || (hints & QPainter::HighQualityAntialiasing)) {
+ if (d->use_fragment_programs && QGLOffscreen::isSupported()
+ && (hints & QPainter::HighQualityAntialiasing)) {
+ d->high_quality_antialiasing = true;
+ } else {
+ d->high_quality_antialiasing = false;
+ if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
+ glEnable(GL_MULTISAMPLE);
+ }
+ } else {
+ d->high_quality_antialiasing = false;
+ if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
+ glDisable(GL_MULTISAMPLE);
+ }
+
+ if (d->high_quality_antialiasing) {
+ d->offscreen.initialize();
+
+ if (!d->offscreen.isValid()) {
+ DEBUG_ONCE_STR("Unable to initialize offscreen, disabling high quality antialiasing");
+ d->high_quality_antialiasing = false;
+ if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
+ glEnable(GL_MULTISAMPLE);
+ }
+ }
+
+ d->has_antialiasing = d->high_quality_antialiasing
+ || ((hints & QPainter::Antialiasing)
+ && (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers));
+}
+
+
+void QOpenGLPaintEnginePrivate::setPorterDuffData(float a, float b, float x, float y, float z)
+{
+ porterduff_ab_data[0] = a;
+ porterduff_ab_data[1] = b;
+
+ porterduff_xyz_data[0] = x;
+ porterduff_xyz_data[1] = y;
+ porterduff_xyz_data[2] = z;
+}
+
+
+void QOpenGLPaintEngine::updateCompositionMode(QPainter::CompositionMode composition_mode)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (!d->use_fragment_programs && composition_mode > QPainter::CompositionMode_Plus)
+ composition_mode = QPainter::CompositionMode_SourceOver;
+
+ d->composition_mode = composition_mode;
+
+ d->has_fast_composition_mode = (!d->high_quality_antialiasing && composition_mode <= QPainter::CompositionMode_Plus)
+ || composition_mode == QPainter::CompositionMode_SourceOver
+ || composition_mode == QPainter::CompositionMode_Destination
+ || composition_mode == QPainter::CompositionMode_DestinationOver
+ || composition_mode == QPainter::CompositionMode_DestinationOut
+ || composition_mode == QPainter::CompositionMode_SourceAtop
+ || composition_mode == QPainter::CompositionMode_Xor
+ || composition_mode == QPainter::CompositionMode_Plus;
+
+ if (d->has_fast_composition_mode)
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODE_BLEND_MODE_MASK : COMPOSITION_MODE_BLEND_MODE_NOMASK;
+ else if (composition_mode <= QPainter::CompositionMode_Plus)
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SIMPLE_PORTER_DUFF : COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK;
+ else
+ switch (composition_mode) {
+ case QPainter::CompositionMode_Multiply:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_MULTIPLY : COMPOSITION_MODES_MULTIPLY_NOMASK;
+ break;
+ case QPainter::CompositionMode_Screen:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SCREEN : COMPOSITION_MODES_SCREEN_NOMASK;
+ break;
+ case QPainter::CompositionMode_Overlay:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_OVERLAY : COMPOSITION_MODES_OVERLAY_NOMASK;
+ break;
+ case QPainter::CompositionMode_Darken:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_DARKEN : COMPOSITION_MODES_DARKEN_NOMASK;
+ break;
+ case QPainter::CompositionMode_Lighten:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_LIGHTEN : COMPOSITION_MODES_LIGHTEN_NOMASK;
+ break;
+ case QPainter::CompositionMode_ColorDodge:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_COLORDODGE : COMPOSITION_MODES_COLORDODGE_NOMASK;
+ break;
+ case QPainter::CompositionMode_ColorBurn:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_COLORBURN : COMPOSITION_MODES_COLORBURN_NOMASK;
+ break;
+ case QPainter::CompositionMode_HardLight:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_HARDLIGHT : COMPOSITION_MODES_HARDLIGHT_NOMASK;
+ break;
+ case QPainter::CompositionMode_SoftLight:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SOFTLIGHT : COMPOSITION_MODES_SOFTLIGHT_NOMASK;
+ break;
+ case QPainter::CompositionMode_Difference:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_DIFFERENCE : COMPOSITION_MODES_DIFFERENCE_NOMASK;
+ break;
+ case QPainter::CompositionMode_Exclusion:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_EXCLUSION : COMPOSITION_MODES_EXCLUSION_NOMASK;
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+
+ switch(composition_mode) {
+ case QPainter::CompositionMode_DestinationOver:
+ glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
+ d->setPorterDuffData(0, 1, 1, 1, 1);
+ break;
+ case QPainter::CompositionMode_Clear:
+ glBlendFunc(GL_ZERO, GL_ZERO);
+ d->setPorterDuffData(0, 0, 0, 0, 0);
+ break;
+ case QPainter::CompositionMode_Source:
+ glBlendFunc(GL_ONE, GL_ZERO);
+ d->setPorterDuffData(1, 0, 1, 1, 0);
+ break;
+ case QPainter::CompositionMode_Destination:
+ glBlendFunc(GL_ZERO, GL_ONE);
+ d->setPorterDuffData(0, 1, 1, 0, 1);
+ break;
+ case QPainter::CompositionMode_SourceIn:
+ glBlendFunc(GL_DST_ALPHA, GL_ZERO);
+ d->setPorterDuffData(1, 0, 1, 0, 0);
+ break;
+ case QPainter::CompositionMode_DestinationIn:
+ glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
+ d->setPorterDuffData(0, 1, 1, 0, 0);
+ break;
+ case QPainter::CompositionMode_SourceOut:
+ glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
+ d->setPorterDuffData(0, 0, 0, 1, 0);
+ break;
+ case QPainter::CompositionMode_DestinationOut:
+ glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
+ d->setPorterDuffData(0, 0, 0, 0, 1);
+ break;
+ case QPainter::CompositionMode_SourceAtop:
+ glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ d->setPorterDuffData(1, 0, 1, 0, 1);
+ break;
+ case QPainter::CompositionMode_DestinationAtop:
+ glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
+ d->setPorterDuffData(0, 1, 1, 1, 0);
+ break;
+ case QPainter::CompositionMode_Xor:
+ glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ d->setPorterDuffData(0, 0, 0, 1, 1);
+ break;
+ case QPainter::CompositionMode_SourceOver:
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ d->setPorterDuffData(1, 0, 1, 1, 1);
+ break;
+ case QPainter::CompositionMode_Plus:
+ glBlendFunc(GL_ONE, GL_ONE);
+ d->setPorterDuffData(1, 1, 1, 1, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+class QGLMaskGenerator
+{
+public:
+ QGLMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal stroke_width = -1)
+ : p(path),
+ m(matrix),
+ w(stroke_width)
+ {
+ }
+
+ virtual QRect screenRect() = 0;
+ virtual void drawMask(const QRect &rect) = 0;
+
+ QPainterPath path() const { return p; }
+ QTransform matrix() const { return m; }
+ qreal strokeWidth() const { return w; }
+
+ virtual ~QGLMaskGenerator() {}
+
+private:
+ QPainterPath p;
+ QTransform m;
+ qreal w;
+};
+
+void QGLMaskTextureCache::setOffscreenSize(const QSize &sz)
+{
+ Q_ASSERT(sz.width() == sz.height());
+
+ if (offscreenSize != sz) {
+ offscreenSize = sz;
+ clearCache();
+ }
+}
+
+void QGLMaskTextureCache::clearCache()
+{
+ cache.clear();
+
+ int quad_tree_size = 1;
+
+ for (int i = block_size; i < offscreenSize.width(); i *= 2)
+ quad_tree_size += quad_tree_size * 4;
+
+ for (int i = 0; i < 4; ++i) {
+ occupied_quadtree[i].resize(quad_tree_size);
+
+ occupied_quadtree[i][0].key = 0;
+ occupied_quadtree[i][0].largest_available_block = offscreenSize.width();
+ occupied_quadtree[i][0].largest_used_block = 0;
+
+ DEBUG_ONCE qDebug() << "QGLMaskTextureCache:: created quad tree of size" << quad_tree_size;
+ }
+}
+
+void QGLMaskTextureCache::setDrawableSize(const QSize &sz)
+{
+ drawableSize = sz;
+}
+
+void QGLMaskTextureCache::maintainCache()
+{
+ QGLTextureCacheHash::iterator it = cache.begin();
+ QGLTextureCacheHash::iterator end = cache.end();
+
+ while (it != end) {
+ CacheInfo &cache_info = it.value();
+ ++cache_info.age;
+
+ if (cache_info.age > 1) {
+ quadtreeInsert(cache_info.loc.channel, 0, cache_info.loc.rect);
+ it = cache.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+//#define DISABLE_MASK_CACHE
+
+QGLMaskTextureCache::CacheLocation QGLMaskTextureCache::getMask(QGLMaskGenerator &maskGenerator, QOpenGLPaintEnginePrivate *e)
+{
+#ifndef DISABLE_MASK_CACHE
+ engine = e;
+
+ quint64 key = hash(maskGenerator.path(), maskGenerator.matrix(), maskGenerator.strokeWidth());
+
+ if (key == 0)
+ key = 1;
+
+ CacheInfo info(maskGenerator.path(), maskGenerator.matrix(), maskGenerator.strokeWidth());
+
+ QGLTextureCacheHash::iterator it = cache.find(key);
+
+ while (it != cache.end() && it.key() == key) {
+ CacheInfo &cache_info = it.value();
+ if (info.stroke_width == cache_info.stroke_width && info.matrix == cache_info.matrix && info.path == cache_info.path) {
+ DEBUG_ONCE_STR("QGLMaskTextureCache::getMask(): Using cached mask");
+
+ cache_info.age = 0;
+ return cache_info.loc;
+ }
+ ++it;
+ }
+
+ // mask was not found, create new mask
+
+ DEBUG_ONCE_STR("QGLMaskTextureCache::getMask(): Creating new mask...");
+
+ createMask(key, info, maskGenerator);
+
+ cache.insert(key, info);
+
+ return info.loc;
+#else
+ CacheInfo info(maskGenerator.path(), maskGenerator.matrix());
+ createMask(0, info, maskGenerator);
+ return info.loc;
+#endif
+}
+
+#ifndef FloatToQuint64
+#define FloatToQuint64(i) (quint64)((i) * 32)
+#endif
+
+quint64 QGLMaskTextureCache::hash(const QPainterPath &p, const QTransform &m, qreal w)
+{
+ Q_ASSERT(sizeof(quint64) == 8);
+
+ quint64 h = 0;
+
+ for (int i = 0; i < p.elementCount(); ++i) {
+ h += FloatToQuint64(p.elementAt(i).x) << 32;
+ h += FloatToQuint64(p.elementAt(i).y);
+ h += p.elementAt(i).type;
+ }
+
+ h += FloatToQuint64(m.m11());
+#ifndef Q_OS_WINCE // ###
+ //Compiler crashes for arm on WinCE
+ h += FloatToQuint64(m.m12()) << 4;
+ h += FloatToQuint64(m.m13()) << 8;
+ h += FloatToQuint64(m.m21()) << 12;
+ h += FloatToQuint64(m.m22()) << 16;
+ h += FloatToQuint64(m.m23()) << 20;
+ h += FloatToQuint64(m.m31()) << 24;
+ h += FloatToQuint64(m.m32()) << 28;
+#endif
+ h += FloatToQuint64(m.m33()) << 32;
+
+ h += FloatToQuint64(w);
+
+ return h;
+}
+
+void QGLMaskTextureCache::createMask(quint64 key, CacheInfo &info, QGLMaskGenerator &maskGenerator)
+{
+ info.loc.screen_rect = maskGenerator.screenRect();
+
+ if (info.loc.screen_rect.isEmpty()) {
+ info.loc.channel = 0;
+ info.loc.rect = QRect();
+ return;
+ }
+
+ quadtreeAllocate(key, info.loc.screen_rect.size(), &info.loc.rect, &info.loc.channel);
+
+ int ch = info.loc.channel;
+ glColorMask(ch == 0, ch == 1, ch == 2, ch == 3);
+
+ maskGenerator.drawMask(info.loc.rect);
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+}
+
+int QGLMaskTextureCache::quadtreeBlocksize(int node)
+{
+ DEBUG_ONCE qDebug() << "Offscreen size:" << offscreenSize.width();
+
+ int blocksize = offscreenSize.width();
+
+ while (node) {
+ node = (node - 1) / 4;
+ blocksize /= 2;
+ }
+
+ return blocksize;
+}
+
+QPoint QGLMaskTextureCache::quadtreeLocation(int node)
+{
+ QPoint location;
+ int blocksize = quadtreeBlocksize(node);
+
+ while (node) {
+ --node;
+
+ if (node & 1)
+ location.setX(location.x() + blocksize);
+
+ if (node & 2)
+ location.setY(location.y() + blocksize);
+
+ node /= 4;
+ blocksize *= 2;
+ }
+
+ return location;
+}
+
+void QGLMaskTextureCache::quadtreeUpdate(int channel, int node, int current_block_size)
+{
+ while (node) {
+ node = (node - 1) / 4;
+
+ int first_child = node * 4 + 1;
+
+ int largest_available = 0;
+ int largest_used = 0;
+
+ bool all_empty = true;
+
+ for (int i = 0; i < 4; ++i) {
+ largest_available = qMax(largest_available, occupied_quadtree[channel][first_child + i].largest_available_block);
+ largest_used = qMax(largest_used, occupied_quadtree[channel][first_child + i].largest_used_block);
+
+ if (occupied_quadtree[channel][first_child + i].largest_available_block < current_block_size)
+ all_empty = false;
+ }
+
+ current_block_size *= 2;
+
+ if (all_empty) {
+ occupied_quadtree[channel][node].largest_available_block = current_block_size;
+ occupied_quadtree[channel][node].largest_used_block = 0;
+ } else {
+ occupied_quadtree[channel][node].largest_available_block = largest_available;
+ occupied_quadtree[channel][node].largest_used_block = largest_used;
+ }
+ }
+}
+
+void QGLMaskTextureCache::quadtreeInsert(int channel, quint64 key, const QRect &rect, int node)
+{
+ int current_block_size = quadtreeBlocksize(node);
+ QPoint location = quadtreeLocation(node);
+ QRect relative = rect.translated(-location);
+
+ if (relative.left() >= current_block_size || relative.top() >= current_block_size
+ || relative.right() < 0 || relative.bottom() < 0)
+ return;
+
+ if (current_block_size == block_size // no more refining possible
+ || (relative.top() < block_size && relative.bottom() >= (current_block_size - block_size)
+ && relative.left() < block_size && relative.right() >= (current_block_size - block_size)))
+ {
+ if (key != 0) {
+ occupied_quadtree[channel][node].largest_available_block = 0;
+ occupied_quadtree[channel][node].largest_used_block = rect.width() * rect.height();
+ } else {
+ occupied_quadtree[channel][node].largest_available_block = current_block_size;
+ occupied_quadtree[channel][node].largest_used_block = 0;
+ }
+
+ occupied_quadtree[channel][node].key = key;
+
+ quadtreeUpdate(channel, node, current_block_size);
+ } else {
+ if (key && occupied_quadtree[channel][node].largest_available_block == current_block_size) {
+ // refining the quad tree, initialize child nodes
+ int half_block_size = current_block_size / 2;
+
+ int temp = node * 4 + 1;
+ for (int sibling = 0; sibling < 4; ++sibling) {
+ occupied_quadtree[channel][temp + sibling].largest_available_block = half_block_size;
+ occupied_quadtree[channel][temp + sibling].largest_used_block = 0;
+ occupied_quadtree[channel][temp + sibling].key = 0;
+ }
+ }
+
+ node = node * 4 + 1;
+
+ for (int sibling = 0; sibling < 4; ++sibling)
+ quadtreeInsert(channel, key, rect, node + sibling);
+ }
+}
+
+void QGLMaskTextureCache::quadtreeClear(int channel, const QRect &rect, int node)
+{
+ const quint64 &key = occupied_quadtree[channel][node].key;
+
+ int current_block_size = quadtreeBlocksize(node);
+ QPoint location = quadtreeLocation(node);
+
+ QRect relative = rect.translated(-location);
+
+ if (relative.left() >= current_block_size || relative.top() >= current_block_size
+ || relative.right() < 0 || relative.bottom() < 0)
+ return;
+
+ if (key != 0) {
+ QGLTextureCacheHash::iterator it = cache.find(key);
+
+ Q_ASSERT(it != cache.end());
+
+ while (it != cache.end() && it.key() == key) {
+ const CacheInfo &cache_info = it.value();
+
+ if (cache_info.loc.channel == channel
+ && cache_info.loc.rect.left() <= location.x()
+ && cache_info.loc.rect.top() <= location.y()
+ && cache_info.loc.rect.right() >= location.x()
+ && cache_info.loc.rect.bottom() >= location.y())
+ {
+ quadtreeInsert(channel, 0, cache_info.loc.rect);
+ engine->cacheItemErased(channel, cache_info.loc.rect);
+ cache.erase(it);
+ goto found;
+ } else {
+ ++it;
+ }
+ }
+
+ // if we don't find the key there's an error in the quadtree
+ Q_ASSERT(false);
+found:
+ Q_ASSERT(occupied_quadtree[channel][node].key == 0);
+ } else if (occupied_quadtree[channel][node].largest_available_block < current_block_size) {
+ Q_ASSERT(current_block_size >= block_size);
+
+ node = node * 4 + 1;
+
+ for (int sibling = 0; sibling < 4; ++sibling)
+ quadtreeClear(channel, rect, node + sibling);
+ }
+}
+
+bool QGLMaskTextureCache::quadtreeFindAvailableLocation(const QSize &size, QRect *rect, int *channel)
+{
+ int needed_block_size = qMax(1, qMax(size.width(), size.height()));
+
+ for (int i = 0; i < 4; ++i) {
+ int current_block_size = offscreenSize.width();
+
+ if (occupied_quadtree[i][0].largest_available_block >= needed_block_size) {
+ int node = 0;
+
+ while (current_block_size != occupied_quadtree[i][node].largest_available_block) {
+ Q_ASSERT(current_block_size > block_size);
+ Q_ASSERT(current_block_size > occupied_quadtree[i][node].largest_available_block);
+
+ node = node * 4 + 1;
+ current_block_size /= 2;
+
+ int sibling = 0;
+
+ while (occupied_quadtree[i][node + sibling].largest_available_block < needed_block_size)
+ ++sibling;
+
+ Q_ASSERT(sibling < 4);
+ node += sibling;
+ }
+
+ *channel = i;
+ *rect = QRect(quadtreeLocation(node), size);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QGLMaskTextureCache::quadtreeFindExistingLocation(const QSize &size, QRect *rect, int *channel)
+{
+ // try to pick small masks to throw out, as large masks are more expensive to recompute
+ *channel = qrand() % 4;
+ for (int i = 0; i < 4; ++i)
+ if (occupied_quadtree[i][0].largest_used_block < occupied_quadtree[*channel][0].largest_used_block)
+ *channel = i;
+
+ int needed_block_size = qt_next_power_of_two(qMax(1, qMax(size.width(), size.height())));
+
+ int node = 0;
+ int current_block_size = offscreenSize.width();
+
+ while (current_block_size > block_size
+ && current_block_size >= needed_block_size * 2
+ && occupied_quadtree[*channel][node].key == 0)
+ {
+ node = node * 4 + 1;
+
+ int sibling = 0;
+
+ for (int i = 1; i < 4; ++i) {
+ if (occupied_quadtree[*channel][node + i].largest_used_block
+ <= occupied_quadtree[*channel][node + sibling].largest_used_block)
+ {
+ sibling = i;
+ }
+ }
+
+ node += sibling;
+ current_block_size /= 2;
+ }
+
+ *rect = QRect(quadtreeLocation(node), size);
+}
+
+void QGLMaskTextureCache::quadtreeAllocate(quint64 key, const QSize &size, QRect *rect, int *channel)
+{
+#ifndef DISABLE_MASK_CACHE
+ if (!quadtreeFindAvailableLocation(size, rect, channel)) {
+ quadtreeFindExistingLocation(size, rect, channel);
+ quadtreeClear(*channel, *rect);
+ }
+
+ quadtreeInsert(*channel, key, *rect);
+#else
+ *channel = 0;
+ *rect = QRect(QPoint(), size);
+#endif
+}
+
+class QGLTrapezoidMaskGenerator : public QGLMaskGenerator
+{
+public:
+ QGLTrapezoidMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram, qreal strokeWidth = -1.0);
+
+ QRect screenRect();
+ void drawMask(const QRect &rect);
+
+private:
+ QRect screen_rect;
+ bool has_screen_rect;
+
+ QGLOffscreen *offscreen;
+
+ GLuint maskFragmentProgram;
+
+ virtual QVector<QGLTrapezoid> generateTrapezoids() = 0;
+ virtual QRect computeScreenRect() = 0;
+};
+
+class QGLPathMaskGenerator : public QGLTrapezoidMaskGenerator
+{
+public:
+ QGLPathMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
+
+private:
+ QVector<QGLTrapezoid> generateTrapezoids();
+ QRect computeScreenRect();
+
+ QPolygonF poly;
+};
+
+class QGLLineMaskGenerator : public QGLTrapezoidMaskGenerator
+{
+public:
+ QGLLineMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal width, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
+
+private:
+ QVector<QGLTrapezoid> generateTrapezoids();
+ QRect computeScreenRect();
+
+ QPainterPath transformedPath;
+};
+
+class QGLRectMaskGenerator : public QGLTrapezoidMaskGenerator
+{
+public:
+ QGLRectMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
+
+private:
+ QVector<QGLTrapezoid> generateTrapezoids();
+ QRect computeScreenRect();
+
+ QPainterPath transformedPath;
+};
+
+class QGLEllipseMaskGenerator : public QGLMaskGenerator
+{
+public:
+ QGLEllipseMaskGenerator(const QRectF &rect, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram, int *maskVariableLocations);
+
+ QRect screenRect();
+ void drawMask(const QRect &rect);
+
+private:
+ QRect screen_rect;
+
+ QRectF ellipseRect;
+
+ QGLOffscreen *offscreen;
+
+ GLuint maskFragmentProgram;
+
+ int *maskVariableLocations;
+
+ float vertexArray[4 * 2];
+};
+
+QGLTrapezoidMaskGenerator::QGLTrapezoidMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program, qreal stroke_width)
+ : QGLMaskGenerator(path, matrix, stroke_width)
+ , has_screen_rect(false)
+ , offscreen(&offs)
+ , maskFragmentProgram(program)
+{
+}
+
+extern void qt_add_rect_to_array(const QRectF &r, GLfloat *array);
+extern void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, GLfloat *array);
+
+void QGLTrapezoidMaskGenerator::drawMask(const QRect &rect)
+{
+#ifdef QT_OPENGL_ES
+ Q_UNUSED(rect);
+#else
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ QGLContext *ctx = offscreen->context();
+ offscreen->bind();
+
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_1D);
+
+ GLfloat vertexArray[4 * 2];
+ qt_add_rect_to_array(rect, vertexArray);
+
+ bool needs_scissor = rect != screen_rect;
+
+ if (needs_scissor) {
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(rect.left(), offscreen->offscreenSize().height() - rect.bottom() - 1, rect.width(), rect.height());
+ }
+
+ QVector<QGLTrapezoid> trapezoids = generateTrapezoids();
+
+ // clear mask
+ glBlendFunc(GL_ZERO, GL_ZERO); // clear
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glBlendFunc(GL_ONE, GL_ONE); // add mask
+ glEnable(GL_FRAGMENT_PROGRAM_ARB);
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, maskFragmentProgram);
+
+ QPoint delta = rect.topLeft() - screen_rect.topLeft();
+ glBegin(GL_QUADS);
+ for (int i = 0; i < trapezoids.size(); ++i)
+ drawTrapezoid(trapezoids[i].translated(delta), offscreen->offscreenSize().height(), ctx);
+ glEnd();
+
+ if (needs_scissor)
+ glDisable(GL_SCISSOR_TEST);
+
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+#endif
+}
+
+QRect QGLTrapezoidMaskGenerator::screenRect()
+{
+ if (!has_screen_rect) {
+ screen_rect = computeScreenRect();
+ has_screen_rect = true;
+ }
+
+ screen_rect = screen_rect.intersected(QRect(QPoint(), offscreen->drawableSize()));
+
+ return screen_rect;
+}
+
+QGLPathMaskGenerator::QGLPathMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program)
+ : QGLTrapezoidMaskGenerator(path, matrix, offs, program)
+{
+}
+
+QRect QGLPathMaskGenerator::computeScreenRect()
+{
+ poly = path().toFillPolygon(matrix());
+ return poly.boundingRect().toAlignedRect();
+}
+
+QVector<QGLTrapezoid> QGLPathMaskGenerator::generateTrapezoids()
+{
+ QOpenGLImmediateModeTessellator tessellator;
+ tessellator.tessellate(poly.data(), poly.count(), path().fillRule() == Qt::WindingFill);
+ return tessellator.trapezoids;
+}
+
+QGLRectMaskGenerator::QGLRectMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program)
+ : QGLTrapezoidMaskGenerator(path, matrix, offs, program)
+{
+}
+
+QGLLineMaskGenerator::QGLLineMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal width, QGLOffscreen &offs, GLuint program)
+ : QGLTrapezoidMaskGenerator(path, matrix, offs, program, width)
+{
+}
+
+QRect QGLRectMaskGenerator::computeScreenRect()
+{
+ transformedPath = matrix().map(path());
+
+ return transformedPath.controlPointRect().adjusted(-1, -1, 1, 1).toAlignedRect();
+}
+
+QRect QGLLineMaskGenerator::computeScreenRect()
+{
+ transformedPath = matrix().map(path());
+
+ return transformedPath.controlPointRect().adjusted(-1, -1, 1, 1).toAlignedRect();
+}
+
+QVector<QGLTrapezoid> QGLLineMaskGenerator::generateTrapezoids()
+{
+ QOpenGLImmediateModeTessellator tessellator;
+ QPointF last;
+ for (int i = 0; i < transformedPath.elementCount(); ++i) {
+ QPainterPath::Element element = transformedPath.elementAt(i);
+
+ Q_ASSERT(!element.isCurveTo());
+
+ if (element.isLineTo())
+ tessellator.tessellateRect(last, element, strokeWidth());
+
+ last = element;
+ }
+
+ return tessellator.trapezoids;
+}
+
+QVector<QGLTrapezoid> QGLRectMaskGenerator::generateTrapezoids()
+{
+ Q_ASSERT(transformedPath.elementCount() == 5);
+
+ QOpenGLImmediateModeTessellator tessellator;
+ if (matrix().type() <= QTransform::TxScale) {
+ QPointF a = transformedPath.elementAt(0);
+ QPointF b = transformedPath.elementAt(1);
+ QPointF c = transformedPath.elementAt(2);
+ QPointF d = transformedPath.elementAt(3);
+
+ QPointF first = (a + d) * 0.5;
+ QPointF last = (b + c) * 0.5;
+
+ QPointF delta = a - d;
+
+ // manhattan distance (no rotation)
+ qreal width = qAbs(delta.x()) + qAbs(delta.y());
+
+ Q_ASSERT(qFuzzyIsNull(delta.x()) || qFuzzyIsNull(delta.y()));
+
+ tessellator.tessellateRect(first, last, width);
+ } else {
+ QPointF points[5];
+
+ for (int i = 0; i < 5; ++i)
+ points[i] = transformedPath.elementAt(i);
+
+ tessellator.tessellateConvex(points, 5);
+ }
+ return tessellator.trapezoids;
+}
+
+static QPainterPath ellipseRectToPath(const QRectF &rect)
+{
+ QPainterPath path;
+ path.addEllipse(rect);
+ return path;
+}
+
+QGLEllipseMaskGenerator::QGLEllipseMaskGenerator(const QRectF &rect, const QTransform &matrix, QGLOffscreen &offs, GLuint program, int *locations)
+ : QGLMaskGenerator(ellipseRectToPath(rect), matrix),
+ ellipseRect(rect),
+ offscreen(&offs),
+ maskFragmentProgram(program),
+ maskVariableLocations(locations)
+{
+}
+
+QRect QGLEllipseMaskGenerator::screenRect()
+{
+ QPointF center = ellipseRect.center();
+
+ QPointF points[] = {
+ QPointF(ellipseRect.left(), center.y()),
+ QPointF(ellipseRect.right(), center.y()),
+ QPointF(center.x(), ellipseRect.top()),
+ QPointF(center.x(), ellipseRect.bottom())
+ };
+
+ qreal min_screen_delta_len = QREAL_MAX;
+
+ for (int i = 0; i < 4; ++i) {
+ QPointF delta = points[i] - center;
+
+ // normalize
+ delta /= qSqrt(delta.x() * delta.x() + delta.y() * delta.y());
+
+ QPointF screen_delta(matrix().m11() * delta.x() + matrix().m21() * delta.y(),
+ matrix().m12() * delta.x() + matrix().m22() * delta.y());
+
+ min_screen_delta_len = qMin(min_screen_delta_len,
+ qreal(qSqrt(screen_delta.x() * screen_delta.x() + screen_delta.y() * screen_delta.y())));
+ }
+
+ const qreal padding = 2.0f;
+
+ qreal grow = padding / min_screen_delta_len;
+
+ QRectF boundingRect = ellipseRect.adjusted(-grow, -grow, grow, grow);
+
+ boundingRect = matrix().mapRect(boundingRect);
+
+ QPointF p(0.5, 0.5);
+
+ screen_rect = QRect((boundingRect.topLeft() - p).toPoint(),
+ (boundingRect.bottomRight() + p).toPoint());
+
+ return screen_rect;
+}
+
+void QGLEllipseMaskGenerator::drawMask(const QRect &rect)
+{
+#ifdef QT_OPENGL_ES
+ Q_UNUSED(rect);
+#else
+ QGLContext *ctx = offscreen->context();
+ offscreen->bind();
+
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_1D);
+
+ // fragment program needs the inverse radii of the ellipse
+ glTexCoord2f(1.0f / (ellipseRect.width() * 0.5f),
+ 1.0f / (ellipseRect.height() * 0.5f));
+
+ QTransform translate(1, 0, 0, 1, -ellipseRect.center().x(), -ellipseRect.center().y());
+ QTransform gl_to_qt(1, 0, 0, -1, 0, offscreen->drawableSize().height());
+ QTransform inv_matrix = gl_to_qt * matrix().inverted() * translate;
+
+ float m[3][4] = { { float(inv_matrix.m11()), float(inv_matrix.m12()), float(inv_matrix.m13()) },
+ { float(inv_matrix.m21()), float(inv_matrix.m22()), float(inv_matrix.m23()) },
+ { float(inv_matrix.m31()), float(inv_matrix.m32()), float(inv_matrix.m33()) } };
+
+ QPoint offs(screen_rect.left() - rect.left(), (offscreen->drawableSize().height() - screen_rect.top())
+ - (offscreen->offscreenSize().height() - rect.top()));
+
+ // last component needs to be 1.0f to avoid Nvidia bug on linux
+ float ellipse_offset[4] = { float(offs.x()), float(offs.y()), 0.0f, 1.0f };
+
+ GLfloat vertexArray[4 * 2];
+ qt_add_rect_to_array(rect, vertexArray);
+
+ glBlendFunc(GL_ONE, GL_ZERO); // set mask
+ glEnable(GL_FRAGMENT_PROGRAM_ARB);
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, maskFragmentProgram);
+
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M0], m[0]);
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M1], m[1]);
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M2], m[2]);
+
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_ELLIPSE_OFFSET], ellipse_offset);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+#endif
+}
+
+void QOpenGLPaintEnginePrivate::drawOffscreenPath(const QPainterPath &path)
+{
+#ifdef Q_WS_QWS
+ Q_UNUSED(path);
+#else
+ DEBUG_ONCE_STR("QOpenGLPaintEnginePrivate::drawOffscreenPath()");
+
+ disableClipping();
+
+ GLuint program = qt_gl_program_cache()->getProgram(device->context(),
+ FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
+ QGLPathMaskGenerator maskGenerator(path, matrix, offscreen, program);
+ addItem(qt_mask_texture_cache()->getMask(maskGenerator, this));
+
+ enableClipping();
+#endif
+}
+
+void QOpenGLPaintEnginePrivate::drawFastRect(const QRectF &r)
+{
+ Q_Q(QOpenGLPaintEngine);
+ DEBUG_ONCE_STR("QOpenGLPaintEngine::drawRects(): drawing fast rect");
+
+ GLfloat vertexArray[10];
+ qt_add_rect_to_array(r, vertexArray);
+
+ if (has_pen)
+ QOpenGLCoordinateOffset::enableOffset(this);
+
+ if (has_brush) {
+ flushDrawQueue();
+
+ bool temp = high_quality_antialiasing;
+ high_quality_antialiasing = false;
+
+ q->updateCompositionMode(composition_mode);
+
+ setGradientOps(cbrush, r);
+
+ bool fast_style = current_style == Qt::LinearGradientPattern
+ || current_style == Qt::SolidPattern;
+
+ if (fast_style && has_fast_composition_mode) {
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ } else {
+ composite(r);
+ }
+
+ high_quality_antialiasing = temp;
+
+ q->updateCompositionMode(composition_mode);
+ }
+
+ if (has_pen) {
+ if (has_fast_pen && !high_quality_antialiasing) {
+ setGradientOps(cpen.brush(), r);
+
+ vertexArray[8] = vertexArray[0];
+ vertexArray[9] = vertexArray[1];
+
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDrawArrays(GL_LINE_STRIP, 0, 5);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ } else {
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+
+ qreal left = r.left();
+ qreal right = r.right();
+ qreal top = r.top();
+ qreal bottom = r.bottom();
+
+ path.moveTo(left, top);
+ path.lineTo(right, top);
+ path.lineTo(right, bottom);
+ path.lineTo(left, bottom);
+ path.lineTo(left, top);
+
+ strokePath(path, false);
+ }
+
+ QOpenGLCoordinateOffset::disableOffset(this);
+ }
+}
+
+bool QOpenGLPaintEnginePrivate::isFastRect(const QRectF &rect)
+{
+ if (matrix.type() < QTransform::TxRotate) {
+ QRectF r = matrix.mapRect(rect);
+ return r.topLeft().toPoint() == r.topLeft()
+ && r.bottomRight().toPoint() == r.bottomRight();
+ }
+
+ return false;
+}
+
+void QOpenGLPaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+ struct RectF {
+ qreal x;
+ qreal y;
+ qreal w;
+ qreal h;
+ };
+ Q_ASSERT(sizeof(RectF) == sizeof(QRectF));
+ RectF fr[256];
+ while (rectCount) {
+ int i = 0;
+ while (i < rectCount && i < 256) {
+ fr[i].x = rects[i].x();
+ fr[i].y = rects[i].y();
+ fr[i].w = rects[i].width();
+ fr[i].h = rects[i].height();
+ ++i;
+ }
+ drawRects((QRectF *)(void *)fr, i);
+ rects += i;
+ rectCount -= i;
+ }
+}
+
+void QOpenGLPaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (d->use_emulation) {
+ QPaintEngineEx::drawRects(rects, rectCount);
+ return;
+ }
+
+ for (int i=0; i<rectCount; ++i) {
+ const QRectF &r = rects[i];
+
+ // optimization for rects which can be drawn aliased
+ if (!d->high_quality_antialiasing || d->isFastRect(r)) {
+ d->drawFastRect(r);
+ } else {
+ QPainterPath path;
+ path.addRect(r);
+
+ if (d->has_brush) {
+ d->disableClipping();
+ GLuint program = qt_gl_program_cache()->getProgram(d->device->context(),
+ FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
+
+ if (d->matrix.type() >= QTransform::TxProject) {
+ QGLPathMaskGenerator maskGenerator(path, d->matrix, d->offscreen, program);
+ d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
+ } else {
+ QGLRectMaskGenerator maskGenerator(path, d->matrix, d->offscreen, program);
+ d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
+ }
+
+ d->enableClipping();
+ }
+
+ if (d->has_pen) {
+ if (d->has_fast_pen)
+ d->strokeLines(path);
+ else
+ d->strokePath(path, false);
+ }
+ }
+ }
+}
+
+static void addQuadAsTriangle(GLfloat *quad, GLfloat *triangle)
+{
+ triangle[0] = quad[0];
+ triangle[1] = quad[1];
+
+ triangle[2] = quad[2];
+ triangle[3] = quad[3];
+
+ triangle[4] = quad[4];
+ triangle[5] = quad[5];
+
+ triangle[6] = quad[4];
+ triangle[7] = quad[5];
+
+ triangle[8] = quad[6];
+ triangle[9] = quad[7];
+
+ triangle[10] = quad[0];
+ triangle[11] = quad[1];
+}
+
+void QOpenGLPaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+ Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
+ QT_PointF fp[256];
+ while (pointCount) {
+ int i = 0;
+ while (i < pointCount && i < 256) {
+ fp[i].x = points[i].x();
+ fp[i].y = points[i].y();
+ ++i;
+ }
+ drawPoints((QPointF *)(void *)fp, i);
+ points += i;
+ pointCount -= i;
+ }
+}
+
+void QOpenGLPaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (d->use_emulation) {
+ QPaintEngineEx::drawPoints(points, pointCount);
+ return;
+ }
+
+ d->setGradientOps(d->cpen.brush(), QRectF());
+
+ if (!d->cpen.isCosmetic() || d->high_quality_antialiasing) {
+ Qt::PenCapStyle capStyle = d->cpen.capStyle();
+ if (capStyle == Qt::FlatCap)
+ d->cpen.setCapStyle(Qt::SquareCap);
+ QPaintEngine::drawPoints(points, pointCount);
+ d->cpen.setCapStyle(capStyle);
+ return;
+ }
+
+ d->flushDrawQueue();
+
+ if (d->has_fast_pen) {
+ QVarLengthArray<GLfloat> vertexArray(6 * pointCount);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ int j = 0;
+ for (int i = 0; i < pointCount; ++i) {
+ QPointF mapped = d->matrix.map(points[i]);
+
+ GLfloat x = GLfloat(qRound(mapped.x()));
+ GLfloat y = GLfloat(qRound(mapped.y()));
+
+ vertexArray[j++] = x;
+ vertexArray[j++] = y - 0.5f;
+
+ vertexArray[j++] = x + 1.5f;
+ vertexArray[j++] = y + 1.0f;
+
+ vertexArray[j++] = x;
+ vertexArray[j++] = y + 1.0f;
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray.constData());
+ glDrawArrays(GL_TRIANGLES, 0, pointCount*3);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glPopMatrix();
+ return;
+ }
+
+ const qreal *vertexArray = reinterpret_cast<const qreal*>(&points[0]);
+
+ if (sizeof(qreal) == sizeof(double)) {
+ Q_ASSERT(sizeof(QPointF) == 16);
+ glVertexPointer(2, GL_DOUBLE, 0, vertexArray);
+ }
+ else {
+ Q_ASSERT(sizeof(QPointF) == 8);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDrawArrays(GL_POINTS, 0, pointCount);
+ glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+void QOpenGLPaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+ struct PointF {
+ qreal x;
+ qreal y;
+ };
+ struct LineF {
+ PointF p1;
+ PointF p2;
+ };
+ Q_ASSERT(sizeof(PointF) == sizeof(QPointF));
+ Q_ASSERT(sizeof(LineF) == sizeof(QLineF));
+ LineF fl[256];
+ while (lineCount) {
+ int i = 0;
+ while (i < lineCount && i < 256) {
+ fl[i].p1.x = lines[i].x1();
+ fl[i].p1.y = lines[i].y1();
+ fl[i].p2.x = lines[i].x2();
+ fl[i].p2.y = lines[i].y2();
+ ++i;
+ }
+ drawLines((QLineF *)(void *)fl, i);
+ lines += i;
+ lineCount -= i;
+ }
+}
+
+void QOpenGLPaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (d->use_emulation) {
+ QPaintEngineEx::drawLines(lines, lineCount);
+ return;
+ }
+
+ if (d->has_pen) {
+ QOpenGLCoordinateOffset offset(d);
+ if (d->has_fast_pen && !d->high_quality_antialiasing) {
+ //### gradient resolving on lines isn't correct
+ d->setGradientOps(d->cpen.brush(), QRectF());
+
+ bool useRects = false;
+ // scale or 90 degree rotation?
+ if (d->matrix.type() <= QTransform::TxTranslate
+ || (!d->cpen.isCosmetic()
+ && (d->matrix.type() <= QTransform::TxScale
+ || (d->matrix.type() == QTransform::TxRotate
+ && d->matrix.m11() == 0 && d->matrix.m22() == 0)))) {
+ useRects = true;
+ for (int i = 0; i < lineCount; ++i) {
+ if (lines[i].p1().x() != lines[i].p2().x()
+ && lines[i].p1().y() != lines[i].p2().y()) {
+ useRects = false;
+ break;
+ }
+ }
+ }
+
+ GLfloat endCap = d->cpen.capStyle() == Qt::FlatCap ? 0.0f : 0.5f;
+ if (useRects) {
+ QVarLengthArray<GLfloat> vertexArray(12 * lineCount);
+
+ GLfloat quad[8];
+ for (int i = 0; i < lineCount; ++i) {
+ GLfloat x1 = lines[i].x1();
+ GLfloat x2 = lines[i].x2();
+ GLfloat y1 = lines[i].y1();
+ GLfloat y2 = lines[i].y2();
+
+ if (x1 == x2) {
+ if (y1 > y2)
+ qSwap(y1, y2);
+
+ quad[0] = x1 - 0.5f;
+ quad[1] = y1 - endCap;
+
+ quad[2] = x1 + 0.5f;
+ quad[3] = y1 - endCap;
+
+ quad[4] = x1 + 0.5f;
+ quad[5] = y2 + endCap;
+
+ quad[6] = x1 - 0.5f;
+ quad[7] = y2 + endCap;
+ } else {
+ if (x1 > x2)
+ qSwap(x1, x2);
+
+ quad[0] = x1 - endCap;
+ quad[1] = y1 + 0.5f;
+
+ quad[2] = x1 - endCap;
+ quad[3] = y1 - 0.5f;
+
+ quad[4] = x2 + endCap;
+ quad[5] = y1 - 0.5f;
+
+ quad[6] = x2 + endCap;
+ quad[7] = y1 + 0.5f;
+ }
+
+ addQuadAsTriangle(quad, &vertexArray[12*i]);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray.constData());
+ glDrawArrays(GL_TRIANGLES, 0, lineCount*6);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ } else {
+ QVarLengthArray<GLfloat> vertexArray(4 * lineCount);
+ for (int i = 0; i < lineCount; ++i) {
+ const QPointF a = lines[i].p1();
+ vertexArray[4*i] = lines[i].x1();
+ vertexArray[4*i+1] = lines[i].y1();
+ vertexArray[4*i+2] = lines[i].x2();
+ vertexArray[4*i+3] = lines[i].y2();
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray.constData());
+ glDrawArrays(GL_LINES, 0, lineCount*2);
+
+ glVertexPointer(2, GL_FLOAT, 4*sizeof(GLfloat), vertexArray.constData() + 2);
+ glDrawArrays(GL_POINTS, 0, lineCount);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ }
+ } else {
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+ for (int i=0; i<lineCount; ++i) {
+ const QLineF &l = lines[i];
+
+ if (l.p1() == l.p2()) {
+ if (d->cpen.capStyle() != Qt::FlatCap) {
+ QPointF p = l.p1();
+ drawPoints(&p, 1);
+ }
+ continue;
+ }
+
+ path.moveTo(l.x1(), l.y1());
+ path.lineTo(l.x2(), l.y2());
+ }
+
+ if (d->has_fast_pen && d->high_quality_antialiasing)
+ d->strokeLines(path);
+ else
+ d->strokePath(path, false);
+ }
+ }
+}
+
+void QOpenGLPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
+ QVarLengthArray<QT_PointF> p(pointCount);
+ for (int i=0; i<pointCount; ++i) {
+ p[i].x = points[i].x();
+ p[i].y = points[i].y();
+ }
+ drawPolygon((QPointF *)p.data(), pointCount, mode);
+}
+
+void QOpenGLPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QOpenGLPaintEngine);
+ if(pointCount < 2)
+ return;
+
+ if (d->use_emulation) {
+ QPaintEngineEx::drawPolygon(points, pointCount, mode);
+ return;
+ }
+
+ QRectF bounds;
+ if ((mode == ConvexMode && !d->high_quality_antialiasing && state()->brushNeedsResolving()) ||
+ ((d->has_fast_pen && !d->high_quality_antialiasing) && state()->penNeedsResolving())) {
+ qreal minx = points[0].x(), miny = points[0].y(),
+ maxx = points[0].x(), maxy = points[0].y();
+ for (int i = 1; i < pointCount; ++i) {
+ const QPointF &pt = points[i];
+ if (minx > pt.x())
+ minx = pt.x();
+ if (miny > pt.y())
+ miny = pt.y();
+ if (maxx < pt.x())
+ maxx = pt.x();
+ if (maxy < pt.y())
+ maxy = pt.y();
+ }
+ bounds = QRectF(minx, maxx, maxx-minx, maxy-miny);
+ }
+
+ QOpenGLCoordinateOffset offset(d);
+
+ if (d->has_brush && mode != PolylineMode) {
+ if (mode == ConvexMode && !d->high_quality_antialiasing) {
+ //### resolving on polygon from points isn't correct
+ d->setGradientOps(d->cbrush, bounds);
+
+ const qreal *vertexArray = reinterpret_cast<const qreal*>(&points[0]);
+
+ if (sizeof(qreal) == sizeof(double)) {
+ Q_ASSERT(sizeof(QPointF) == 16);
+ glVertexPointer(2, GL_DOUBLE, 0, vertexArray);
+ }
+ else {
+ Q_ASSERT(sizeof(QPointF) == 8);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, pointCount);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ } else {
+ QPainterPath path;
+ path.setFillRule(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
+ path.moveTo(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ path.lineTo(points[i]);
+ d->fillPath(path);
+ }
+ }
+
+ if (d->has_pen) {
+ if (d->has_fast_pen && !d->high_quality_antialiasing) {
+ d->setGradientOps(d->cpen.brush(), bounds);
+ QVarLengthArray<GLfloat> vertexArray(pointCount*2 + 2);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray.constData());
+ int i;
+ for (i=0; i<pointCount; ++i) {
+ vertexArray[i*2] = points[i].x();
+ vertexArray[i*2+1] = points[i].y();
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ if (mode != PolylineMode) {
+ vertexArray[i*2] = vertexArray[0];
+ vertexArray[i*2+1] = vertexArray[1];
+ glDrawArrays(GL_LINE_STRIP, 0, pointCount+1);
+ } else {
+ glDrawArrays(GL_LINE_STRIP, 0, pointCount);
+ glDrawArrays(GL_POINTS, pointCount-1, 1);
+ }
+ glDisableClientState(GL_VERTEX_ARRAY);
+ } else {
+ QPainterPath path(points[0]);
+ for (int i = 1; i < pointCount; ++i)
+ path.lineTo(points[i]);
+ if (mode != PolylineMode)
+ path.lineTo(points[0]);
+
+ if (d->has_fast_pen)
+ d->strokeLines(path);
+ else
+ d->strokePath(path, true);
+ }
+ }
+}
+
+void QOpenGLPaintEnginePrivate::strokeLines(const QPainterPath &path)
+{
+ DEBUG_ONCE_STR("QOpenGLPaintEnginePrivate::strokeLines()");
+
+ qreal penWidth = cpen.widthF();
+
+ GLuint program = qt_gl_program_cache()->getProgram(device->context(),
+ FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
+ QGLLineMaskGenerator maskGenerator(path, matrix, penWidth == 0 ? 1.0 : penWidth,
+ offscreen, program);
+
+ disableClipping();
+
+ QBrush temp = cbrush;
+ QPointF origin = brush_origin;
+
+ cbrush = cpen.brush();
+ brush_origin = QPointF();
+
+ addItem(qt_mask_texture_cache()->getMask(maskGenerator, this));
+
+ cbrush = temp;
+ brush_origin = origin;
+
+ enableClipping();
+}
+
+Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
+
+void QOpenGLPaintEnginePrivate::strokePath(const QPainterPath &path, bool use_cache)
+{
+ QBrush old_brush = cbrush;
+ cbrush = cpen.brush();
+
+ qreal txscale = 1;
+ if (cpen.isCosmetic() || (qt_scaleForTransform(matrix, &txscale) && txscale != 1)) {
+ QTransform temp = matrix;
+ matrix = QTransform();
+ glPushMatrix();
+
+ if (has_antialiasing) {
+ glLoadIdentity();
+ } else {
+ float offs_matrix[] =
+ { 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0.5, 0.5, 0, 1 };
+ glLoadMatrixf(offs_matrix);
+ }
+
+ QPen pen = cpen;
+ if (txscale != 1)
+ pen.setWidthF(pen.widthF() * txscale);
+ if (use_cache)
+ fillPath(qt_opengl_stroke_cache()->getStrokedPath(temp.map(path), pen));
+ else
+ fillPath(strokeForPath(temp.map(path), pen));
+
+ glPopMatrix();
+ matrix = temp;
+ } else if (use_cache) {
+ fillPath(qt_opengl_stroke_cache()->getStrokedPath(path, cpen));
+ } else {
+ fillPath(strokeForPath(path, cpen));
+ }
+
+ cbrush = old_brush;
+}
+
+void QOpenGLPaintEnginePrivate::strokePathFastPen(const QPainterPath &path, bool needsResolving)
+{
+#ifndef QT_OPENGL_ES
+ QRectF bounds;
+ if (needsResolving)
+ bounds = path.controlPointRect();
+ setGradientOps(cpen.brush(), bounds);
+
+ QBezier beziers[32];
+ for (int i=0; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ if (i != 0)
+ glEnd(); // GL_LINE_STRIP
+ glBegin(GL_LINE_STRIP);
+ glVertex2d(e.x, e.y);
+
+ break;
+ case QPainterPath::LineToElement:
+ glVertex2d(e.x, e.y);
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ QPointF sp = path.elementAt(i-1);
+ QPointF cp2 = path.elementAt(i+1);
+ QPointF ep = path.elementAt(i+2);
+ i+=2;
+
+ qreal inverseScaleHalf = inverseScale / 2;
+ beziers[0] = QBezier::fromPoints(sp, e, cp2, ep);
+ QBezier *b = beziers;
+ while (b >= beziers) {
+ // check if we can pop the top bezier curve from the stack
+ qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
+ qreal d;
+ if (l > inverseScale) {
+ d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2)
+ - (b->y4 - b->y1)*(b->x1 - b->x2) )
+ + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3)
+ - (b->y4 - b->y1)*(b->x1 - b->x3) );
+ d /= l;
+ } else {
+ d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
+ qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
+ }
+ if (d < inverseScaleHalf || b == beziers + 31) {
+ // good enough, we pop it off and add the endpoint
+ glVertex2d(b->x4, b->y4);
+ --b;
+ } else {
+ // split, second half of the polygon goes lower into the stack
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+ } // case CurveToElement
+ default:
+ break;
+ } // end of switch
+ }
+ glEnd(); // GL_LINE_STRIP
+#else
+ // have to use vertex arrays on embedded
+ QRectF bounds;
+ if (needsResolving)
+ bounds = path.controlPointRect();
+ setGradientOps(cpen.brush(), bounds);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ tess_points.reset();
+ QBezier beziers[32];
+ for (int i=0; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ if (i != 0) {
+ glVertexPointer(2, GL_FLOAT, 0, tess_points.data());
+ glDrawArrays(GL_LINE_STRIP, 0, tess_points.size());
+ tess_points.reset();
+ }
+ tess_points.add(QPointF(e.x, e.y));
+
+ break;
+ case QPainterPath::LineToElement:
+ tess_points.add(QPointF(e.x, e.y));
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ QPointF sp = path.elementAt(i-1);
+ QPointF cp2 = path.elementAt(i+1);
+ QPointF ep = path.elementAt(i+2);
+ i+=2;
+
+ qreal inverseScaleHalf = inverseScale / 2;
+ beziers[0] = QBezier::fromPoints(sp, e, cp2, ep);
+ QBezier *b = beziers;
+ while (b >= beziers) {
+ // check if we can pop the top bezier curve from the stack
+ qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
+ qreal d;
+ if (l > inverseScale) {
+ d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2)
+ - (b->y4 - b->y1)*(b->x1 - b->x2) )
+ + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3)
+ - (b->y4 - b->y1)*(b->x1 - b->x3) );
+ d /= l;
+ } else {
+ d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
+ qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
+ }
+ if (d < inverseScaleHalf || b == beziers + 31) {
+ // good enough, we pop it off and add the endpoint
+ tess_points.add(QPointF(b->x4, b->y4));
+ --b;
+ } else {
+ // split, second half of the polygon goes lower into the stack
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+ } // case CurveToElement
+ default:
+ break;
+ } // end of switch
+ }
+ glVertexPointer(2, GL_FLOAT, 0, tess_points.data());
+ glDrawArrays(GL_LINE_STRIP, 0, tess_points.size());
+ glDisableClientState(GL_VERTEX_ARRAY);
+#endif
+}
+
+static bool pathClosed(const QPainterPath &path)
+{
+ QPointF lastMoveTo = path.elementAt(0);
+ QPointF lastPoint = lastMoveTo;
+
+ for (int i = 1; i < path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ if (lastMoveTo != lastPoint)
+ return false;
+ lastMoveTo = lastPoint = e;
+ break;
+ case QPainterPath::LineToElement:
+ lastPoint = e;
+ break;
+ case QPainterPath::CurveToElement:
+ lastPoint = path.elementAt(i + 2);
+ i+=2;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return lastMoveTo == lastPoint;
+}
+
+void QOpenGLPaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (path.isEmpty())
+ return;
+
+ if (d->use_emulation) {
+ QPaintEngineEx::drawPath(path);
+ return;
+ }
+
+ QOpenGLCoordinateOffset offset(d);
+
+ if (d->has_brush) {
+ bool path_closed = pathClosed(path);
+
+ bool has_thick_pen =
+ path_closed
+ && d->has_pen
+ && d->cpen.style() == Qt::SolidLine
+ && d->cpen.isSolid()
+ && d->cpen.color().alpha() == 255
+ && d->txop < QTransform::TxProject
+ && d->cpen.widthF() >= 2 / qSqrt(qMin(d->matrix.m11() * d->matrix.m11()
+ + d->matrix.m21() * d->matrix.m21(),
+ d->matrix.m12() * d->matrix.m12()
+ + d->matrix.m22() * d->matrix.m22()));
+
+ if (has_thick_pen) {
+ DEBUG_ONCE qDebug() << "QOpenGLPaintEngine::drawPath(): Using thick pen optimization, style:" << d->cbrush.style();
+
+ d->flushDrawQueue();
+
+ bool temp = d->high_quality_antialiasing;
+ d->high_quality_antialiasing = false;
+
+ updateCompositionMode(d->composition_mode);
+
+ d->fillPath(path);
+
+ d->high_quality_antialiasing = temp;
+ updateCompositionMode(d->composition_mode);
+ } else {
+ d->fillPath(path);
+ }
+ }
+
+ if (d->has_pen) {
+ if (d->has_fast_pen && !d->high_quality_antialiasing)
+ d->strokePathFastPen(path, state()->penNeedsResolving());
+ else
+ d->strokePath(path, true);
+ }
+}
+
+void QOpenGLPaintEnginePrivate::drawImageAsPath(const QRectF &r, const QImage &img, const QRectF &sr)
+{
+ QBrush old_brush = cbrush;
+ QPointF old_brush_origin = brush_origin;
+
+ qreal scaleX = r.width() / sr.width();
+ qreal scaleY = r.height() / sr.height();
+
+ QTransform brush_matrix = QTransform::fromTranslate(r.left(), r.top());
+ brush_matrix.scale(scaleX, scaleY);
+ brush_matrix.translate(-sr.left(), -sr.top());
+
+ cbrush = QBrush(img);
+ cbrush.setTransform(brush_matrix);
+ brush_origin = QPointF();
+
+ QPainterPath p;
+ p.addRect(r);
+ fillPath(p);
+
+ cbrush = old_brush;
+ brush_origin = old_brush_origin;
+}
+
+void QOpenGLPaintEnginePrivate::drawTiledImageAsPath(const QRectF &r, const QImage &img, qreal sx, qreal sy,
+ const QPointF &offset)
+{
+ QBrush old_brush = cbrush;
+ QPointF old_brush_origin = brush_origin;
+
+ QTransform brush_matrix = QTransform::fromTranslate(r.left(), r.top());
+ brush_matrix.scale(sx, sy);
+ brush_matrix.translate(-offset.x(), -offset.y());
+
+ cbrush = QBrush(img);
+ cbrush.setTransform(brush_matrix);
+ brush_origin = QPointF();
+
+ QPainterPath p;
+ p.addRect(r);
+ fillPath(p);
+
+ cbrush = old_brush;
+ brush_origin = old_brush_origin;
+}
+
+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);
+}
+
+template <typename T>
+static const T qSubImage(const T &image, const QRectF &src, QRectF *srcNew)
+{
+ const int sx1 = qMax(0, qFloor(src.left()));
+ const int sy1 = qMax(0, qFloor(src.top()));
+ const int sx2 = qMin(image.width(), qCeil(src.right()));
+ const int sy2 = qMin(image.height(), qCeil(src.bottom()));
+
+ const T sub = image.copy(sx1, sy1, sx2 - sx1, sy2 - sy1);
+
+ if (srcNew)
+ *srcNew = src.translated(-sx1, -sy1);
+
+ return sub;
+}
+
+void QOpenGLPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QOpenGLPaintEngine);
+ if (pm.depth() == 1) {
+ QPixmap tpx(pm.size());
+ tpx.fill(Qt::transparent);
+ QPainter p(&tpx);
+ p.setPen(d->cpen);
+ p.drawPixmap(0, 0, pm);
+ p.end();
+ drawPixmap(r, tpx, sr);
+ return;
+ }
+
+ const int sz = d->max_texture_size;
+ if (pm.width() > sz || pm.height() > sz) {
+ QRectF subsr;
+ const QPixmap sub = qSubImage(pm, sr, &subsr);
+
+ if (sub.width() <= sz && sub.height() <= sz) {
+ drawPixmap(r, sub, subsr);
+ } else {
+ const QPixmap scaled = sub.scaled(sz, sz, Qt::KeepAspectRatio);
+ const qreal sx = scaled.width() / qreal(sub.width());
+ const qreal sy = scaled.height() / qreal(sub.height());
+
+ drawPixmap(r, scaled, scaleRect(subsr, sx, sy));
+ }
+ return;
+ }
+
+
+ if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r)))
+ d->drawImageAsPath(r, pm.toImage(), sr);
+ else {
+ GLenum target = qt_gl_preferredTextureTarget();
+ d->flushDrawQueue();
+ QGLTexture *tex =
+ d->device->context()->d_func()->bindTexture(pm, target, GL_RGBA,
+ QGLContext::InternalBindOption);
+ drawTextureRect(pm.width(), pm.height(), r, sr, target, tex);
+ }
+}
+
+void QOpenGLPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &offset)
+{
+ Q_D(QOpenGLPaintEngine);
+ if (pm.depth() == 1) {
+ QPixmap tpx(pm.size());
+ tpx.fill(Qt::transparent);
+ QPainter p(&tpx);
+ p.setPen(d->cpen);
+ p.drawPixmap(0, 0, pm);
+ p.end();
+ drawTiledPixmap(r, tpx, offset);
+ return;
+ }
+
+ QImage scaled;
+ const int sz = d->max_texture_size;
+ if (pm.width() > sz || pm.height() > sz) {
+ int rw = qCeil(r.width());
+ int rh = qCeil(r.height());
+ if (rw < pm.width() && rh < pm.height()) {
+ drawTiledPixmap(r, pm.copy(0, 0, rw, rh), offset);
+ return;
+ }
+
+ scaled = pm.toImage().scaled(sz, sz, Qt::KeepAspectRatio);
+ }
+
+ if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r))) {
+ if (scaled.isNull())
+ d->drawTiledImageAsPath(r, pm.toImage(), 1, 1, offset);
+ else {
+ const qreal sx = pm.width() / qreal(scaled.width());
+ const qreal sy = pm.height() / qreal(scaled.height());
+ d->drawTiledImageAsPath(r, scaled, sx, sy, offset);
+ }
+ } else {
+ d->flushDrawQueue();
+
+ QGLTexture *tex;
+ if (scaled.isNull())
+ tex = d->device->context()->d_func()->bindTexture(pm, GL_TEXTURE_2D, GL_RGBA,
+ QGLContext::InternalBindOption);
+ else
+ tex = d->device->context()->d_func()->bindTexture(scaled, GL_TEXTURE_2D, GL_RGBA,
+ QGLContext::InternalBindOption);
+ updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, d->use_smooth_pixmap_transform);
+
+#ifndef QT_OPENGL_ES
+ glPushAttrib(GL_CURRENT_BIT);
+ glDisable(GL_TEXTURE_GEN_S);
+#endif
+ glColor4f(d->opacity, d->opacity, d->opacity, d->opacity);
+ glEnable(GL_TEXTURE_2D);
+
+ GLdouble tc_w = r.width()/pm.width();
+ GLdouble tc_h = r.height()/pm.height();
+
+ // Rotate the texture so that it is aligned correctly and the
+ // wrapping is done correctly
+ if (tex->options & QGLContext::InvertedYBindOption) {
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ glRotatef(180.0, 0.0, 1.0, 0.0);
+ glRotatef(180.0, 0.0, 0.0, 1.0);
+ }
+
+ GLfloat vertexArray[4*2];
+ GLfloat texCoordArray[4*2];
+
+ double offset_x = offset.x() / pm.width();
+ double offset_y = offset.y() / pm.height();
+
+ qt_add_rect_to_array(r, vertexArray);
+ qt_add_texcoords_to_array(offset_x, offset_y,
+ tc_w + offset_x, tc_h + offset_y, texCoordArray);
+
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ if (tex->options & QGLContext::InvertedYBindOption)
+ glPopMatrix();
+
+ glDisable(GL_TEXTURE_2D);
+#ifndef QT_OPENGL_ES
+ glPopAttrib();
+#endif
+ }
+}
+
+void QOpenGLPaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ const int sz = d->max_texture_size;
+ if (image.width() > sz || image.height() > sz) {
+ QRectF subsr;
+ const QImage sub = qSubImage(image, sr, &subsr);
+
+ if (sub.width() <= sz && sub.height() <= sz) {
+ drawImage(r, sub, subsr, 0);
+ } else {
+ const QImage scaled = sub.scaled(sz, sz, Qt::KeepAspectRatio);
+ const qreal sx = scaled.width() / qreal(sub.width());
+ const qreal sy = scaled.height() / qreal(sub.height());
+
+ drawImage(r, scaled, scaleRect(subsr, sx, sy), 0);
+ }
+ return;
+ }
+
+ if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r)))
+ d->drawImageAsPath(r, image, sr);
+ else {
+ GLenum target = qt_gl_preferredTextureTarget();
+ d->flushDrawQueue();
+ QGLTexture *tex =
+ d->device->context()->d_func()->bindTexture(image, target, GL_RGBA,
+ QGLContext::InternalBindOption);
+ drawTextureRect(image.width(), image.height(), r, sr, target, tex);
+ }
+}
+
+void QOpenGLPaintEngine::drawTextureRect(int tx_width, int tx_height, const QRectF &r,
+ const QRectF &sr, GLenum target, QGLTexture *tex)
+{
+ Q_D(QOpenGLPaintEngine);
+#ifndef QT_OPENGL_ES
+ glPushAttrib(GL_CURRENT_BIT);
+ glDisable(GL_TEXTURE_GEN_S);
+#endif
+ glColor4f(d->opacity, d->opacity, d->opacity, d->opacity);
+ glEnable(target);
+ updateTextureFilter(target, GL_CLAMP_TO_EDGE, d->use_smooth_pixmap_transform);
+
+ qreal x1, x2, y1, y2;
+ if (target == GL_TEXTURE_2D) {
+ x1 = sr.x() / tx_width;
+ x2 = x1 + sr.width() / tx_width;
+ if (tex->options & QGLContext::InvertedYBindOption) {
+ y1 = 1 - (sr.bottom() / tx_height);
+ y2 = 1 - (sr.y() / tx_height);
+ } else {
+ y1 = sr.bottom() / tx_height;
+ y2 = sr.y() / tx_height;
+ }
+ } else {
+ x1 = sr.x();
+ x2 = sr.right();
+ y1 = sr.bottom();
+ y2 = sr.y();
+ }
+
+ GLfloat vertexArray[4*2];
+ GLfloat texCoordArray[4*2];
+
+ qt_add_rect_to_array(r, vertexArray);
+ qt_add_texcoords_to_array(x1, y2, x2, y1, texCoordArray);
+
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glDisable(target);
+#ifndef QT_OPENGL_ES
+ glPopAttrib();
+#endif
+}
+
+#ifdef Q_WS_WIN
+HDC
+#else
+Qt::HANDLE
+#endif
+QOpenGLPaintEngine::handle() const
+{
+ return 0;
+}
+
+static const int x_margin = 1;
+static const int y_margin = 0;
+
+struct QGLGlyphCoord {
+ // stores the offset and size of a glyph texture
+ qreal x;
+ qreal y;
+ qreal width;
+ qreal height;
+ qreal log_width;
+ qreal log_height;
+ QFixed x_offset;
+ QFixed y_offset;
+};
+
+struct QGLFontTexture {
+ int x_offset; // glyph offset within the
+ int y_offset;
+ GLuint texture;
+ int width;
+ int height;
+};
+
+typedef QHash<glyph_t, QGLGlyphCoord*> QGLGlyphHash;
+typedef QHash<QFontEngine*, QGLGlyphHash*> QGLFontGlyphHash;
+typedef QHash<quint64, QGLFontTexture*> QGLFontTexHash;
+typedef QHash<const QGLContext*, QGLFontGlyphHash*> QGLContextHash;
+
+static inline void qt_delete_glyph_hash(QGLGlyphHash *hash)
+{
+ qDeleteAll(*hash);
+ delete hash;
+}
+
+class QGLGlyphCache : public QObject
+{
+ Q_OBJECT
+public:
+ QGLGlyphCache() : QObject(0) { current_cache = 0; }
+ ~QGLGlyphCache();
+ QGLGlyphCoord *lookup(QFontEngine *, glyph_t);
+ void cacheGlyphs(QGLContext *, QFontEngine *, glyph_t *glyphs, int numGlyphs);
+ void cleanCache();
+ void allocTexture(int width, int height, GLuint texture);
+
+public slots:
+ void cleanupContext(const QGLContext *);
+ void fontEngineDestroyed(QObject *);
+ void widgetDestroyed(QObject *);
+
+protected:
+ QGLGlyphHash *current_cache;
+ QGLFontTexHash qt_font_textures;
+ QGLContextHash qt_context_cache;
+};
+
+QGLGlyphCache::~QGLGlyphCache()
+{
+// qDebug() << "cleaning out the QGLGlyphCache";
+ cleanCache();
+}
+
+void QGLGlyphCache::fontEngineDestroyed(QObject *o)
+{
+// qDebug() << "fontEngineDestroyed()";
+ QFontEngine *fe = static_cast<QFontEngine *>(o); // safe, since only the type is used
+ QList<const QGLContext *> keys = qt_context_cache.keys();
+ const QGLContext *ctx = 0;
+
+ for (int i=0; i < keys.size(); ++i) {
+ QGLFontGlyphHash *font_cache = qt_context_cache.value(keys.at(i));
+ if (font_cache->find(fe) != font_cache->end()) {
+ ctx = keys.at(i);
+ QGLGlyphHash *cache = font_cache->take(fe);
+ qt_delete_glyph_hash(cache);
+ break;
+ }
+ }
+
+ quint64 font_key = (reinterpret_cast<quint64>(ctx) << 32) | reinterpret_cast<quint64>(fe);
+ QGLFontTexture *tex = qt_font_textures.take(font_key);
+ if (tex) {
+#ifdef Q_WS_MAC
+ if (
+# ifndef QT_MAC_USE_COCOA
+ aglGetCurrentContext() != 0
+# else
+ qt_current_nsopengl_context() != 0
+# endif
+ )
+#endif
+ glDeleteTextures(1, &tex->texture);
+ delete tex;
+ }
+}
+
+void QGLGlyphCache::widgetDestroyed(QObject *)
+{
+// qDebug() << "widget destroyed";
+ cleanCache(); // ###
+}
+
+void QGLGlyphCache::cleanupContext(const QGLContext *ctx)
+{
+// qDebug() << "==> cleaning for: " << hex << ctx;
+ QGLFontGlyphHash *font_cache = qt_context_cache.take(ctx);
+
+ if (font_cache) {
+ QList<QFontEngine *> keys = font_cache->keys();
+ for (int i=0; i < keys.size(); ++i) {
+ QFontEngine *fe = keys.at(i);
+ qt_delete_glyph_hash(font_cache->take(fe));
+ quint64 font_key = (reinterpret_cast<quint64>(ctx) << 32) | reinterpret_cast<quint64>(fe);
+ QGLFontTexture *font_tex = qt_font_textures.take(font_key);
+ if (font_tex) {
+#ifdef Q_WS_MAC
+ if (
+# ifndef QT_MAC_USE_COCOA
+ aglGetCurrentContext() == 0
+# else
+ qt_current_nsopengl_context() != 0
+# endif
+ )
+#endif
+ glDeleteTextures(1, &font_tex->texture);
+ delete font_tex;
+ }
+ }
+ delete font_cache;
+ }
+// qDebug() << "<=== done cleaning, num tex:" << qt_font_textures.size() << "num ctx:" << qt_context_cache.size();
+}
+
+void QGLGlyphCache::cleanCache()
+{
+ QGLFontTexHash::const_iterator it = qt_font_textures.constBegin();
+ if (QGLContext::currentContext()) {
+ while (it != qt_font_textures.constEnd()) {
+#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
+ if (qt_current_nsopengl_context() == 0)
+ break;
+#endif
+ glDeleteTextures(1, &it.value()->texture);
+ ++it;
+ }
+ }
+ qDeleteAll(qt_font_textures);
+ qt_font_textures.clear();
+
+ QList<const QGLContext *> keys = qt_context_cache.keys();
+ for (int i=0; i < keys.size(); ++i) {
+ QGLFontGlyphHash *font_cache = qt_context_cache.value(keys.at(i));
+ QGLFontGlyphHash::Iterator it = font_cache->begin();
+ for (; it != font_cache->end(); ++it)
+ qt_delete_glyph_hash(it.value());
+ font_cache->clear();
+ }
+ qDeleteAll(qt_context_cache);
+ qt_context_cache.clear();
+}
+
+void QGLGlyphCache::allocTexture(int width, int height, GLuint texture)
+{
+ uchar *tex_data = (uchar *) malloc(width*height*2);
+ memset(tex_data, 0, width*height*2);
+ glBindTexture(GL_TEXTURE_2D, texture);
+#ifndef QT_OPENGL_ES
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8_ALPHA8,
+ width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
+#else
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
+ width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
+#endif
+ free(tex_data);
+}
+
+#if 0
+// useful for debugging the glyph cache
+static QImage getCurrentTexture(const QColor &color, QGLFontTexture *font_tex)
+{
+ ushort *old_tex_data = (ushort *) malloc(font_tex->width*font_tex->height*2);
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
+ QImage im(font_tex->width, font_tex->height, QImage::Format_ARGB32);
+ for (int y=0; y<font_tex->height; ++y) {
+ for (int x=0; x<font_tex->width; ++x) {
+ im.setPixel(x, y, ((*(old_tex_data+x+y*font_tex->width)) << 24) | (0x00ffffff & color.rgb()));
+ }
+ }
+ delete old_tex_data;
+ return im;
+}
+#endif
+
+void QGLGlyphCache::cacheGlyphs(QGLContext *context, QFontEngine *fontEngine,
+ glyph_t *glyphs, int numGlyphs)
+{
+ QGLContextHash::const_iterator dev_it = qt_context_cache.constFind(context);
+ QGLFontGlyphHash *font_cache = 0;
+ const QGLContext *context_key = 0;
+
+ if (dev_it == qt_context_cache.constEnd()) {
+ // check for shared contexts
+ QList<const QGLContext *> contexts = qt_context_cache.keys();
+ for (int i=0; i<contexts.size(); ++i) {
+ const QGLContext *ctx = contexts.at(i);
+ if (ctx != context && QGLContext::areSharing(context, ctx)) {
+ context_key = ctx;
+ dev_it = qt_context_cache.constFind(context_key);
+ break;
+ }
+ }
+ }
+
+ if (dev_it == qt_context_cache.constEnd()) {
+ // no shared contexts either - create a new entry
+ font_cache = new QGLFontGlyphHash;
+// qDebug() << "new context" << context << font_cache;
+ qt_context_cache.insert(context, font_cache);
+ if (context->isValid()) {
+ if (context->device() && context->device()->devType() == QInternal::Widget) {
+ QWidget *widget = static_cast<QWidget *>(context->device());
+ connect(widget, SIGNAL(destroyed(QObject*)), SLOT(widgetDestroyed(QObject*)));
+ }
+ connect(QGLSignalProxy::instance(),
+ SIGNAL(aboutToDestroyContext(const QGLContext*)),
+ SLOT(cleanupContext(const QGLContext*)));
+ }
+ } else {
+ font_cache = dev_it.value();
+ }
+ Q_ASSERT(font_cache != 0);
+
+ QGLFontGlyphHash::const_iterator cache_it = font_cache->constFind(fontEngine);
+ QGLGlyphHash *cache = 0;
+ if (cache_it == font_cache->constEnd()) {
+ cache = new QGLGlyphHash;
+ font_cache->insert(fontEngine, cache);
+ connect(fontEngine, SIGNAL(destroyed(QObject*)), SLOT(fontEngineDestroyed(QObject*)));
+ } else {
+ cache = cache_it.value();
+ }
+ current_cache = cache;
+
+ quint64 font_key = (reinterpret_cast<quint64>(context_key ? context_key : context) << 32)
+ | reinterpret_cast<quint64>(fontEngine);
+ QGLFontTexHash::const_iterator it = qt_font_textures.constFind(font_key);
+ QGLFontTexture *font_tex;
+ if (it == qt_font_textures.constEnd()) {
+ GLuint font_texture;
+ glGenTextures(1, &font_texture);
+ GLint tex_height = qt_next_power_of_two(qRound(fontEngine->ascent().toReal() + fontEngine->descent().toReal())+2);
+ GLint tex_width = qt_next_power_of_two(tex_height*30); // ###
+ GLint max_tex_size;
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
+ Q_ASSERT(max_tex_size > 0);
+ if (tex_width > max_tex_size)
+ tex_width = max_tex_size;
+ allocTexture(tex_width, tex_height, font_texture);
+ font_tex = new QGLFontTexture;
+ font_tex->texture = font_texture;
+ font_tex->x_offset = x_margin;
+ font_tex->y_offset = y_margin;
+ font_tex->width = tex_width;
+ font_tex->height = tex_height;
+// qDebug() << "new font tex - width:" << tex_width << "height:"<< tex_height
+// << hex << "tex id:" << font_tex->texture << "key:" << font_key << "num cached:" << qt_font_textures.size();
+ qt_font_textures.insert(font_key, font_tex);
+ } else {
+ font_tex = it.value();
+ glBindTexture(GL_TEXTURE_2D, font_tex->texture);
+ }
+
+ for (int i=0; i< numGlyphs; ++i) {
+ QGLGlyphHash::const_iterator it = cache->constFind(glyphs[i]);
+ if (it == cache->constEnd()) {
+ // render new glyph and put it in the cache
+ glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
+ int glyph_width = qRound(metrics.width.toReal())+2;
+ int glyph_height = qRound(fontEngine->ascent().toReal() + fontEngine->descent().toReal())+2;
+
+ if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
+ int strip_height = qt_next_power_of_two(qRound(fontEngine->ascent().toReal() + fontEngine->descent().toReal())+2);
+ font_tex->x_offset = x_margin;
+ font_tex->y_offset += strip_height;
+ if (font_tex->y_offset >= font_tex->height) {
+ // get hold of the old font texture
+ uchar *old_tex_data = (uchar *) malloc(font_tex->width*font_tex->height*2);
+ int old_tex_height = font_tex->height;
+#ifndef QT_OPENGL_ES
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
+#endif
+
+ // realloc a larger texture
+ glDeleteTextures(1, &font_tex->texture);
+ glGenTextures(1, &font_tex->texture);
+ font_tex->height = qt_next_power_of_two(font_tex->height + strip_height);
+ allocTexture(font_tex->width, font_tex->height, font_tex->texture);
+
+ // write back the old texture data
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, font_tex->width, old_tex_height,
+ GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
+ free(old_tex_data);
+
+ // update the texture coords and the y offset for the existing glyphs in
+ // the cache, because of the texture size change
+ QGLGlyphHash::iterator it = cache->begin();
+ while (it != cache->end()) {
+ it.value()->height = (it.value()->height * old_tex_height) / font_tex->height;
+ it.value()->y = (it.value()->y * old_tex_height) / font_tex->height;
+ ++it;
+ }
+ }
+ }
+
+ QImage glyph_im(fontEngine->alphaMapForGlyph(glyphs[i]));
+ glyph_width = glyph_im.width();
+ Q_ASSERT(glyph_width >= 0);
+ // pad the glyph width to an even number
+ if (glyph_width%2 != 0)
+ ++glyph_width;
+
+ QGLGlyphCoord *qgl_glyph = new QGLGlyphCoord;
+ qgl_glyph->x = qreal(font_tex->x_offset) / font_tex->width;
+ qgl_glyph->y = qreal(font_tex->y_offset) / font_tex->height;
+ qgl_glyph->width = qreal(glyph_width) / font_tex->width;
+ qgl_glyph->height = qreal(glyph_height) / font_tex->height;
+ qgl_glyph->log_width = qreal(glyph_width);
+ qgl_glyph->log_height = qgl_glyph->height * font_tex->height;
+#ifdef Q_WS_MAC
+ qgl_glyph->x_offset = -metrics.x + 1;
+ qgl_glyph->y_offset = metrics.y - 2;
+#else
+ qgl_glyph->x_offset = -metrics.x;
+ qgl_glyph->y_offset = metrics.y;
+#endif
+
+ if (!glyph_im.isNull()) {
+ int idx = 0;
+ uchar *tex_data = (uchar *) malloc(glyph_width*glyph_im.height()*2);
+ memset(tex_data, 0, glyph_width*glyph_im.height()*2);
+
+ bool is8BitGray = false;
+#ifdef Q_WS_QPA
+ if (glyph_im.format() == QImage::Format_Indexed8) {
+ is8BitGray = true;
+ }
+#endif
+ glyph_im = glyph_im.convertToFormat(QImage::Format_Indexed8);
+ for (int y=0; y<glyph_im.height(); ++y) {
+ uchar *s = (uchar *) glyph_im.scanLine(y);
+ for (int x=0; x<glyph_im.width(); ++x) {
+ uchar alpha = is8BitGray ? *s : qAlpha(glyph_im.color(*s));
+ tex_data[idx] = alpha;
+ tex_data[idx+1] = alpha;
+ ++s;
+ idx += 2;
+ }
+ if (glyph_im.width()%2 != 0)
+ idx += 2;
+ }
+ glTexSubImage2D(GL_TEXTURE_2D, 0, font_tex->x_offset, font_tex->y_offset,
+ glyph_width, glyph_im.height(),
+ GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
+ free(tex_data);
+ }
+ if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
+ font_tex->x_offset = x_margin;
+ font_tex->y_offset += glyph_height + y_margin;
+ } else {
+ font_tex->x_offset += glyph_width + x_margin;
+ }
+
+ cache->insert(glyphs[i], qgl_glyph);
+ }
+ }
+}
+
+QGLGlyphCoord *QGLGlyphCache::lookup(QFontEngine *, glyph_t g)
+{
+ Q_ASSERT(current_cache != 0);
+ // ### careful here
+ QGLGlyphHash::const_iterator it = current_cache->constFind(g);
+ if (it == current_cache->constEnd())
+ return 0;
+ else
+ return it.value();
+}
+
+Q_GLOBAL_STATIC(QGLGlyphCache, qt_glyph_cache)
+
+//
+// assumption: the context that this is called for has to be the
+// current context
+//
+void qgl_cleanup_glyph_cache(QGLContext *ctx)
+{
+ qt_glyph_cache()->cleanupContext(ctx);
+}
+
+void QOpenGLPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ d->flushDrawQueue();
+
+ // make sure the glyphs we want to draw are in the cache
+ qt_glyph_cache()->cacheGlyphs(d->device->context(), textItem->fontEngine(), textItem->glyphs,
+ textItem->numGlyphs);
+
+ d->setGradientOps(Qt::SolidPattern, QRectF()); // turns off gradient ops
+ qt_glColor4ubv(d->pen_color);
+ glEnable(GL_TEXTURE_2D);
+
+#ifdef Q_WS_QWS
+ // XXX: it is necessary to disable alpha writes on GLES/embedded because we don't want
+ // text rendering to update the alpha in the window surface.
+ // XXX: This may not be needed as this behavior does seem to be caused by driver bug
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+#endif
+
+ // do the actual drawing
+ GLfloat vertexArray[4*2];
+ GLfloat texCoordArray[4*2];
+
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bool antialias = !(textItem->fontEngine()->fontDef.styleStrategy & QFont::NoAntialias)
+ && (d->matrix.type() > QTransform::TxTranslate);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, antialias ? GL_LINEAR : GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, antialias ? GL_LINEAR : GL_NEAREST);
+
+ for (int i=0; i< textItem->numGlyphs; ++i) {
+ QGLGlyphCoord *g = qt_glyph_cache()->lookup(textItem->fontEngine(), textItem->glyphs[i]);
+
+ // we don't cache glyphs with no width/height
+ if (!g)
+ continue;
+
+ qreal x1, x2, y1, y2;
+ x1 = g->x;
+ y1 = g->y;
+ x2 = x1 + g->width;
+ y2 = y1 + g->height;
+
+ QPointF logical_pos((textItem->glyphPositions[i].x - g->x_offset).toReal(),
+ (textItem->glyphPositions[i].y + g->y_offset).toReal());
+
+ qt_add_rect_to_array(QRectF(logical_pos, QSizeF(g->log_width, g->log_height)), vertexArray);
+ qt_add_texcoords_to_array(x1, y1, x2, y2, texCoordArray);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glDisable(GL_TEXTURE_2D);
+
+#ifdef Q_WS_QWS
+ // XXX: This may not be needed as this behavior does seem to be caused by driver bug
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+#endif
+
+}
+
+void QOpenGLPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+
+ // fall back to drawing a polygon if the scale factor is large, or
+ // we use a gradient pen
+ if ((d->matrix.det() > 1) || (d->pen_brush_style >= Qt::LinearGradientPattern
+ && d->pen_brush_style <= Qt::ConicalGradientPattern)) {
+ QPaintEngine::drawTextItem(p, textItem);
+ return;
+ }
+
+ // add the glyphs used to the glyph texture cache
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix = QTransform::fromTranslate(qRound(p.x()), qRound(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();
+ drawStaticTextItem(&staticTextItem);
+ }
+
+}
+
+
+void QOpenGLPaintEngine::drawEllipse(const QRectF &rect)
+{
+#ifndef Q_WS_QWS
+ Q_D(QOpenGLPaintEngine);
+
+ if (d->use_emulation) {
+ QPaintEngineEx::drawEllipse(rect);
+ return;
+ }
+
+ if (d->high_quality_antialiasing) {
+ if (d->has_brush) {
+ d->disableClipping();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ GLuint program = qt_gl_program_cache()->getProgram(d->device->context(),
+ FRAGMENT_PROGRAM_MASK_ELLIPSE_AA, 0, true);
+ QGLEllipseMaskGenerator maskGenerator(rect,
+ d->matrix,
+ d->offscreen,
+ program,
+ mask_variable_locations[FRAGMENT_PROGRAM_MASK_ELLIPSE_AA]);
+
+ d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
+
+ d->enableClipping();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ }
+
+ if (d->has_pen) {
+ QPainterPath path;
+ path.addEllipse(rect);
+
+ d->strokePath(path, false);
+ }
+ } else {
+ DEBUG_ONCE_STR("QOpenGLPaintEngine::drawEllipse(): falling back to drawPath()");
+
+ QPainterPath path;
+ path.addEllipse(rect);
+ drawPath(path);
+ }
+#else
+ QPaintEngineEx::drawEllipse(rect);
+#endif
+}
+
+
+void QOpenGLPaintEnginePrivate::updateFragmentProgramData(int locations[])
+{
+#ifdef Q_WS_QWS
+ Q_UNUSED(locations);
+#else
+ QGL_FUNC_CONTEXT;
+
+ QSize sz = offscreen.offscreenSize();
+
+ float inv_mask_size_data[4] = { 1.0f / sz.width(), 1.0f / sz.height(), 0.0f, 0.0f };
+
+ sz = drawable_texture_size;
+
+ float inv_dst_size_data[4] = { 1.0f / sz.width(), 1.0f / sz.height(), 0.0f, 0.0f };
+
+ // default inv size 0.125f == 1.0f / 8.0f for pattern brushes
+ float inv_brush_texture_size_data[4] = { 0.125f, 0.125f };
+
+ // texture patterns have their own size
+ if (current_style == Qt::TexturePattern) {
+ QSize sz = cbrush.texture().size();
+
+ inv_brush_texture_size_data[0] = 1.0f / sz.width();
+ inv_brush_texture_size_data[1] = 1.0f / sz.height();
+ }
+
+ for (unsigned int i = 0; i < num_fragment_variables; ++i) {
+ int location = locations[i];
+
+ if (location < 0)
+ continue;
+
+ switch (i) {
+ case VAR_ANGLE:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, angle_data);
+ break;
+ case VAR_LINEAR:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, linear_data);
+ break;
+ case VAR_FMP:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, fmp_data);
+ break;
+ case VAR_FMP2_M_RADIUS2:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, fmp2_m_radius2_data);
+ break;
+ case VAR_INV_MASK_SIZE:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_mask_size_data);
+ break;
+ case VAR_INV_DST_SIZE:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_dst_size_data);
+ break;
+ case VAR_INV_MATRIX_M0:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[0]);
+ break;
+ case VAR_INV_MATRIX_M1:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[1]);
+ break;
+ case VAR_INV_MATRIX_M2:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[2]);
+ break;
+ case VAR_PORTERDUFF_AB:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, porterduff_ab_data);
+ break;
+ case VAR_PORTERDUFF_XYZ:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, porterduff_xyz_data);
+ break;
+ case VAR_INV_BRUSH_TEXTURE_SIZE:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_brush_texture_size_data);
+ break;
+ case VAR_MASK_OFFSET:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, mask_offset_data);
+ break;
+ case VAR_MASK_CHANNEL:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, mask_channel_data);
+ break;
+ case VAR_DST_TEXTURE:
+ case VAR_MASK_TEXTURE:
+ case VAR_PALETTE:
+ case VAR_BRUSH_TEXTURE:
+ // texture variables, not handled here
+ break;
+ default:
+ qDebug() << "QOpenGLPaintEnginePrivate: Unhandled fragment variable:" << i;
+ }
+ }
+#endif
+}
+
+
+void QOpenGLPaintEnginePrivate::copyDrawable(const QRectF &rect)
+{
+#ifdef Q_WS_QWS
+ Q_UNUSED(rect);
+#else
+ ensureDrawableTexture();
+
+ DEBUG_ONCE qDebug() << "Refreshing drawable_texture for rectangle" << rect;
+ QRectF screen_rect = rect.adjusted(-1, -1, 1, 1);
+
+ int left = qMax(0, static_cast<int>(screen_rect.left()));
+ int width = qMin(device->size().width() - left, static_cast<int>(screen_rect.width()) + 1);
+
+ int bottom = qMax(0, static_cast<int>(device->size().height() - screen_rect.bottom()));
+ int height = qMin(device->size().height() - bottom, static_cast<int>(screen_rect.height()) + 1);
+
+ glBindTexture(GL_TEXTURE_2D, drawable_texture);
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, left, bottom, left, bottom, width, height);
+#endif
+}
+
+
+void QOpenGLPaintEnginePrivate::composite(const QRectF &rect, const QPoint &maskOffset)
+{
+#ifdef Q_WS_QWS
+ Q_UNUSED(rect);
+ Q_UNUSED(maskOffset);
+#else
+ GLfloat vertexArray[8];
+ qt_add_rect_to_array(rect, vertexArray);
+
+ composite(GL_TRIANGLE_FAN, vertexArray, 4, maskOffset);
+#endif
+}
+
+
+void QOpenGLPaintEnginePrivate::composite(GLuint primitive, const GLfloat *vertexArray, int vertexCount, const QPoint &maskOffset)
+{
+#ifdef QT_OPENGL_ES
+ Q_UNUSED(primitive);
+ Q_UNUSED(vertexArray);
+ Q_UNUSED(vertexCount);
+ Q_UNUSED(maskOffset);
+#else
+ Q_Q(QOpenGLPaintEngine);
+ QGL_FUNC_CONTEXT;
+
+ if (current_style == Qt::NoBrush)
+ return;
+
+ DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Using compositing program: fragment_brush ="
+ << fragment_brush << ", fragment_composition_mode =" << fragment_composition_mode;
+
+ if (has_fast_composition_mode)
+ q->updateCompositionMode(composition_mode);
+ else {
+ qreal minX = 1e9, minY = 1e9, maxX = -1e9, maxY = -1e9;
+
+ for (int i = 0; i < vertexCount; ++i) {
+ qreal x = vertexArray[2 * i];
+ qreal y = vertexArray[2 * i + 1];
+
+ qreal tx, ty;
+ matrix.map(x, y, &tx, &ty);
+
+ minX = qMin(minX, tx);
+ minY = qMin(minY, ty);
+ maxX = qMax(maxX, tx);
+ maxY = qMax(maxY, ty);
+ }
+
+ QRectF r(minX, minY, maxX - minX, maxY - minY);
+ copyDrawable(r);
+
+ glBlendFunc(GL_ONE, GL_ZERO);
+ }
+
+ int *locations = painter_variable_locations[fragment_brush][fragment_composition_mode];
+
+ int texture_locations[] = { locations[VAR_DST_TEXTURE],
+ locations[VAR_MASK_TEXTURE],
+ locations[VAR_PALETTE] };
+
+ int brush_texture_location = locations[VAR_BRUSH_TEXTURE];
+
+ GLuint texture_targets[] = { GL_TEXTURE_2D,
+ GL_TEXTURE_2D,
+ GL_TEXTURE_1D };
+
+ GLuint textures[] = { drawable_texture,
+ offscreen.offscreenTexture(),
+ grad_palette };
+
+ const int num_textures = sizeof(textures) / sizeof(*textures);
+
+ Q_ASSERT(num_textures == sizeof(texture_locations) / sizeof(*texture_locations));
+ Q_ASSERT(num_textures == sizeof(texture_targets) / sizeof(*texture_targets));
+
+ for (int i = 0; i < num_textures; ++i)
+ if (texture_locations[i] >= 0) {
+ glActiveTexture(GL_TEXTURE0 + texture_locations[i]);
+ glBindTexture(texture_targets[i], textures[i]);
+ }
+
+ if (brush_texture_location >= 0) {
+ glActiveTexture(GL_TEXTURE0 + brush_texture_location);
+
+ if (current_style == Qt::TexturePattern)
+ device->context()->d_func()->bindTexture(cbrush.textureImage(), GL_TEXTURE_2D, GL_RGBA,
+ QGLContext::InternalBindOption);
+ else
+ device->context()->d_func()->bindTexture(qt_imageForBrush(current_style, false),
+ GL_TEXTURE_2D, GL_RGBA,
+ QGLContext::InternalBindOption);
+
+ updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, use_smooth_pixmap_transform);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glEnable(GL_FRAGMENT_PROGRAM_ARB);
+ GLuint program = qt_gl_program_cache()->getProgram(device->context(),
+ fragment_brush,
+ fragment_composition_mode, false);
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program);
+
+ mask_offset_data[0] = maskOffset.x();
+ mask_offset_data[1] = -maskOffset.y();
+
+ updateFragmentProgramData(locations);
+
+ glDrawArrays(primitive, 0, vertexCount);
+
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ for (int i = 0; i < num_textures; ++i)
+ if (texture_locations[i] >= 0) {
+ glActiveTexture(GL_TEXTURE0 + texture_locations[i]);
+ glBindTexture(texture_targets[i], 0);
+ }
+
+ if (brush_texture_location >= 0) {
+ glActiveTexture(GL_TEXTURE0 + brush_texture_location);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ glActiveTexture(GL_TEXTURE0);
+
+ if (!has_fast_composition_mode)
+ q->updateCompositionMode(composition_mode);
+#endif
+}
+
+void QOpenGLPaintEnginePrivate::cacheItemErased(int channel, const QRect &rect)
+{
+ bool isInDrawQueue = false;
+
+ foreach (const QDrawQueueItem &item, drawQueue) {
+ if (item.location.channel == channel && item.location.rect == rect) {
+ isInDrawQueue = true;
+ break;
+ }
+ }
+
+ if (isInDrawQueue)
+ flushDrawQueue();
+}
+
+void QOpenGLPaintEnginePrivate::addItem(const QGLMaskTextureCache::CacheLocation &location)
+{
+ drawQueue << QDrawQueueItem(opacity, cbrush, brush_origin, composition_mode, matrix, location);
+}
+
+void QOpenGLPaintEnginePrivate::drawItem(const QDrawQueueItem &item)
+{
+ Q_Q(QOpenGLPaintEngine);
+
+ opacity = item.opacity;
+ brush_origin = item.brush_origin;
+ q->updateCompositionMode(item.composition_mode);
+ matrix = item.matrix;
+ cbrush = item.brush;
+ brush_style = item.brush.style();
+
+ mask_channel_data[0] = item.location.channel == 0;
+ mask_channel_data[1] = item.location.channel == 1;
+ mask_channel_data[2] = item.location.channel == 2;
+ mask_channel_data[3] = item.location.channel == 3;
+
+ setGradientOps(item.brush, item.location.screen_rect);
+
+ composite(item.location.screen_rect, item.location.rect.topLeft() - item.location.screen_rect.topLeft()
+ - QPoint(0, offscreen.offscreenSize().height() - device->size().height()));
+}
+
+void QOpenGLPaintEnginePrivate::flushDrawQueue()
+{
+#ifndef QT_OPENGL_ES
+ Q_Q(QOpenGLPaintEngine);
+
+ offscreen.release();
+
+ if (!drawQueue.isEmpty()) {
+ DEBUG_ONCE qDebug() << "QOpenGLPaintEngine::flushDrawQueue():" << drawQueue.size() << "items";
+
+ glPushMatrix();
+ glLoadIdentity();
+ qreal old_opacity = opacity;
+ QPointF old_brush_origin = brush_origin;
+ QPainter::CompositionMode old_composition_mode = composition_mode;
+ QTransform old_matrix = matrix;
+ QBrush old_brush = cbrush;
+
+ bool hqaa_old = high_quality_antialiasing;
+
+ high_quality_antialiasing = true;
+
+ foreach (const QDrawQueueItem &item, drawQueue)
+ drawItem(item);
+
+ opacity = old_opacity;
+ brush_origin = old_brush_origin;
+ q->updateCompositionMode(old_composition_mode);
+ matrix = old_matrix;
+ cbrush = old_brush;
+ brush_style = old_brush.style();
+
+ high_quality_antialiasing = hqaa_old;
+
+ setGLBrush(old_brush.color());
+ qt_glColor4ubv(brush_color);
+
+ drawQueue.clear();
+
+ glPopMatrix();
+ }
+#endif
+}
+
+void QOpenGLPaintEngine::clipEnabledChanged()
+{
+ Q_D(QOpenGLPaintEngine);
+
+ d->updateDepthClip();
+}
+
+void QOpenGLPaintEngine::penChanged()
+{
+ updatePen(state()->pen);
+}
+
+void QOpenGLPaintEngine::brushChanged()
+{
+ updateBrush(state()->brush, state()->brushOrigin);
+}
+
+void QOpenGLPaintEngine::brushOriginChanged()
+{
+ updateBrush(state()->brush, state()->brushOrigin);
+}
+
+void QOpenGLPaintEngine::opacityChanged()
+{
+ Q_D(QOpenGLPaintEngine);
+ QPainterState *s = state();
+ d->opacity = s->opacity;
+ updateBrush(s->brush, s->brushOrigin);
+ updatePen(s->pen);
+}
+
+void QOpenGLPaintEngine::compositionModeChanged()
+{
+ updateCompositionMode(state()->composition_mode);
+}
+
+void QOpenGLPaintEngine::renderHintsChanged()
+{
+ updateRenderHints(state()->renderHints);
+}
+
+void QOpenGLPaintEngine::transformChanged()
+{
+ updateMatrix(state()->matrix);
+}
+
+static QPainterPath painterPathFromVectorPath(const QVectorPath &path)
+{
+ const qreal *points = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+
+ QPainterPath p;
+ if (types) {
+ int id = 0;
+ for (int i=0; i<path.elementCount(); ++i) {
+ switch(types[i]) {
+ case QPainterPath::MoveToElement:
+ p.moveTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ break;
+ case QPainterPath::LineToElement:
+ p.lineTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ break;
+ case QPainterPath::CurveToElement: {
+ QPointF p1(points[id], points[id+1]);
+ QPointF p2(points[id+2], points[id+3]);
+ QPointF p3(points[id+4], points[id+5]);
+ p.cubicTo(p1, p2, p3);
+ id+=6;
+ break;
+ }
+ case QPainterPath::CurveToDataElement:
+ ;
+ break;
+ }
+ }
+ } else {
+ p.moveTo(QPointF(points[0], points[1]));
+ int id = 2;
+ for (int i=1; i<path.elementCount(); ++i) {
+ p.lineTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ }
+ }
+ if (path.hints() & QVectorPath::WindingFill)
+ p.setFillRule(Qt::WindingFill);
+
+ return p;
+}
+
+void QOpenGLPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (brush.style() == Qt::NoBrush)
+ return;
+
+ if (!d->use_fragment_programs && needsEmulation(brush.style())) {
+ QPainter *p = painter();
+ QBrush oldBrush = p->brush();
+ p->setBrush(brush);
+ qt_draw_helper(p->d_ptr.data(), painterPathFromVectorPath(path), QPainterPrivate::FillDraw);
+ p->setBrush(oldBrush);
+ return;
+ }
+
+ QBrush old_brush = state()->brush;
+ updateBrush(brush, state()->brushOrigin);
+
+ const qreal *points = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+ if (!types && path.shape() == QVectorPath::RectangleHint) {
+ QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
+ QPen old_pen = state()->pen;
+ updatePen(Qt::NoPen);
+ drawRects(&r, 1);
+ updatePen(old_pen);
+ } else {
+ d->fillPath(painterPathFromVectorPath(path));
+ }
+
+ updateBrush(old_brush, state()->brushOrigin);
+}
+
+template <typename T> static inline bool isRect(const T *pts, int elementCount) {
+ return (elementCount == 5 // 5-point polygon, check for closed rect
+ && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
+ && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
+ && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
+ ) ||
+ (elementCount == 4 // 4-point polygon, check for unclosed rect
+ && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
+ && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
+ );
+}
+
+void QOpenGLPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+ const qreal *points = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+ if (!types && path.shape() == QVectorPath::RectangleHint) {
+ QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
+ updateClipRegion(QRegion(r.toRect()), op);
+ return;
+ }
+
+ QPainterPath p;
+ if (types) {
+ int id = 0;
+ for (int i=0; i<path.elementCount(); ++i) {
+ switch(types[i]) {
+ case QPainterPath::MoveToElement:
+ p.moveTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ break;
+ case QPainterPath::LineToElement:
+ p.lineTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ break;
+ case QPainterPath::CurveToElement: {
+ QPointF p1(points[id], points[id+1]);
+ QPointF p2(points[id+2], points[id+3]);
+ QPointF p3(points[id+4], points[id+5]);
+ p.cubicTo(p1, p2, p3);
+ id+=6;
+ break;
+ }
+ case QPainterPath::CurveToDataElement:
+ ;
+ break;
+ }
+ }
+ } else if (!path.isEmpty()) {
+ p.moveTo(QPointF(points[0], points[1]));
+ int id = 2;
+ for (int i=1; i<path.elementCount(); ++i) {
+ p.lineTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ }
+ }
+ if (path.hints() & QVectorPath::WindingFill)
+ p.setFillRule(Qt::WindingFill);
+
+ updateClipRegion(QRegion(p.toFillPolygon().toPolygon(), p.fillRule()), op);
+ return;
+}
+
+void QOpenGLPaintEngine::setState(QPainterState *s)
+{
+ Q_D(QOpenGLPaintEngine);
+ QOpenGLPaintEngineState *new_state = static_cast<QOpenGLPaintEngineState *>(s);
+ QOpenGLPaintEngineState *old_state = state();
+
+ QPaintEngineEx::setState(s);
+
+ // are we in a save() ?
+ if (s == d->last_created_state) {
+ d->last_created_state = 0;
+ return;
+ }
+
+ if (isActive()) {
+ if (old_state->depthClipId != new_state->depthClipId)
+ d->updateDepthClip();
+ penChanged();
+ brushChanged();
+ opacityChanged();
+ compositionModeChanged();
+ renderHintsChanged();
+ transformChanged();
+ }
+}
+
+QPainterState *QOpenGLPaintEngine::createState(QPainterState *orig) const
+{
+ const Q_D(QOpenGLPaintEngine);
+
+ QOpenGLPaintEngineState *s;
+ if (!orig)
+ s = new QOpenGLPaintEngineState();
+ else
+ s = new QOpenGLPaintEngineState(*static_cast<QOpenGLPaintEngineState *>(orig));
+
+ d->last_created_state = s;
+ return s;
+}
+
+//
+// QOpenGLPaintEngineState
+//
+
+QOpenGLPaintEngineState::QOpenGLPaintEngineState(QOpenGLPaintEngineState &other)
+ : QPainterState(other)
+{
+ clipRegion = other.clipRegion;
+ hasClipping = other.hasClipping;
+ fastClip = other.fastClip;
+ depthClipId = other.depthClipId;
+}
+
+QOpenGLPaintEngineState::QOpenGLPaintEngineState()
+{
+ hasClipping = false;
+ depthClipId = 0;
+}
+
+QOpenGLPaintEngineState::~QOpenGLPaintEngineState()
+{
+}
+
+void QOpenGLPaintEnginePrivate::ensureDrawableTexture()
+{
+ if (!dirty_drawable_texture)
+ return;
+
+ dirty_drawable_texture = false;
+
+#ifndef QT_OPENGL_ES
+ glGenTextures(1, &drawable_texture);
+ glBindTexture(GL_TEXTURE_2D, drawable_texture);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
+ drawable_texture_size.width(),
+ drawable_texture_size.height(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+#endif
+}
+
+QT_END_NAMESPACE
+
+#include "qpaintengine_opengl.moc"
diff --git a/src/opengl/qpaintengine_opengl_p.h b/src/opengl/qpaintengine_opengl_p.h
new file mode 100644
index 0000000000..8f12be40f6
--- /dev/null
+++ b/src/opengl/qpaintengine_opengl_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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_OPENGL_P_H
+#define QPAINTENGINE_OPENGL_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/qpaintengineex_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLPaintEnginePrivate;
+class QGLTexture;
+
+class QOpenGLPaintEngineState : public QPainterState
+{
+public:
+ QOpenGLPaintEngineState(QOpenGLPaintEngineState &other);
+ QOpenGLPaintEngineState();
+ ~QOpenGLPaintEngineState();
+
+ QRegion clipRegion;
+ bool hasClipping;
+ QRect fastClip;
+ uint depthClipId;
+};
+
+class QOpenGLPaintEngine : public QPaintEngineEx
+{
+ Q_DECLARE_PRIVATE(QOpenGLPaintEngine)
+public:
+ QOpenGLPaintEngine();
+ ~QOpenGLPaintEngine();
+
+ bool begin(QPaintDevice *pdev);
+ bool end();
+
+ // new stuff
+ void clipEnabledChanged();
+ void penChanged();
+ void brushChanged();
+ void brushOriginChanged();
+ void opacityChanged();
+ void compositionModeChanged();
+ void renderHintsChanged();
+ void transformChanged();
+
+ void fill(const QVectorPath &path, const QBrush &brush);
+ void clip(const QVectorPath &path, Qt::ClipOperation op);
+
+ void setState(QPainterState *s);
+ QPainterState *createState(QPainterState *orig) const;
+ inline QOpenGLPaintEngineState *state() {
+ return static_cast<QOpenGLPaintEngineState *>(QPaintEngineEx::state());
+ }
+ inline const QOpenGLPaintEngineState *state() const {
+ return static_cast<const QOpenGLPaintEngineState *>(QPaintEngineEx::state());
+ }
+
+
+ // old stuff
+ void updateState(const QPaintEngineState &state);
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush, const QPointF &pt);
+ void updateFont(const QFont &font);
+ void updateMatrix(const QTransform &matrix);
+ void updateClipRegion(const QRegion &region, Qt::ClipOperation op);
+ void updateRenderHints(QPainter::RenderHints hints);
+ void updateCompositionMode(QPainter::CompositionMode composition_mode);
+
+ void drawRects(const QRectF *r, int rectCount);
+ void drawLines(const QLineF *lines, int lineCount);
+ void drawPoints(const QPointF *p, int pointCount);
+ void drawRects(const QRect *r, int rectCount);
+ void drawLines(const QLine *lines, int lineCount);
+ void drawPoints(const QPoint *p, int pointCount);
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+
+ void drawPath(const QPainterPath &path);
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ void drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags conversionFlags);
+ void drawTextItem(const QPointF &p, const QTextItem &ti);
+ void drawStaticTextItem(QStaticTextItem *staticTextItem);
+
+ void drawEllipse(const QRectF &rect);
+
+#ifdef Q_WS_WIN
+ HDC handle() const;
+#else
+ Qt::HANDLE handle() const;
+#endif
+ inline Type type() const { return QPaintEngine::OpenGL; }
+
+private:
+ void drawPolyInternal(const QPolygonF &pa, bool close = true);
+ void drawTextureRect(int tx_width, int tx_height, const QRectF &r, const QRectF &sr,
+ GLenum target, QGLTexture *tex);
+ Q_DISABLE_COPY(QOpenGLPaintEngine)
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_OPENGL_P_H
diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp
new file mode 100644
index 0000000000..45722d1bf9
--- /dev/null
+++ b/src/opengl/qpixmapdata_gl.cpp
@@ -0,0 +1,829 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmap.h"
+#include "qglframebufferobject.h"
+
+#include <private/qpaintengine_raster_p.h>
+
+#include "qpixmapdata_gl_p.h"
+
+#include <private/qgl_p.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qimage_p.h>
+#include <private/qfont_p.h>
+
+#include <private/qpaintengineex_opengl2_p.h>
+
+#include <qdesktopwidget.h>
+#include <qfile.h>
+#include <qimagereader.h>
+#include <qbuffer.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_OPENGL_EXPORT extern const QGLContext* qt_gl_share_context();
+
+/*!
+ \class QGLFramebufferObjectPool
+ \since 4.6
+
+ \brief The QGLFramebufferObject class provides a pool of framebuffer
+ objects for offscreen rendering purposes.
+
+ When requesting an FBO of a given size and format, an FBO of the same
+ format and a size at least as big as the requested size will be returned.
+
+ \internal
+*/
+
+static inline int areaDiff(const QSize &size, const QGLFramebufferObject *fbo)
+{
+ return qAbs(size.width() * size.height() - fbo->width() * fbo->height());
+}
+
+extern int qt_next_power_of_two(int v);
+
+static inline QSize maybeRoundToNextPowerOfTwo(const QSize &sz)
+{
+#ifdef QT_OPENGL_ES_2
+ QSize rounded(qt_next_power_of_two(sz.width()), qt_next_power_of_two(sz.height()));
+ if (rounded.width() * rounded.height() < 1.20 * sz.width() * sz.height())
+ return rounded;
+#endif
+ return sz;
+}
+
+
+QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat, bool strictSize)
+{
+ QGLFramebufferObject *chosen = 0;
+ QGLFramebufferObject *candidate = 0;
+ for (int i = 0; !chosen && i < m_fbos.size(); ++i) {
+ QGLFramebufferObject *fbo = m_fbos.at(i);
+
+ if (strictSize) {
+ if (fbo->size() == requestSize && fbo->format() == requestFormat) {
+ chosen = fbo;
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ if (fbo->format() == requestFormat) {
+ // choose the fbo with a matching format and the closest size
+ if (!candidate || areaDiff(requestSize, candidate) > areaDiff(requestSize, fbo))
+ candidate = fbo;
+ }
+
+ if (candidate) {
+ m_fbos.removeOne(candidate);
+
+ const QSize fboSize = candidate->size();
+ QSize sz = fboSize;
+
+ if (sz.width() < requestSize.width())
+ sz.setWidth(qMax(requestSize.width(), qRound(sz.width() * 1.5)));
+ if (sz.height() < requestSize.height())
+ sz.setHeight(qMax(requestSize.height(), qRound(sz.height() * 1.5)));
+
+ // wasting too much space?
+ if (sz.width() * sz.height() > requestSize.width() * requestSize.height() * 4)
+ sz = requestSize;
+
+ if (sz != fboSize) {
+ delete candidate;
+ candidate = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(sz), requestFormat);
+ }
+
+ chosen = candidate;
+ }
+ }
+
+ if (!chosen) {
+ if (strictSize)
+ chosen = new QGLFramebufferObject(requestSize, requestFormat);
+ else
+ chosen = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(requestSize), requestFormat);
+ }
+
+ if (!chosen->isValid()) {
+ delete chosen;
+ chosen = 0;
+ }
+
+ return chosen;
+}
+
+void QGLFramebufferObjectPool::release(QGLFramebufferObject *fbo)
+{
+ if (fbo)
+ m_fbos << fbo;
+}
+
+
+QPaintEngine* QGLPixmapGLPaintDevice::paintEngine() const
+{
+ return data->paintEngine();
+}
+
+void QGLPixmapGLPaintDevice::beginPaint()
+{
+ if (!data->isValid())
+ return;
+
+ // QGLPaintDevice::beginPaint will store the current binding and replace
+ // it with m_thisFBO:
+ m_thisFBO = data->m_renderFbo->handle();
+ QGLPaintDevice::beginPaint();
+
+ Q_ASSERT(data->paintEngine()->type() == QPaintEngine::OpenGL2);
+
+ // QPixmap::fill() is deferred until now, where we actually need to do the fill:
+ if (data->needsFill()) {
+ const QColor &c = data->fillColor();
+ float alpha = c.alphaF();
+ glDisable(GL_SCISSOR_TEST);
+ glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ else if (!data->isUninitialized()) {
+ // If the pixmap (GL Texture) has valid content (it has been
+ // uploaded from an image or rendered into before), we need to
+ // copy it from the texture to the render FBO.
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_BLEND);
+
+#if !defined(QT_OPENGL_ES_2)
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, data->width(), data->height(), 0, -999999, 999999);
+#endif
+
+ glViewport(0, 0, data->width(), data->height());
+
+ // Pass false to bind so it doesn't copy the FBO into the texture!
+ context()->drawTexture(QRect(0, 0, data->width(), data->height()), data->bind(false));
+ }
+}
+
+void QGLPixmapGLPaintDevice::endPaint()
+{
+ if (!data->isValid())
+ return;
+
+ data->copyBackFromRenderFbo(false);
+
+ // Base's endPaint will restore the previous FBO binding
+ QGLPaintDevice::endPaint();
+
+ qgl_fbo_pool()->release(data->m_renderFbo);
+ data->m_renderFbo = 0;
+}
+
+QGLContext* QGLPixmapGLPaintDevice::context() const
+{
+ data->ensureCreated();
+ return data->m_ctx;
+}
+
+QSize QGLPixmapGLPaintDevice::size() const
+{
+ return data->size();
+}
+
+bool QGLPixmapGLPaintDevice::alphaRequested() const
+{
+ return data->m_hasAlpha;
+}
+
+void QGLPixmapGLPaintDevice::setPixmapData(QGLPixmapData* d)
+{
+ data = d;
+}
+
+static int qt_gl_pixmap_serial = 0;
+
+QGLPixmapData::QGLPixmapData(PixelType type)
+ : QPixmapData(type, OpenGLClass)
+ , m_renderFbo(0)
+ , m_engine(0)
+ , m_ctx(0)
+ , m_dirty(false)
+ , m_hasFillColor(false)
+ , m_hasAlpha(false)
+{
+ setSerialNumber(++qt_gl_pixmap_serial);
+ m_glDevice.setPixmapData(this);
+}
+
+QGLPixmapData::~QGLPixmapData()
+{
+ const QGLContext *shareContext = qt_gl_share_context();
+ if (!shareContext)
+ return;
+
+ delete m_engine;
+
+ if (m_texture.id) {
+ QGLShareContextScope ctx(shareContext);
+ glDeleteTextures(1, &m_texture.id);
+ }
+}
+
+QPixmapData *QGLPixmapData::createCompatiblePixmapData() const
+{
+ return new QGLPixmapData(pixelType());
+}
+
+bool QGLPixmapData::isValid() const
+{
+ return w > 0 && h > 0;
+}
+
+bool QGLPixmapData::isValidContext(const QGLContext *ctx) const
+{
+ if (ctx == m_ctx)
+ return true;
+
+ const QGLContext *share_ctx = qt_gl_share_context();
+ return ctx == share_ctx || QGLContext::areSharing(ctx, share_ctx);
+}
+
+void QGLPixmapData::resize(int width, int height)
+{
+ if (width == w && height == h)
+ return;
+
+ if (width <= 0 || height <= 0) {
+ width = 0;
+ height = 0;
+ }
+
+ w = width;
+ h = height;
+ is_null = (w <= 0 || h <= 0);
+ d = pixelType() == QPixmapData::PixmapType ? 32 : 1;
+
+ if (m_texture.id) {
+ QGLShareContextScope ctx(qt_gl_share_context());
+ glDeleteTextures(1, &m_texture.id);
+ m_texture.id = 0;
+ }
+
+ m_source = QImage();
+ m_dirty = isValid();
+ setSerialNumber(++qt_gl_pixmap_serial);
+}
+
+void QGLPixmapData::ensureCreated() const
+{
+ if (!m_dirty)
+ return;
+
+ m_dirty = false;
+
+ QGLShareContextScope ctx(qt_gl_share_context());
+ m_ctx = ctx;
+
+ const GLenum internal_format = m_hasAlpha ? GL_RGBA : GL_RGB;
+#ifdef QT_OPENGL_ES_2
+ const GLenum external_format = internal_format;
+#else
+ const GLenum external_format = qt_gl_preferredTextureFormat();
+#endif
+ const GLenum target = GL_TEXTURE_2D;
+
+ if (!m_texture.id) {
+ glGenTextures(1, &m_texture.id);
+ glBindTexture(target, m_texture.id);
+ glTexImage2D(target, 0, internal_format, w, h, 0, external_format, GL_UNSIGNED_BYTE, 0);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+
+ if (!m_source.isNull()) {
+ if (external_format == GL_RGB) {
+ const QImage tx = m_source.convertToFormat(QImage::Format_RGB888).mirrored(false, true);
+
+ glBindTexture(target, m_texture.id);
+ glTexSubImage2D(target, 0, 0, 0, w, h, external_format,
+ GL_UNSIGNED_BYTE, tx.bits());
+ } else {
+ const QImage tx = ctx->d_func()->convertToGLFormat(m_source, true, external_format);
+
+ glBindTexture(target, m_texture.id);
+ glTexSubImage2D(target, 0, 0, 0, w, h, external_format,
+ GL_UNSIGNED_BYTE, tx.bits());
+ }
+
+ if (useFramebufferObjects())
+ m_source = QImage();
+ }
+
+ m_texture.options &= ~QGLContext::MemoryManagedBindOption;
+}
+
+void QGLPixmapData::fromImage(const QImage &image,
+ Qt::ImageConversionFlags flags)
+{
+ QImage img = image;
+ createPixmapForImage(img, flags, false);
+}
+
+void QGLPixmapData::fromImageReader(QImageReader *imageReader,
+ Qt::ImageConversionFlags flags)
+{
+ QImage image = imageReader->read();
+ if (image.isNull())
+ return;
+
+ createPixmapForImage(image, flags, true);
+}
+
+bool QGLPixmapData::fromFile(const QString &filename, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ if (pixelType() == QPixmapData::BitmapType)
+ return QPixmapData::fromFile(filename, format, flags);
+ QFile file(filename);
+ if (file.open(QIODevice::ReadOnly)) {
+ QByteArray data = file.peek(64);
+ bool alpha;
+ if (m_texture.canBindCompressedTexture
+ (data.constData(), data.size(), format, &alpha)) {
+ resize(0, 0);
+ data = file.readAll();
+ file.close();
+ QGLShareContextScope ctx(qt_gl_share_context());
+ QSize size = m_texture.bindCompressedTexture
+ (data.constData(), data.size(), format);
+ if (!size.isEmpty()) {
+ w = size.width();
+ h = size.height();
+ is_null = false;
+ d = 32;
+ m_hasAlpha = alpha;
+ m_source = QImage();
+ m_dirty = isValid();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ QImage image = QImageReader(filename, format).read();
+ if (image.isNull())
+ return false;
+
+ createPixmapForImage(image, flags, true);
+
+ return !isNull();
+}
+
+bool QGLPixmapData::fromData(const uchar *buffer, uint len, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ bool alpha;
+ const char *buf = reinterpret_cast<const char *>(buffer);
+ if (m_texture.canBindCompressedTexture(buf, int(len), format, &alpha)) {
+ resize(0, 0);
+ QGLShareContextScope ctx(qt_gl_share_context());
+ QSize size = m_texture.bindCompressedTexture(buf, int(len), format);
+ if (!size.isEmpty()) {
+ w = size.width();
+ h = size.height();
+ is_null = false;
+ d = 32;
+ m_hasAlpha = alpha;
+ m_source = QImage();
+ m_dirty = isValid();
+ return true;
+ }
+ }
+
+ QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(buffer), len);
+ QBuffer b(&a);
+ b.open(QIODevice::ReadOnly);
+ QImage image = QImageReader(&b, format).read();
+ if (image.isNull())
+ return false;
+
+ createPixmapForImage(image, flags, true);
+
+ return !isNull();
+}
+
+/*!
+ out-of-place conversion (inPlace == false) will always detach()
+ */
+void QGLPixmapData::createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace)
+{
+ if (image.size() == QSize(w, h))
+ setSerialNumber(++qt_gl_pixmap_serial);
+
+ resize(image.width(), image.height());
+
+ if (pixelType() == BitmapType) {
+ m_source = image.convertToFormat(QImage::Format_MonoLSB);
+
+ } else {
+ QImage::Format format = QImage::Format_RGB32;
+ if (qApp->desktop()->depth() == 16)
+ format = QImage::Format_RGB16;
+
+ if (image.hasAlphaChannel()
+ && ((flags & Qt::NoOpaqueDetection)
+ || const_cast<QImage &>(image).data_ptr()->checkForAlphaPixels()))
+ format = QImage::Format_ARGB32_Premultiplied;;
+
+ if (inPlace && image.data_ptr()->convertInPlace(format, flags)) {
+ m_source = image;
+ } else {
+ m_source = image.convertToFormat(format);
+
+ // convertToFormat won't detach the image if format stays the same.
+ if (image.format() == format)
+ m_source.detach();
+ }
+ }
+
+ m_dirty = true;
+ m_hasFillColor = false;
+
+ m_hasAlpha = m_source.hasAlphaChannel();
+ w = image.width();
+ h = image.height();
+ is_null = (w <= 0 || h <= 0);
+ d = m_source.depth();
+
+ if (m_texture.id) {
+ QGLShareContextScope ctx(qt_gl_share_context());
+ glDeleteTextures(1, &m_texture.id);
+ m_texture.id = 0;
+ }
+}
+
+bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect)
+{
+ Q_UNUSED(dx);
+ Q_UNUSED(dy);
+ Q_UNUSED(rect);
+ return false;
+}
+
+void QGLPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (data->classId() != QPixmapData::OpenGLClass || !static_cast<const QGLPixmapData *>(data)->useFramebufferObjects()) {
+ QPixmapData::copy(data, rect);
+ return;
+ }
+
+ const QGLPixmapData *other = static_cast<const QGLPixmapData *>(data);
+ if (other->m_renderFbo) {
+ QGLShareContextScope ctx(qt_gl_share_context());
+
+ resize(rect.width(), rect.height());
+ m_hasAlpha = other->m_hasAlpha;
+ ensureCreated();
+
+ if (!ctx->d_ptr->fbo)
+ glGenFramebuffers(1, &ctx->d_ptr->fbo);
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, ctx->d_ptr->fbo);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, m_texture.id, 0);
+
+ if (!other->m_renderFbo->isBound())
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, other->m_renderFbo->handle());
+
+ glDisable(GL_SCISSOR_TEST);
+ if (ctx->d_ptr->active_engine && ctx->d_ptr->active_engine->type() == QPaintEngine::OpenGL2)
+ static_cast<QGL2PaintEngineEx *>(ctx->d_ptr->active_engine)->invalidateState();
+
+ glBlitFramebufferEXT(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height(),
+ 0, 0, w, h,
+ GL_COLOR_BUFFER_BIT,
+ GL_NEAREST);
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
+ } else {
+ QPixmapData::copy(data, rect);
+ }
+}
+
+void QGLPixmapData::fill(const QColor &color)
+{
+ if (!isValid())
+ return;
+
+ bool hasAlpha = color.alpha() != 255;
+ if (hasAlpha && !m_hasAlpha) {
+ if (m_texture.id) {
+ glDeleteTextures(1, &m_texture.id);
+ m_texture.id = 0;
+ m_dirty = true;
+ }
+ m_hasAlpha = color.alpha() != 255;
+ }
+
+ if (useFramebufferObjects()) {
+ m_source = QImage();
+ m_hasFillColor = true;
+ m_fillColor = color;
+ } else {
+
+ if (m_source.isNull()) {
+ m_fillColor = color;
+ m_hasFillColor = true;
+
+ } else if (m_source.depth() == 32) {
+ m_source.fill(PREMUL(color.rgba()));
+
+ } else if (m_source.depth() == 1) {
+ if (color == Qt::color1)
+ m_source.fill(1);
+ else
+ m_source.fill(0);
+ }
+ }
+}
+
+bool QGLPixmapData::hasAlphaChannel() const
+{
+ return m_hasAlpha;
+}
+
+QImage QGLPixmapData::fillImage(const QColor &color) const
+{
+ QImage img;
+ if (pixelType() == BitmapType) {
+ img = QImage(w, h, QImage::Format_MonoLSB);
+
+ img.setColorCount(2);
+ img.setColor(0, QColor(Qt::color0).rgba());
+ img.setColor(1, QColor(Qt::color1).rgba());
+
+ if (color == Qt::color1)
+ img.fill(1);
+ else
+ img.fill(0);
+ } else {
+ img = QImage(w, h,
+ m_hasAlpha
+ ? QImage::Format_ARGB32_Premultiplied
+ : QImage::Format_RGB32);
+ img.fill(PREMUL(color.rgba()));
+ }
+ return img;
+}
+
+extern QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha);
+
+QImage QGLPixmapData::toImage() const
+{
+ if (!isValid())
+ return QImage();
+
+ if (m_renderFbo) {
+ copyBackFromRenderFbo(true);
+ } else if (!m_source.isNull()) {
+ QImageData *data = const_cast<QImage &>(m_source).data_ptr();
+ if (data->paintEngine && data->paintEngine->isActive()
+ && data->paintEngine->paintDevice() == &m_source)
+ {
+ return m_source.copy();
+ }
+ return m_source;
+ } else if (m_dirty || m_hasFillColor) {
+ return fillImage(m_fillColor);
+ } else {
+ ensureCreated();
+ }
+
+ QGLShareContextScope ctx(qt_gl_share_context());
+ glBindTexture(GL_TEXTURE_2D, m_texture.id);
+ return qt_gl_read_texture(QSize(w, h), true, true);
+}
+
+struct TextureBuffer
+{
+ QGLFramebufferObject *fbo;
+ QGL2PaintEngineEx *engine;
+};
+
+Q_GLOBAL_STATIC(QGLFramebufferObjectPool, _qgl_fbo_pool)
+QGLFramebufferObjectPool* qgl_fbo_pool()
+{
+ return _qgl_fbo_pool();
+}
+
+void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const
+{
+ if (!isValid())
+ return;
+
+ m_hasFillColor = false;
+
+ const QGLContext *share_ctx = qt_gl_share_context();
+ QGLShareContextScope ctx(share_ctx);
+
+ ensureCreated();
+
+ if (!ctx->d_ptr->fbo)
+ glGenFramebuffers(1, &ctx->d_ptr->fbo);
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, ctx->d_ptr->fbo);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, m_texture.id, 0);
+
+ const int x0 = 0;
+ const int x1 = w;
+ const int y0 = 0;
+ const int y1 = h;
+
+ if (!m_renderFbo->isBound())
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, m_renderFbo->handle());
+
+ glDisable(GL_SCISSOR_TEST);
+
+ glBlitFramebufferEXT(x0, y0, x1, y1,
+ x0, y0, x1, y1,
+ GL_COLOR_BUFFER_BIT,
+ GL_NEAREST);
+
+ if (keepCurrentFboBound) {
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
+ } else {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, m_renderFbo->handle());
+ ctx->d_ptr->current_fbo = m_renderFbo->handle();
+ }
+}
+
+bool QGLPixmapData::useFramebufferObjects() const
+{
+ return QGLFramebufferObject::hasOpenGLFramebufferObjects()
+ && QGLFramebufferObject::hasOpenGLFramebufferBlit()
+ && qt_gl_preferGL2Engine()
+ && (w * h > 32*32); // avoid overhead of FBOs for small pixmaps
+}
+
+QPaintEngine* QGLPixmapData::paintEngine() const
+{
+ if (!isValid())
+ return 0;
+
+ if (m_renderFbo)
+ return m_engine;
+
+ if (useFramebufferObjects()) {
+ extern QGLWidget* qt_gl_share_widget();
+
+ if (!QGLContext::currentContext())
+ const_cast<QGLContext *>(qt_gl_share_context())->makeCurrent();
+ QGLShareContextScope ctx(qt_gl_share_context());
+
+ QGLFramebufferObjectFormat format;
+ format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format.setSamples(4);
+ format.setInternalTextureFormat(GLenum(m_hasAlpha ? GL_RGBA : GL_RGB));
+
+ m_renderFbo = qgl_fbo_pool()->acquire(size(), format);
+
+ if (m_renderFbo) {
+ if (!m_engine)
+ m_engine = new QGL2PaintEngineEx;
+ return m_engine;
+ }
+
+ qWarning() << "Failed to create pixmap texture buffer of size " << size() << ", falling back to raster paint engine";
+ }
+
+ m_dirty = true;
+ if (m_source.size() != size())
+ m_source = QImage(size(), QImage::Format_ARGB32_Premultiplied);
+ if (m_hasFillColor) {
+ m_source.fill(PREMUL(m_fillColor.rgba()));
+ m_hasFillColor = false;
+ }
+ return m_source.paintEngine();
+}
+
+extern QRgb qt_gl_convertToGLFormat(QRgb src_pixel, GLenum texture_format);
+
+// If copyBack is true, bind will copy the contents of the render
+// FBO to the texture (which is not bound to the texture, as it's
+// a multisample FBO).
+GLuint QGLPixmapData::bind(bool copyBack) const
+{
+ if (m_renderFbo && copyBack) {
+ copyBackFromRenderFbo(true);
+ } else {
+ ensureCreated();
+ }
+
+ GLuint id = m_texture.id;
+ glBindTexture(GL_TEXTURE_2D, id);
+
+ if (m_hasFillColor) {
+ if (!useFramebufferObjects()) {
+ m_source = QImage(w, h, QImage::Format_ARGB32_Premultiplied);
+ m_source.fill(PREMUL(m_fillColor.rgba()));
+ }
+
+ m_hasFillColor = false;
+
+ GLenum format = qt_gl_preferredTextureFormat();
+ QImage tx(w, h, QImage::Format_ARGB32_Premultiplied);
+ tx.fill(qt_gl_convertToGLFormat(m_fillColor.rgba(), format));
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format, GL_UNSIGNED_BYTE, tx.bits());
+ }
+
+ return id;
+}
+
+QGLTexture* QGLPixmapData::texture() const
+{
+ return &m_texture;
+}
+
+int QGLPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ if (w == 0)
+ return 0;
+
+ switch (metric) {
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmNumColors:
+ return 0;
+ case QPaintDevice::PdmDepth:
+ return d;
+ case QPaintDevice::PdmWidthMM:
+ return qRound(w * 25.4 / qt_defaultDpiX());
+ case QPaintDevice::PdmHeightMM:
+ return qRound(h * 25.4 / qt_defaultDpiY());
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX:
+ return qt_defaultDpiX();
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return qt_defaultDpiY();
+ default:
+ qWarning("QGLPixmapData::metric(): Invalid metric");
+ return 0;
+ }
+}
+
+QGLPaintDevice *QGLPixmapData::glDevice() const
+{
+ return &m_glDevice;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h
new file mode 100644
index 0000000000..8855c20362
--- /dev/null
+++ b/src/opengl/qpixmapdata_gl_p.h
@@ -0,0 +1,247 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATA_GL_P_H
+#define QPIXMAPDATA_GL_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 "qgl_p.h"
+#include "qgl.h"
+
+#include "private/qpixmapdata_p.h"
+#include "private/qglpaintdevice_p.h"
+
+#ifdef Q_OS_SYMBIAN
+#include "private/qvolatileimage_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QPaintEngine;
+class QGLFramebufferObject;
+class QGLFramebufferObjectFormat;
+class QGLPixmapData;
+
+#ifdef QGL_USE_TEXTURE_POOL
+void qt_gl_register_pixmap(QGLPixmapData *pd);
+void qt_gl_unregister_pixmap(QGLPixmapData *pd);
+void qt_gl_hibernate_pixmaps();
+#endif
+
+#ifdef Q_OS_SYMBIAN
+class QNativeImageHandleProvider;
+#endif
+
+class QGLFramebufferObjectPool
+{
+public:
+ QGLFramebufferObject *acquire(const QSize &size, const QGLFramebufferObjectFormat &format, bool strictSize = false);
+ void release(QGLFramebufferObject *fbo);
+
+private:
+ QList<QGLFramebufferObject *> m_fbos;
+};
+
+QGLFramebufferObjectPool* qgl_fbo_pool();
+
+
+class QGLPixmapGLPaintDevice : public QGLPaintDevice
+{
+public:
+ QPaintEngine* paintEngine() const;
+
+ void beginPaint();
+ void endPaint();
+ QGLContext* context() const;
+ QSize size() const;
+ bool alphaRequested() const;
+
+ void setPixmapData(QGLPixmapData*);
+private:
+ QGLPixmapData *data;
+};
+
+
+class Q_OPENGL_EXPORT QGLPixmapData : public QPixmapData
+{
+public:
+ QGLPixmapData(PixelType type);
+ ~QGLPixmapData();
+
+ QPixmapData *createCompatiblePixmapData() const;
+
+ // Re-implemented from QPixmapData:
+ void resize(int width, int height);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void fromImageReader(QImageReader *imageReader,
+ Qt::ImageConversionFlags flags);
+ bool fromFile(const QString &filename, const char *format,
+ Qt::ImageConversionFlags flags);
+ bool fromData(const uchar *buffer, uint len, const char *format,
+ Qt::ImageConversionFlags flags);
+ void copy(const QPixmapData *data, const QRect &rect);
+ bool scroll(int dx, int dy, const QRect &rect);
+ void fill(const QColor &color);
+ bool hasAlphaChannel() const;
+ QImage toImage() const;
+ QPaintEngine *paintEngine() const;
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+
+ // For accessing as a target:
+ QGLPaintDevice *glDevice() const;
+
+ // For accessing as a source:
+ bool isValidContext(const QGLContext *ctx) const;
+ GLuint bind(bool copyBack = true) const;
+ QGLTexture *texture() const;
+
+#ifdef QGL_USE_TEXTURE_POOL
+ void destroyTexture();
+ // Detach this image from the image pool.
+ void detachTextureFromPool();
+ // Release the GL resources associated with this pixmap and copy
+ // the pixmap's contents out of the GPU back into main memory.
+ // The GL resource will be automatically recreated the next time
+ // ensureCreated() is called. Does nothing if the pixmap cannot be
+ // hibernated for some reason (e.g. texture is shared with another
+ // process via a SgImage).
+ void hibernate();
+ // Called when the QGLTexturePool wants to reclaim this pixmap's
+ // texture objects to reuse storage.
+ void reclaimTexture();
+ void forceToImage();
+#endif
+
+#ifdef Q_OS_SYMBIAN
+ QImage::Format idealFormat(QImage &image, Qt::ImageConversionFlags flags);
+ void* toNativeType(NativeType type);
+ void fromNativeType(void* pixmap, NativeType type);
+ bool initFromNativeImageHandle(void *handle, const QString &type);
+ void createFromNativeImageHandleProvider();
+ void releaseNativeImageHandle();
+#endif
+
+private:
+ bool isValid() const;
+
+ void ensureCreated() const;
+
+ bool isUninitialized() const { return m_dirty && m_source.isNull(); }
+
+ bool needsFill() const { return m_hasFillColor; }
+ QColor fillColor() const { return m_fillColor; }
+
+
+
+ QGLPixmapData(const QGLPixmapData &other);
+ QGLPixmapData &operator=(const QGLPixmapData &other);
+
+ void copyBackFromRenderFbo(bool keepCurrentFboBound) const;
+ QSize size() const { return QSize(w, h); }
+
+ bool useFramebufferObjects() const;
+
+ QImage fillImage(const QColor &color) const;
+
+ void createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace);
+
+ mutable QGLFramebufferObject *m_renderFbo;
+ mutable QPaintEngine *m_engine;
+ mutable QGLContext *m_ctx;
+#ifdef Q_OS_SYMBIAN
+ mutable QVolatileImage m_source;
+ mutable QNativeImageHandleProvider *nativeImageHandleProvider;
+ void *nativeImageHandle;
+ QString nativeImageType;
+#else
+ mutable QImage m_source;
+#endif
+ mutable QGLTexture m_texture;
+
+ // the texture is not in sync with the source image
+ mutable bool m_dirty;
+
+ // fill has been called and no painting has been done, so the pixmap is
+ // represented by a single fill color
+ mutable QColor m_fillColor;
+ mutable bool m_hasFillColor;
+
+ mutable bool m_hasAlpha;
+
+ mutable QGLPixmapGLPaintDevice m_glDevice;
+
+#ifdef QGL_USE_TEXTURE_POOL
+ QGLPixmapData *nextLRU;
+ QGLPixmapData *prevLRU;
+ mutable bool inLRU;
+ mutable bool failedToAlloc;
+ mutable bool inTexturePool;
+
+ QGLPixmapData *next;
+ QGLPixmapData *prev;
+
+ friend class QGLTexturePool;
+
+ friend void qt_gl_register_pixmap(QGLPixmapData *pd);
+ friend void qt_gl_unregister_pixmap(QGLPixmapData *pd);
+ friend void qt_gl_hibernate_pixmaps();
+#endif
+
+ friend class QGLPixmapGLPaintDevice;
+ friend class QMeeGoPixmapData;
+ friend class QMeeGoLivePixmapData;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPDATA_GL_P_H
+
+
diff --git a/src/opengl/qpixmapdata_poolgl.cpp b/src/opengl/qpixmapdata_poolgl.cpp
new file mode 100644
index 0000000000..041210d404
--- /dev/null
+++ b/src/opengl/qpixmapdata_poolgl.cpp
@@ -0,0 +1,934 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmap.h"
+#include "qglframebufferobject.h"
+
+#include <private/qpaintengine_raster_p.h>
+
+#include "qpixmapdata_gl_p.h"
+
+#include <private/qgl_p.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qimage_p.h>
+#include <private/qnativeimagehandleprovider_p.h>
+#include <private/qfont_p.h>
+
+#include <private/qpaintengineex_opengl2_p.h>
+
+#include <qdesktopwidget.h>
+#include <qfile.h>
+#include <qimagereader.h>
+#include <qbuffer.h>
+
+#include "qgltexturepool_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_OPENGL_EXPORT extern QGLWidget* qt_gl_share_widget();
+
+static inline int areaDiff(const QSize &size, const QGLFramebufferObject *fbo)
+{
+ return qAbs(size.width() * size.height() - fbo->width() * fbo->height());
+}
+
+extern int qt_next_power_of_two(int v);
+
+static inline QSize maybeRoundToNextPowerOfTwo(const QSize &sz)
+{
+#ifdef QT_OPENGL_ES_2
+ QSize rounded(qt_next_power_of_two(sz.width()), qt_next_power_of_two(sz.height()));
+ if (rounded.width() * rounded.height() < 1.20 * sz.width() * sz.height())
+ return rounded;
+#endif
+ return sz;
+}
+
+
+QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat, bool strictSize)
+{
+ QGLFramebufferObject *chosen = 0;
+ QGLFramebufferObject *candidate = 0;
+ for (int i = 0; !chosen && i < m_fbos.size(); ++i) {
+ QGLFramebufferObject *fbo = m_fbos.at(i);
+
+ if (strictSize) {
+ if (fbo->size() == requestSize && fbo->format() == requestFormat) {
+ chosen = fbo;
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ if (fbo->format() == requestFormat) {
+ // choose the fbo with a matching format and the closest size
+ if (!candidate || areaDiff(requestSize, candidate) > areaDiff(requestSize, fbo))
+ candidate = fbo;
+ }
+
+ if (candidate) {
+ m_fbos.removeOne(candidate);
+
+ const QSize fboSize = candidate->size();
+ QSize sz = fboSize;
+
+ if (sz.width() < requestSize.width())
+ sz.setWidth(qMax(requestSize.width(), qRound(sz.width() * 1.5)));
+ if (sz.height() < requestSize.height())
+ sz.setHeight(qMax(requestSize.height(), qRound(sz.height() * 1.5)));
+
+ // wasting too much space?
+ if (sz.width() * sz.height() > requestSize.width() * requestSize.height() * 4)
+ sz = requestSize;
+
+ if (sz != fboSize) {
+ delete candidate;
+ candidate = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(sz), requestFormat);
+ }
+
+ chosen = candidate;
+ }
+ }
+
+ if (!chosen) {
+ if (strictSize)
+ chosen = new QGLFramebufferObject(requestSize, requestFormat);
+ else
+ chosen = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(requestSize), requestFormat);
+ }
+
+ if (!chosen->isValid()) {
+ delete chosen;
+ chosen = 0;
+ }
+
+ return chosen;
+}
+
+void QGLFramebufferObjectPool::release(QGLFramebufferObject *fbo)
+{
+ if (fbo)
+ m_fbos << fbo;
+}
+
+
+QPaintEngine* QGLPixmapGLPaintDevice::paintEngine() const
+{
+ return data->paintEngine();
+}
+
+void QGLPixmapGLPaintDevice::beginPaint()
+{
+ if (!data->isValid())
+ return;
+
+ // QGLPaintDevice::beginPaint will store the current binding and replace
+ // it with m_thisFBO:
+ m_thisFBO = data->m_renderFbo->handle();
+ QGLPaintDevice::beginPaint();
+
+ Q_ASSERT(data->paintEngine()->type() == QPaintEngine::OpenGL2);
+
+ // QPixmap::fill() is deferred until now, where we actually need to do the fill:
+ if (data->needsFill()) {
+ const QColor &c = data->fillColor();
+ float alpha = c.alphaF();
+ glDisable(GL_SCISSOR_TEST);
+ glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ else if (!data->isUninitialized()) {
+ // If the pixmap (GL Texture) has valid content (it has been
+ // uploaded from an image or rendered into before), we need to
+ // copy it from the texture to the render FBO.
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_BLEND);
+
+#if !defined(QT_OPENGL_ES_2)
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, data->width(), data->height(), 0, -999999, 999999);
+#endif
+
+ glViewport(0, 0, data->width(), data->height());
+
+ // Pass false to bind so it doesn't copy the FBO into the texture!
+ context()->drawTexture(QRect(0, 0, data->width(), data->height()), data->bind(false));
+ }
+}
+
+void QGLPixmapGLPaintDevice::endPaint()
+{
+ if (!data->isValid())
+ return;
+
+ data->copyBackFromRenderFbo(false);
+
+ // Base's endPaint will restore the previous FBO binding
+ QGLPaintDevice::endPaint();
+
+ qgl_fbo_pool()->release(data->m_renderFbo);
+ data->m_renderFbo = 0;
+}
+
+QGLContext* QGLPixmapGLPaintDevice::context() const
+{
+ data->ensureCreated();
+ return data->m_ctx;
+}
+
+QSize QGLPixmapGLPaintDevice::size() const
+{
+ return data->size();
+}
+
+bool QGLPixmapGLPaintDevice::alphaRequested() const
+{
+ return data->m_hasAlpha;
+}
+
+void QGLPixmapGLPaintDevice::setPixmapData(QGLPixmapData* d)
+{
+ data = d;
+}
+
+int qt_gl_pixmap_serial = 0;
+
+QGLPixmapData::QGLPixmapData(PixelType type)
+ : QPixmapData(type, OpenGLClass)
+ , m_renderFbo(0)
+ , m_engine(0)
+ , m_ctx(0)
+ , nativeImageHandleProvider(0)
+ , nativeImageHandle(0)
+ , m_dirty(false)
+ , m_hasFillColor(false)
+ , m_hasAlpha(false)
+ , inLRU(false)
+ , failedToAlloc(false)
+ , inTexturePool(false)
+{
+ setSerialNumber(++qt_gl_pixmap_serial);
+ m_glDevice.setPixmapData(this);
+
+ qt_gl_register_pixmap(this);
+}
+
+QGLPixmapData::~QGLPixmapData()
+{
+ delete m_engine;
+
+ destroyTexture();
+ qt_gl_unregister_pixmap(this);
+}
+
+void QGLPixmapData::destroyTexture()
+{
+ if (inTexturePool) {
+ QGLTexturePool *pool = QGLTexturePool::instance();
+ if (m_texture.id)
+ pool->releaseTexture(this, m_texture.id);
+ } else {
+ if (m_texture.id) {
+ QGLWidget *shareWidget = qt_gl_share_widget();
+ if (shareWidget) {
+ QGLShareContextScope ctx(shareWidget->context());
+ glDeleteTextures(1, &m_texture.id);
+ }
+ }
+ }
+ m_texture.id = 0;
+ inTexturePool = false;
+
+ releaseNativeImageHandle();
+}
+
+QPixmapData *QGLPixmapData::createCompatiblePixmapData() const
+{
+ return new QGLPixmapData(pixelType());
+}
+
+bool QGLPixmapData::isValid() const
+{
+ return w > 0 && h > 0;
+}
+
+bool QGLPixmapData::isValidContext(const QGLContext *ctx) const
+{
+ if (ctx == m_ctx)
+ return true;
+
+ const QGLContext *share_ctx = qt_gl_share_widget()->context();
+ return ctx == share_ctx || QGLContext::areSharing(ctx, share_ctx);
+}
+
+void QGLPixmapData::resize(int width, int height)
+{
+ if (width == w && height == h)
+ return;
+
+ if (width <= 0 || height <= 0) {
+ width = 0;
+ height = 0;
+ }
+
+ w = width;
+ h = height;
+ is_null = (w <= 0 || h <= 0);
+ d = pixelType() == QPixmapData::PixmapType ? 32 : 1;
+
+ destroyTexture();
+
+ m_source = QVolatileImage();
+ m_dirty = isValid();
+ setSerialNumber(++qt_gl_pixmap_serial);
+}
+
+void QGLPixmapData::ensureCreated() const
+{
+ if (!m_dirty)
+ return;
+
+ m_dirty = false;
+
+ if (nativeImageHandleProvider && !nativeImageHandle)
+ const_cast<QGLPixmapData *>(this)->createFromNativeImageHandleProvider();
+
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ m_ctx = ctx;
+
+ const GLenum internal_format = m_hasAlpha ? GL_RGBA : GL_RGB;
+#ifdef QT_OPENGL_ES_2
+ const GLenum external_format = internal_format;
+#else
+ const GLenum external_format = qt_gl_preferredTextureFormat();
+#endif
+ const GLenum target = GL_TEXTURE_2D;
+
+ GLenum type = GL_UNSIGNED_BYTE;
+ // Avoid conversion when pixmap is created from CFbsBitmap of EColor64K.
+ if (!m_source.isNull() && m_source.format() == QImage::Format_RGB16)
+ type = GL_UNSIGNED_SHORT_5_6_5;
+
+ m_texture.options &= ~QGLContext::MemoryManagedBindOption;
+
+ if (!m_texture.id) {
+ m_texture.id = QGLTexturePool::instance()->createTextureForPixmap(
+ target,
+ 0, internal_format,
+ w, h,
+ external_format,
+ type,
+ const_cast<QGLPixmapData*>(this));
+ if (!m_texture.id) {
+ failedToAlloc = true;
+ return;
+ }
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ inTexturePool = true;
+ } else if (inTexturePool) {
+ glBindTexture(target, m_texture.id);
+ QGLTexturePool::instance()->useTexture(const_cast<QGLPixmapData*>(this));
+ }
+
+ if (!m_source.isNull() && m_texture.id) {
+ if (external_format == GL_RGB) {
+ m_source.beginDataAccess();
+ QImage tx;
+ if (type == GL_UNSIGNED_BYTE)
+ tx = m_source.imageRef().convertToFormat(QImage::Format_RGB888).mirrored(false, true);
+ else if (type == GL_UNSIGNED_SHORT_5_6_5)
+ tx = m_source.imageRef().mirrored(false, true);
+ m_source.endDataAccess(true);
+
+ glBindTexture(target, m_texture.id);
+ if (!tx.isNull())
+ glTexSubImage2D(target, 0, 0, 0, w, h, external_format,
+ type, tx.constBits());
+ else
+ qWarning("QGLPixmapData: Failed to create GL_RGB image of size %dx%d", w, h);
+ } else {
+ // do byte swizzling ARGB -> RGBA
+ m_source.beginDataAccess();
+ const QImage tx = ctx->d_func()->convertToGLFormat(m_source.imageRef(), true, external_format);
+ m_source.endDataAccess(true);
+ glBindTexture(target, m_texture.id);
+ if (!tx.isNull())
+ glTexSubImage2D(target, 0, 0, 0, w, h, external_format,
+ type, tx.constBits());
+ else
+ qWarning("QGLPixmapData: Failed to create GL_RGBA image of size %dx%d", w, h);
+ }
+
+ if (useFramebufferObjects())
+ m_source = QVolatileImage();
+ }
+}
+
+
+void QGLPixmapData::fromImage(const QImage &image,
+ Qt::ImageConversionFlags flags)
+{
+ QImage img = image;
+ createPixmapForImage(img, flags, false);
+}
+
+void QGLPixmapData::fromImageReader(QImageReader *imageReader,
+ Qt::ImageConversionFlags flags)
+{
+ QImage image = imageReader->read();
+ if (image.isNull())
+ return;
+
+ createPixmapForImage(image, flags, true);
+}
+
+bool QGLPixmapData::fromFile(const QString &filename, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ if (pixelType() == QPixmapData::BitmapType)
+ return QPixmapData::fromFile(filename, format, flags);
+ QFile file(filename);
+ if (file.open(QIODevice::ReadOnly)) {
+ QByteArray data = file.peek(64);
+ bool alpha;
+ if (m_texture.canBindCompressedTexture
+ (data.constData(), data.size(), format, &alpha)) {
+ resize(0, 0);
+ data = file.readAll();
+ file.close();
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ QSize size = m_texture.bindCompressedTexture
+ (data.constData(), data.size(), format);
+ if (!size.isEmpty()) {
+ w = size.width();
+ h = size.height();
+ is_null = false;
+ d = 32;
+ m_hasAlpha = alpha;
+ m_source = QVolatileImage();
+ m_dirty = isValid();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ QImage image = QImageReader(filename, format).read();
+ if (image.isNull())
+ return false;
+
+ createPixmapForImage(image, flags, true);
+
+ return !isNull();
+}
+
+bool QGLPixmapData::fromData(const uchar *buffer, uint len, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ bool alpha;
+ const char *buf = reinterpret_cast<const char *>(buffer);
+ if (m_texture.canBindCompressedTexture(buf, int(len), format, &alpha)) {
+ resize(0, 0);
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ QSize size = m_texture.bindCompressedTexture(buf, int(len), format);
+ if (!size.isEmpty()) {
+ w = size.width();
+ h = size.height();
+ is_null = false;
+ d = 32;
+ m_hasAlpha = alpha;
+ m_source = QVolatileImage();
+ m_dirty = isValid();
+ return true;
+ }
+ }
+
+ QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(buffer), len);
+ QBuffer b(&a);
+ b.open(QIODevice::ReadOnly);
+ QImage image = QImageReader(&b, format).read();
+ if (image.isNull())
+ return false;
+
+ createPixmapForImage(image, flags, true);
+
+ return !isNull();
+}
+
+QImage::Format QGLPixmapData::idealFormat(QImage &image, Qt::ImageConversionFlags flags)
+{
+ QImage::Format format = QImage::Format_RGB32;
+ if (qApp->desktop()->depth() == 16)
+ format = QImage::Format_RGB16;
+
+ if (image.hasAlphaChannel()
+ && ((flags & Qt::NoOpaqueDetection)
+ || const_cast<QImage &>(image).data_ptr()->checkForAlphaPixels()))
+ format = QImage::Format_ARGB32_Premultiplied;
+
+ return format;
+}
+
+void QGLPixmapData::createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace)
+{
+ if (image.size() == QSize(w, h))
+ setSerialNumber(++qt_gl_pixmap_serial);
+
+ resize(image.width(), image.height());
+
+ if (pixelType() == BitmapType) {
+ QImage convertedImage = image.convertToFormat(QImage::Format_MonoLSB);
+ if (image.format() == QImage::Format_MonoLSB)
+ convertedImage.detach();
+
+ m_source = QVolatileImage(convertedImage);
+
+ } else {
+ QImage::Format format = idealFormat(image, flags);
+
+ if (inPlace && image.data_ptr()->convertInPlace(format, flags)) {
+ m_source = QVolatileImage(image);
+ } else {
+ QImage convertedImage = image.convertToFormat(format);
+
+ // convertToFormat won't detach the image if format stays the same.
+ if (image.format() == format)
+ convertedImage.detach();
+
+ m_source = QVolatileImage(convertedImage);
+ }
+ }
+
+ m_dirty = true;
+ m_hasFillColor = false;
+
+ m_hasAlpha = m_source.hasAlphaChannel();
+ w = image.width();
+ h = image.height();
+ is_null = (w <= 0 || h <= 0);
+ d = m_source.depth();
+
+ destroyTexture();
+}
+
+bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect)
+{
+ Q_UNUSED(dx);
+ Q_UNUSED(dy);
+ Q_UNUSED(rect);
+ return false;
+}
+
+void QGLPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (data->classId() != QPixmapData::OpenGLClass || !static_cast<const QGLPixmapData *>(data)->useFramebufferObjects()) {
+ QPixmapData::copy(data, rect);
+ return;
+ }
+
+ const QGLPixmapData *other = static_cast<const QGLPixmapData *>(data);
+ if (other->m_renderFbo) {
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+
+ resize(rect.width(), rect.height());
+ m_hasAlpha = other->m_hasAlpha;
+ ensureCreated();
+
+ if (!ctx->d_ptr->fbo)
+ glGenFramebuffers(1, &ctx->d_ptr->fbo);
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, ctx->d_ptr->fbo);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, m_texture.id, 0);
+
+ if (!other->m_renderFbo->isBound())
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, other->m_renderFbo->handle());
+
+ glDisable(GL_SCISSOR_TEST);
+ if (ctx->d_ptr->active_engine && ctx->d_ptr->active_engine->type() == QPaintEngine::OpenGL2)
+ static_cast<QGL2PaintEngineEx *>(ctx->d_ptr->active_engine)->invalidateState();
+
+ glBlitFramebufferEXT(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height(),
+ 0, 0, w, h,
+ GL_COLOR_BUFFER_BIT,
+ GL_NEAREST);
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
+ } else {
+ QPixmapData::copy(data, rect);
+ }
+}
+
+void QGLPixmapData::fill(const QColor &color)
+{
+ if (!isValid())
+ return;
+
+ bool hasAlpha = color.alpha() != 255;
+ if (hasAlpha && !m_hasAlpha) {
+ if (m_texture.id) {
+ destroyTexture();
+ m_dirty = true;
+ }
+ m_hasAlpha = color.alpha() != 255;
+ }
+
+ if (useFramebufferObjects()) {
+ m_source = QVolatileImage();
+ m_hasFillColor = true;
+ m_fillColor = color;
+ } else {
+ forceToImage();
+
+ if (m_source.depth() == 32) {
+ m_source.fill(PREMUL(color.rgba()));
+
+ } else if (m_source.depth() == 1) {
+ if (color == Qt::color1)
+ m_source.fill(1);
+ else
+ m_source.fill(0);
+ }
+ }
+}
+
+bool QGLPixmapData::hasAlphaChannel() const
+{
+ return m_hasAlpha;
+}
+
+QImage QGLPixmapData::fillImage(const QColor &color) const
+{
+ QImage img;
+ if (pixelType() == BitmapType) {
+ img = QImage(w, h, QImage::Format_MonoLSB);
+
+ img.setColorCount(2);
+ img.setColor(0, QColor(Qt::color0).rgba());
+ img.setColor(1, QColor(Qt::color1).rgba());
+
+ if (color == Qt::color1)
+ img.fill(1);
+ else
+ img.fill(0);
+ } else {
+ img = QImage(w, h,
+ m_hasAlpha
+ ? QImage::Format_ARGB32_Premultiplied
+ : QImage::Format_RGB32);
+ img.fill(PREMUL(color.rgba()));
+ }
+ return img;
+}
+
+extern QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha);
+
+QImage QGLPixmapData::toImage() const
+{
+ if (!isValid())
+ return QImage();
+
+ if (m_renderFbo) {
+ copyBackFromRenderFbo(true);
+ } else if (!m_source.isNull()) {
+ // QVolatileImage::toImage() will make a copy always so no check
+ // for active painting is needed.
+ QImage img = m_source.toImage();
+ if (img.format() == QImage::Format_MonoLSB) {
+ img.setColorCount(2);
+ img.setColor(0, QColor(Qt::color0).rgba());
+ img.setColor(1, QColor(Qt::color1).rgba());
+ }
+ return img;
+ } else if (m_dirty || m_hasFillColor) {
+ return fillImage(m_fillColor);
+ } else {
+ ensureCreated();
+ }
+
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ glBindTexture(GL_TEXTURE_2D, m_texture.id);
+ return qt_gl_read_texture(QSize(w, h), true, true);
+}
+
+struct TextureBuffer
+{
+ QGLFramebufferObject *fbo;
+ QGL2PaintEngineEx *engine;
+};
+
+Q_GLOBAL_STATIC(QGLFramebufferObjectPool, _qgl_fbo_pool)
+QGLFramebufferObjectPool* qgl_fbo_pool()
+{
+ return _qgl_fbo_pool();
+}
+
+void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const
+{
+ if (!isValid())
+ return;
+
+ m_hasFillColor = false;
+
+ const QGLContext *share_ctx = qt_gl_share_widget()->context();
+ QGLShareContextScope ctx(share_ctx);
+
+ ensureCreated();
+
+ if (!ctx->d_ptr->fbo)
+ glGenFramebuffers(1, &ctx->d_ptr->fbo);
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, ctx->d_ptr->fbo);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, m_texture.id, 0);
+
+ const int x0 = 0;
+ const int x1 = w;
+ const int y0 = 0;
+ const int y1 = h;
+
+ if (!m_renderFbo->isBound())
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, m_renderFbo->handle());
+
+ glDisable(GL_SCISSOR_TEST);
+
+ glBlitFramebufferEXT(x0, y0, x1, y1,
+ x0, y0, x1, y1,
+ GL_COLOR_BUFFER_BIT,
+ GL_NEAREST);
+
+ if (keepCurrentFboBound) {
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
+ } else {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, m_renderFbo->handle());
+ ctx->d_ptr->current_fbo = m_renderFbo->handle();
+ }
+}
+
+bool QGLPixmapData::useFramebufferObjects() const
+{
+#ifdef Q_OS_SYMBIAN
+ // We don't want to use FBOs on Symbian
+ return false;
+#else
+ return QGLFramebufferObject::hasOpenGLFramebufferObjects()
+ && QGLFramebufferObject::hasOpenGLFramebufferBlit()
+ && qt_gl_preferGL2Engine()
+ && (w * h > 32*32); // avoid overhead of FBOs for small pixmaps
+#endif
+}
+
+QPaintEngine* QGLPixmapData::paintEngine() const
+{
+ if (!isValid())
+ return 0;
+
+ if (m_renderFbo)
+ return m_engine;
+
+ if (useFramebufferObjects()) {
+ extern QGLWidget* qt_gl_share_widget();
+
+ if (!QGLContext::currentContext())
+ qt_gl_share_widget()->makeCurrent();
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+
+ QGLFramebufferObjectFormat format;
+ format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format.setSamples(4);
+ format.setInternalTextureFormat(GLenum(m_hasAlpha ? GL_RGBA : GL_RGB));
+
+ m_renderFbo = qgl_fbo_pool()->acquire(size(), format);
+
+ if (m_renderFbo) {
+ if (!m_engine)
+ m_engine = new QGL2PaintEngineEx;
+ return m_engine;
+ }
+
+ qWarning() << "Failed to create pixmap texture buffer of size " << size() << ", falling back to raster paint engine";
+ }
+
+ // If the application wants to paint into the QPixmap, we first
+ // force it to QImage format and then paint into that.
+ // This is simpler than juggling multiple GL contexts.
+ const_cast<QGLPixmapData *>(this)->forceToImage();
+
+ if (m_hasFillColor) {
+ m_source.fill(PREMUL(m_fillColor.rgba()));
+ m_hasFillColor = false;
+ }
+ return m_source.paintEngine();
+}
+
+extern QRgb qt_gl_convertToGLFormat(QRgb src_pixel, GLenum texture_format);
+
+// If copyBack is true, bind will copy the contents of the render
+// FBO to the texture (which is not bound to the texture, as it's
+// a multisample FBO).
+GLuint QGLPixmapData::bind(bool copyBack) const
+{
+ if (m_renderFbo && copyBack) {
+ copyBackFromRenderFbo(true);
+ } else {
+ ensureCreated();
+ }
+
+ GLuint id = m_texture.id;
+ glBindTexture(GL_TEXTURE_2D, id);
+
+ if (m_hasFillColor) {
+ if (!useFramebufferObjects()) {
+ m_source = QVolatileImage(w, h, QImage::Format_ARGB32_Premultiplied);
+ m_source.fill(PREMUL(m_fillColor.rgba()));
+ }
+
+ m_hasFillColor = false;
+
+ GLenum format = qt_gl_preferredTextureFormat();
+ QImage tx(w, h, QImage::Format_ARGB32_Premultiplied);
+ tx.fill(qt_gl_convertToGLFormat(m_fillColor.rgba(), format));
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format, GL_UNSIGNED_BYTE, tx.constBits());
+ }
+
+ return id;
+}
+
+QGLTexture* QGLPixmapData::texture() const
+{
+ return &m_texture;
+}
+
+void QGLPixmapData::detachTextureFromPool()
+{
+ if (inTexturePool) {
+ QGLTexturePool::instance()->detachTexture(this);
+ inTexturePool = false;
+ }
+}
+
+void QGLPixmapData::hibernate()
+{
+ // If the image was imported (e.g, from an SgImage under Symbian), then
+ // skip the hibernation, there is no sense in copying it back to main
+ // memory because the data is most likely shared between several processes.
+ bool skipHibernate = (m_texture.id && m_source.isNull());
+#if defined(Q_OS_SYMBIAN)
+ // However we have to proceed normally if the image was retrieved via
+ // a handle provider.
+ skipHibernate &= !nativeImageHandleProvider;
+#endif
+ if (skipHibernate)
+ return;
+
+ forceToImage();
+ destroyTexture();
+}
+
+void QGLPixmapData::reclaimTexture()
+{
+ if (!inTexturePool)
+ return;
+ forceToImage();
+ destroyTexture();
+}
+
+int QGLPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ if (w == 0)
+ return 0;
+
+ switch (metric) {
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmNumColors:
+ return 0;
+ case QPaintDevice::PdmDepth:
+ return d;
+ case QPaintDevice::PdmWidthMM:
+ return qRound(w * 25.4 / qt_defaultDpiX());
+ case QPaintDevice::PdmHeightMM:
+ return qRound(h * 25.4 / qt_defaultDpiY());
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX:
+ return qt_defaultDpiX();
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return qt_defaultDpiY();
+ default:
+ qWarning("QGLPixmapData::metric(): Invalid metric");
+ return 0;
+ }
+}
+
+// Force the pixmap data to be backed by some valid data.
+void QGLPixmapData::forceToImage()
+{
+ if (!isValid())
+ return;
+
+ if (m_source.isNull()) {
+ QImage::Format format = QImage::Format_ARGB32_Premultiplied;
+ if (pixelType() == BitmapType)
+ format = QImage::Format_MonoLSB;
+ m_source = QVolatileImage(w, h, format);
+ }
+
+ m_dirty = true;
+}
+
+QGLPaintDevice *QGLPixmapData::glDevice() const
+{
+ return &m_glDevice;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qpixmapdata_x11gl_egl.cpp b/src/opengl/qpixmapdata_x11gl_egl.cpp
new file mode 100644
index 0000000000..6126ba46ae
--- /dev/null
+++ b/src/opengl/qpixmapdata_x11gl_egl.cpp
@@ -0,0 +1,403 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QDebug>
+
+#include <QtGui/private/qt_x11_p.h>
+#include <QtGui/private/qegl_p.h>
+#include <QtGui/private/qeglproperties_p.h>
+#include <QtGui/private/qeglcontext_p.h>
+
+#if !defined(QT_OPENGL_ES_1)
+#include <QtOpenGL/private/qpaintengineex_opengl2_p.h>
+#endif
+
+#ifndef QT_OPENGL_ES_2
+#include <QtOpenGL/private/qpaintengine_opengl_p.h>
+#endif
+
+#include <QtOpenGL/private/qgl_p.h>
+#include <QtOpenGL/private/qgl_egl_p.h>
+
+#include "qpixmapdata_x11gl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+class QX11GLSharedContexts
+{
+public:
+ QX11GLSharedContexts()
+ : rgbContext(0)
+ , argbContext(0)
+ , sharedQGLContext(0)
+ , sharePixmap(0)
+ {
+ EGLint rgbConfigId;
+ EGLint argbConfigId;
+
+ do {
+ EGLConfig rgbConfig = QEgl::defaultConfig(QInternal::Pixmap, QEgl::OpenGL, QEgl::Renderable);
+ EGLConfig argbConfig = QEgl::defaultConfig(QInternal::Pixmap, QEgl::OpenGL,
+ QEgl::Renderable | QEgl::Translucent);
+
+ eglGetConfigAttrib(QEgl::display(), rgbConfig, EGL_CONFIG_ID, &rgbConfigId);
+ eglGetConfigAttrib(QEgl::display(), argbConfig, EGL_CONFIG_ID, &argbConfigId);
+
+ rgbContext = new QEglContext;
+ rgbContext->setConfig(rgbConfig);
+ rgbContext->createContext();
+
+ if (!rgbContext->isValid())
+ break;
+
+ // If the RGB & ARGB configs are the same, use the same egl context for both:
+ if (rgbConfig == argbConfig)
+ argbContext = rgbContext;
+
+ // Otherwise, create a separate context to be used for ARGB pixmaps:
+ if (!argbContext) {
+ argbContext = new QEglContext;
+ argbContext->setConfig(argbConfig);
+ bool success = argbContext->createContext(rgbContext);
+ if (!success) {
+ qWarning("QX11GLPixmapData - RGB & ARGB contexts aren't shared");
+ success = argbContext->createContext();
+ if (!success)
+ argbContext = rgbContext; // Might work, worth a shot at least.
+ }
+ }
+
+ if (!argbContext->isValid())
+ break;
+
+ // Create the pixmap which will be used to create the egl surface for the share QGLContext
+ QX11PixmapData *rgbPixmapData = new QX11PixmapData(QPixmapData::PixmapType);
+ rgbPixmapData->resize(8, 8);
+ rgbPixmapData->fill(Qt::red);
+ sharePixmap = new QPixmap(rgbPixmapData);
+ EGLSurface sharePixmapSurface = QEgl::createSurface(sharePixmap, rgbConfig);
+ rgbPixmapData->gl_surface = (void*)sharePixmapSurface;
+
+ // Create the actual QGLContext which will be used for sharing
+ sharedQGLContext = new QGLContext(QX11GLPixmapData::glFormat());
+ sharedQGLContext->d_func()->eglContext = rgbContext;
+ sharedQGLContext->d_func()->eglSurface = sharePixmapSurface;
+ sharedQGLContext->d_func()->valid = true;
+ qt_glformat_from_eglconfig(sharedQGLContext->d_func()->glFormat, rgbConfig);
+
+
+ valid = rgbContext->makeCurrent(sharePixmapSurface);
+
+ // If the ARGB & RGB configs are different, check ARGB works too:
+ if (argbConfig != rgbConfig) {
+ QX11PixmapData *argbPixmapData = new QX11PixmapData(QPixmapData::PixmapType);
+ argbPixmapData->resize(8, 8);
+ argbPixmapData->fill(Qt::transparent); // Force ARGB
+ QPixmap argbPixmap(argbPixmapData); // destroys pixmap data when goes out of scope
+ EGLSurface argbPixmapSurface = QEgl::createSurface(&argbPixmap, argbConfig);
+ valid = argbContext->makeCurrent(argbPixmapSurface);
+ argbContext->doneCurrent();
+ eglDestroySurface(QEgl::display(), argbPixmapSurface);
+ argbPixmapData->gl_surface = 0;
+ }
+
+ if (!valid) {
+ qWarning() << "Unable to make pixmap surface current:" << QEgl::errorString();
+ break;
+ }
+
+ // The pixmap surface destruction hooks are installed by QGLTextureCache, so we
+ // must make sure this is instanciated:
+ QGLTextureCache::instance();
+ } while(0);
+
+ if (!valid)
+ cleanup();
+ else
+ qDebug("Using QX11GLPixmapData with EGL config %d for ARGB and config %d for RGB", argbConfigId, rgbConfigId);
+
+ }
+
+ ~QX11GLSharedContexts() {
+ cleanup();
+ }
+
+ void cleanup() {
+ if (sharedQGLContext) {
+ delete sharedQGLContext;
+ sharedQGLContext = 0;
+ }
+ if (argbContext && argbContext != rgbContext)
+ delete argbContext;
+ argbContext = 0;
+
+ if (rgbContext) {
+ delete rgbContext;
+ rgbContext = 0;
+ }
+
+ // Deleting the QPixmap will fire the pixmap destruction cleanup hooks which in turn
+ // will destroy the egl surface:
+ if (sharePixmap) {
+ delete sharePixmap;
+ sharePixmap = 0;
+ }
+ }
+
+ bool isValid() { return valid;}
+
+ // On 16bpp systems, RGB & ARGB pixmaps are different bit-depths and therefore need
+ // different contexts:
+ QEglContext *rgbContext;
+ QEglContext *argbContext;
+
+ // The share context wraps the rgbContext and is used as the master of the context share
+ // group. As all other contexts will have the same egl context (or a shared one if rgb != argb)
+ // all QGLContexts will actually be sharing and can be in the same context group.
+ QGLContext *sharedQGLContext;
+private:
+ QPixmap *sharePixmap;
+ bool valid;
+};
+
+static void qt_cleanup_x11gl_share_contexts();
+
+Q_GLOBAL_STATIC_WITH_INITIALIZER(QX11GLSharedContexts, qt_x11gl_share_contexts,
+ {
+ qAddPostRoutine(qt_cleanup_x11gl_share_contexts);
+ })
+
+static void qt_cleanup_x11gl_share_contexts()
+{
+ qt_x11gl_share_contexts()->cleanup();
+}
+
+
+QX11GLSharedContexts* QX11GLPixmapData::sharedContexts()
+{
+ return qt_x11gl_share_contexts();
+}
+
+bool QX11GLPixmapData::hasX11GLPixmaps()
+{
+ static bool checkedForX11GLPixmaps = false;
+ static bool haveX11GLPixmaps = false;
+
+ if (checkedForX11GLPixmaps)
+ return haveX11GLPixmaps;
+
+ haveX11GLPixmaps = qt_x11gl_share_contexts()->isValid();
+ checkedForX11GLPixmaps = true;
+
+ return haveX11GLPixmaps;
+}
+
+QX11GLPixmapData::QX11GLPixmapData()
+ : QX11PixmapData(QPixmapData::PixmapType),
+ ctx(0)
+{
+}
+
+QX11GLPixmapData::~QX11GLPixmapData()
+{
+ if (ctx)
+ delete ctx;
+}
+
+
+void QX11GLPixmapData::fill(const QColor &color)
+{
+ if (ctx) {
+ ctx->makeCurrent();
+ glFinish();
+ eglWaitClient();
+ }
+
+ QX11PixmapData::fill(color);
+ XSync(X11->display, False);
+
+ if (ctx) {
+ ctx->makeCurrent();
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+ }
+}
+
+void QX11GLPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (ctx) {
+ ctx->makeCurrent();
+ glFinish();
+ eglWaitClient();
+ }
+
+ QX11PixmapData::copy(data, rect);
+ XSync(X11->display, False);
+
+ if (ctx) {
+ ctx->makeCurrent();
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+ }
+}
+
+bool QX11GLPixmapData::scroll(int dx, int dy, const QRect &rect)
+{
+ if (ctx) {
+ ctx->makeCurrent();
+ glFinish();
+ eglWaitClient();
+ }
+
+ bool success = QX11PixmapData::scroll(dx, dy, rect);
+ XSync(X11->display, False);
+
+ if (ctx) {
+ ctx->makeCurrent();
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+ }
+
+ return success;
+}
+
+#if !defined(QT_OPENGL_ES_1)
+Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_pixmap_2_engine)
+#endif
+
+#ifndef QT_OPENGL_ES_2
+Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_pixmap_engine)
+#endif
+
+
+QPaintEngine* QX11GLPixmapData::paintEngine() const
+{
+ // We need to create the context before beginPaint - do it here:
+ if (!ctx) {
+ ctx = new QGLContext(glFormat());
+ Q_ASSERT(ctx->d_func()->eglContext == 0);
+ ctx->d_func()->eglContext = hasAlphaChannel() ? sharedContexts()->argbContext : sharedContexts()->rgbContext;
+
+ // While we use a separate QGLContext for each pixmap, the underlying QEglContext is
+ // the same. So we must use a "fake" QGLContext and fool the texture cache into thinking
+ // each pixmap's QGLContext is sharing with this central one. The only place this is
+ // going to fail is where we the underlying EGL RGB and ARGB contexts aren't sharing.
+ ctx->d_func()->sharing = true;
+ QGLContextGroup::addShare(ctx, sharedContexts()->sharedQGLContext);
+
+ // Update the glFormat for the QGLContext:
+ qt_glformat_from_eglconfig(ctx->d_func()->glFormat, ctx->d_func()->eglContext->config());
+ }
+
+ QPaintEngine* engine;
+
+#if defined(QT_OPENGL_ES_1)
+ engine = qt_gl_pixmap_engine();
+#elif defined(QT_OPENGL_ES_2)
+ engine = qt_gl_pixmap_2_engine();
+#else
+ if (qt_gl_preferGL2Engine())
+ engine = qt_gl_pixmap_2_engine();
+ else
+ engine = qt_gl_pixmap_engine();
+#endif
+
+
+
+ // Support multiple painters on multiple pixmaps simultaniously
+ if (engine->isActive()) {
+ qWarning("Pixmap paint engine already active");
+
+#if defined(QT_OPENGL_ES_1)
+ engine = new QOpenGLPaintEngine;
+#elif defined(QT_OPENGL_ES_2)
+ engine = new QGL2PaintEngineEx;
+#else
+ if (qt_gl_preferGL2Engine())
+ engine = new QGL2PaintEngineEx;
+ else
+ engine = new QOpenGLPaintEngine;
+#endif
+
+ engine->setAutoDestruct(true);
+ return engine;
+ }
+
+ return engine;
+}
+
+void QX11GLPixmapData::beginPaint()
+{
+// qDebug("QX11GLPixmapData::beginPaint()");
+ // TODO: Check to see if the surface is renderable
+ if ((EGLSurface)gl_surface == EGL_NO_SURFACE) {
+ QPixmap tmpPixmap(this);
+ EGLConfig cfg = ctx->d_func()->eglContext->config();
+ Q_ASSERT(cfg != QEGL_NO_CONFIG);
+
+// qDebug("QX11GLPixmapData - using EGL Config ID %d", ctx->d_func()->eglContext->configAttrib(EGL_CONFIG_ID));
+ EGLSurface surface = QEgl::createSurface(&tmpPixmap, cfg);
+ if (surface == EGL_NO_SURFACE) {
+ qWarning() << "Error creating EGL surface for pixmap:" << QEgl::errorString();
+ return;
+ }
+ gl_surface = (void*)surface;
+ ctx->d_func()->eglSurface = surface;
+ ctx->d_func()->valid = true;
+ }
+ QGLPaintDevice::beginPaint();
+}
+
+QGLContext* QX11GLPixmapData::context() const
+{
+ return ctx;
+}
+
+QSize QX11GLPixmapData::size() const
+{
+ return QSize(w, h);
+}
+
+
+QGLFormat QX11GLPixmapData::glFormat()
+{
+ return QGLFormat::defaultFormat(); //###
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qpixmapdata_x11gl_p.h b/src/opengl/qpixmapdata_x11gl_p.h
new file mode 100644
index 0000000000..567349a2cb
--- /dev/null
+++ b/src/opengl/qpixmapdata_x11gl_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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATA_X11GL_P_H
+#define QPIXMAPDATA_X11GL_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 <private/qpixmapdata_p.h>
+#include <private/qpixmap_x11_p.h>
+#include <private/qglpaintdevice_p.h>
+
+#include <qgl.h>
+
+#ifndef QT_NO_EGL
+#include <QtGui/private/qeglcontext_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QX11GLSharedContexts;
+
+class QX11GLPixmapData : public QX11PixmapData, public QGLPaintDevice
+{
+public:
+ QX11GLPixmapData();
+ virtual ~QX11GLPixmapData();
+
+ // Re-implemented from QX11PixmapData:
+ void fill(const QColor &color);
+ void copy(const QPixmapData *data, const QRect &rect);
+ bool scroll(int dx, int dy, const QRect &rect);
+
+ // Re-implemented from QGLPaintDevice
+ QPaintEngine* paintEngine() const; // Also re-implements QX11PixmapData::paintEngine
+ void beginPaint();
+ QGLContext* context() const;
+ QSize size() const;
+
+ static bool hasX11GLPixmaps();
+ static QGLFormat glFormat();
+ static QX11GLSharedContexts* sharedContexts();
+
+private:
+ mutable QGLContext* ctx;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPDATA_X11GL_P_H
diff --git a/src/opengl/qwindowsurface_gl.cpp b/src/opengl/qwindowsurface_gl.cpp
new file mode 100644
index 0000000000..56e2c3b517
--- /dev/null
+++ b/src/opengl/qwindowsurface_gl.cpp
@@ -0,0 +1,1149 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/QApplication>
+#include <QtGui/QColormap>
+#include <QtGui/QDesktopWidget>
+#include <QtGui/QPaintDevice>
+#include <QtGui/QWidget>
+
+#include <qglframebufferobject.h>
+#include <qglpixelbuffer.h>
+#include <qcolormap.h>
+#include <qdesktopwidget.h>
+#include <private/qwidget_p.h>
+#include "qdebug.h"
+
+#ifdef Q_WS_X11
+#include <private/qt_x11_p.h>
+#include <qx11info_x11.h>
+
+#ifndef QT_OPENGL_ES
+#include <GL/glx.h>
+#include <X11/Xlib.h>
+#endif
+#endif //Q_WS_X11
+
+#include <private/qglextensions_p.h>
+#include <private/qwindowsurface_gl_p.h>
+
+#include <private/qgl_p.h>
+
+#include <private/qglpixelbuffer_p.h>
+#include <private/qgraphicssystem_gl_p.h>
+
+#include <private/qpaintengineex_opengl2_p.h>
+#include <private/qpixmapdata_gl_p.h>
+
+#ifndef QT_OPENGL_ES_2
+#include <private/qpaintengine_opengl_p.h>
+#endif
+
+#ifndef GLX_ARB_multisample
+#define GLX_SAMPLE_BUFFERS_ARB 100000
+#define GLX_SAMPLES_ARB 100001
+#endif
+
+#ifndef QT_NO_EGL
+#include <private/qeglcontext_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//
+// QGLGraphicsSystem
+//
+#ifdef Q_WS_WIN
+extern Q_GUI_EXPORT bool qt_win_owndc_required;
+#endif
+QGLGraphicsSystem::QGLGraphicsSystem(bool useX11GL)
+ : QGraphicsSystem(), m_useX11GL(useX11GL)
+{
+#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
+ // only override the system defaults if the user hasn't already
+ // picked a visual
+ if (X11->visual == 0 && X11->visual_id == -1 && X11->visual_class == -1) {
+ // find a double buffered, RGBA visual that supports OpenGL
+ // and set that as the default visual for windows in Qt
+ int i = 0;
+ int spec[16];
+ spec[i++] = GLX_RGBA;
+ spec[i++] = GLX_DOUBLEBUFFER;
+
+ if (!qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull()) {
+ spec[i++] = GLX_DEPTH_SIZE;
+ spec[i++] = 8;
+ spec[i++] = GLX_STENCIL_SIZE;
+ spec[i++] = 8;
+ spec[i++] = GLX_SAMPLE_BUFFERS_ARB;
+ spec[i++] = 1;
+ spec[i++] = GLX_SAMPLES_ARB;
+ spec[i++] = 4;
+ }
+
+ spec[i++] = XNone;
+
+ XVisualInfo *vi = glXChooseVisual(X11->display, X11->defaultScreen, spec);
+ if (vi) {
+ X11->visual_id = vi->visualid;
+ X11->visual_class = vi->c_class;
+
+ QGLFormat format;
+ int res;
+ glXGetConfig(X11->display, vi, GLX_LEVEL, &res);
+ format.setPlane(res);
+ glXGetConfig(X11->display, vi, GLX_DOUBLEBUFFER, &res);
+ format.setDoubleBuffer(res);
+ glXGetConfig(X11->display, vi, GLX_DEPTH_SIZE, &res);
+ format.setDepth(res);
+ if (format.depth())
+ format.setDepthBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_RGBA, &res);
+ format.setRgba(res);
+ glXGetConfig(X11->display, vi, GLX_RED_SIZE, &res);
+ format.setRedBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_GREEN_SIZE, &res);
+ format.setGreenBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_BLUE_SIZE, &res);
+ format.setBlueBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_ALPHA_SIZE, &res);
+ format.setAlpha(res);
+ if (format.alpha())
+ format.setAlphaBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_ACCUM_RED_SIZE, &res);
+ format.setAccum(res);
+ if (format.accum())
+ format.setAccumBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_STENCIL_SIZE, &res);
+ format.setStencil(res);
+ if (format.stencil())
+ format.setStencilBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_STEREO, &res);
+ format.setStereo(res);
+ glXGetConfig(X11->display, vi, GLX_SAMPLE_BUFFERS_ARB, &res);
+ format.setSampleBuffers(res);
+ if (format.sampleBuffers()) {
+ glXGetConfig(X11->display, vi, GLX_SAMPLES_ARB, &res);
+ format.setSamples(res);
+ }
+
+ QGLWindowSurface::surfaceFormat = format;
+ XFree(vi);
+
+ printf("using visual class %x, id %x\n", X11->visual_class, X11->visual_id);
+ }
+ }
+#elif defined(Q_WS_WIN)
+ QGLWindowSurface::surfaceFormat.setDoubleBuffer(true);
+
+ qt_win_owndc_required = true;
+#endif
+}
+
+//
+// QGLWindowSurface
+//
+class QGLGlobalShareWidget
+{
+public:
+ QGLGlobalShareWidget() : firstPixmap(0), widgetRefCount(0), widget(0), initializing(false) {}
+
+ QGLWidget *shareWidget() {
+ if (!initializing && !widget && !cleanedUp) {
+ initializing = true;
+ widget = new QGLWidget(QGLFormat(QGL::SingleBuffer | QGL::NoDepthBuffer | QGL::NoStencilBuffer));
+ widget->resize(1, 1);
+
+ // We don't need this internal widget to appear in QApplication::topLevelWidgets()
+ if (QWidgetPrivate::allWidgets)
+ QWidgetPrivate::allWidgets->remove(widget);
+ initializing = false;
+ }
+ return widget;
+ }
+
+ // destroys the share widget and prevents recreation
+ void cleanup() {
+ QGLWidget *w = widget;
+ cleanedUp = true;
+ widget = 0;
+ delete w;
+ }
+
+ // destroys the share widget, but allows it to be recreated later on
+ void destroy() {
+ if (cleanedUp)
+ return;
+
+ QGLWidget *w = widget;
+
+ // prevent potential recursions
+ cleanedUp = true;
+ widget = 0;
+ delete w;
+ cleanedUp = false;
+ }
+
+ static bool cleanedUp;
+
+ QGLPixmapData *firstPixmap;
+ int widgetRefCount;
+
+private:
+ QGLWidget *widget;
+ bool initializing;
+};
+
+bool QGLGlobalShareWidget::cleanedUp = false;
+
+static void qt_cleanup_gl_share_widget();
+Q_GLOBAL_STATIC_WITH_INITIALIZER(QGLGlobalShareWidget, _qt_gl_share_widget,
+ {
+ qAddPostRoutine(qt_cleanup_gl_share_widget);
+ })
+
+static void qt_cleanup_gl_share_widget()
+{
+ _qt_gl_share_widget()->cleanup();
+}
+
+QGLWidget* qt_gl_share_widget()
+{
+ if (QGLGlobalShareWidget::cleanedUp)
+ return 0;
+ return _qt_gl_share_widget()->shareWidget();
+}
+
+void qt_destroy_gl_share_widget()
+{
+ _qt_gl_share_widget()->destroy();
+}
+
+const QGLContext *qt_gl_share_context()
+{
+ QGLWidget *widget = qt_gl_share_widget();
+ if (widget)
+ return widget->context();
+ return 0;
+}
+
+#ifdef QGL_USE_TEXTURE_POOL
+void qt_gl_register_pixmap(QGLPixmapData *pd)
+{
+ QGLGlobalShareWidget *shared = _qt_gl_share_widget();
+ pd->next = shared->firstPixmap;
+ pd->prev = 0;
+ if (shared->firstPixmap)
+ shared->firstPixmap->prev = pd;
+ shared->firstPixmap = pd;
+}
+
+void qt_gl_unregister_pixmap(QGLPixmapData *pd)
+{
+ if (pd->next)
+ pd->next->prev = pd->prev;
+ if (pd->prev) {
+ pd->prev->next = pd->next;
+ } else {
+ QGLGlobalShareWidget *shared = _qt_gl_share_widget();
+ if (shared)
+ shared->firstPixmap = pd->next;
+ }
+}
+
+void qt_gl_hibernate_pixmaps()
+{
+ QGLGlobalShareWidget *shared = _qt_gl_share_widget();
+
+ // Scan all QGLPixmapData objects in the system and hibernate them.
+ QGLPixmapData *pd = shared->firstPixmap;
+ while (pd != 0) {
+ pd->hibernate();
+ pd = pd->next;
+ }
+}
+#endif
+
+struct QGLWindowSurfacePrivate
+{
+ QGLFramebufferObject *fbo;
+ QGLPixelBuffer *pb;
+ GLuint tex_id;
+ GLuint pb_tex_id;
+
+ int tried_fbo : 1;
+ int tried_pb : 1;
+ int destructive_swap_buffers : 1;
+ int geometry_updated : 1;
+ int did_paint : 1;
+
+ QGLContext *ctx;
+
+ QList<QGLContext **> contexts;
+
+ QRegion paintedRegion;
+ QSize size;
+
+ QSize textureSize;
+
+ QList<QImage> buffers;
+ QGLWindowSurfaceGLPaintDevice glDevice;
+ QGLWindowSurface* q_ptr;
+
+ bool swap_region_support;
+};
+
+QGLFormat QGLWindowSurface::surfaceFormat;
+QGLWindowSurface::SwapMode QGLWindowSurface::swapBehavior = QGLWindowSurface::AutomaticSwap;
+
+void QGLWindowSurfaceGLPaintDevice::endPaint()
+{
+ glFlush();
+ QGLPaintDevice::endPaint();
+}
+
+QSize QGLWindowSurfaceGLPaintDevice::size() const
+{
+ return d->size;
+}
+
+QGLContext* QGLWindowSurfaceGLPaintDevice::context() const
+{
+ return d->ctx;
+}
+
+
+int QGLWindowSurfaceGLPaintDevice::metric(PaintDeviceMetric m) const
+{
+ return qt_paint_device_metric(d->q_ptr->window(), m);
+}
+
+QPaintEngine *QGLWindowSurfaceGLPaintDevice::paintEngine() const
+{
+ return qt_qgl_paint_engine();
+}
+
+QGLWindowSurface::QGLWindowSurface(QWidget *window)
+ : QWindowSurface(window), d_ptr(new QGLWindowSurfacePrivate)
+{
+// Q_ASSERT(window->isTopLevel());
+ d_ptr->pb = 0;
+ d_ptr->fbo = 0;
+ d_ptr->ctx = 0;
+ d_ptr->tex_id = 0;
+#if defined (QT_OPENGL_ES_2)
+ d_ptr->tried_fbo = true;
+ d_ptr->tried_pb = true;
+#else
+ d_ptr->tried_fbo = false;
+ d_ptr->tried_pb = false;
+#endif
+ d_ptr->destructive_swap_buffers = qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull();
+ d_ptr->glDevice.d = d_ptr;
+ d_ptr->q_ptr = this;
+ d_ptr->geometry_updated = false;
+ d_ptr->did_paint = false;
+ d_ptr->swap_region_support = false;
+}
+
+QGLWindowSurface::~QGLWindowSurface()
+{
+ if (d_ptr->ctx)
+ glDeleteTextures(1, &d_ptr->tex_id);
+#ifndef Q_WS_QPA // Dont delete the contexts. Destroying the window does that for us
+ foreach(QGLContext **ctx, d_ptr->contexts) {
+ delete *ctx;
+ *ctx = 0;
+ }
+#endif
+ delete d_ptr->pb;
+ delete d_ptr->fbo;
+ delete d_ptr;
+
+ if (QGLGlobalShareWidget::cleanedUp)
+ return;
+
+ --(_qt_gl_share_widget()->widgetRefCount);
+
+#ifdef QGL_USE_TEXTURE_POOL
+ if (_qt_gl_share_widget()->widgetRefCount <= 0) {
+ // All of the widget window surfaces have been destroyed
+ // but we still have GL pixmaps active. Ask them to hibernate
+ // to free up GPU resources until a widget is shown again.
+ // This may eventually cause the EGLContext to be destroyed
+ // because nothing in the system needs a context, which will
+ // free up even more GPU resources.
+ qt_gl_hibernate_pixmaps();
+
+ // Destroy the context if necessary.
+ if (!qt_gl_share_widget()->context()->isSharing())
+ qt_destroy_gl_share_widget();
+ }
+#endif // QGL_USE_TEXTURE_POOL
+}
+
+void QGLWindowSurface::deleted(QObject *object)
+{
+ QWidget *widget = qobject_cast<QWidget *>(object);
+ if (widget) {
+ if (widget == window()) {
+ // Make sure that the fbo is destroyed before destroying its context.
+ delete d_ptr->fbo;
+ d_ptr->fbo = 0;
+ }
+
+#ifndef Q_WS_QPA //no need to specifically delete the QGLContext as it will be deleted by QWidget
+ QWidgetPrivate *widgetPrivate = widget->d_func();
+ if (widgetPrivate->extraData()) {
+ union { QGLContext **ctxPtrPtr; void **voidPtrPtr; };
+ voidPtrPtr = &widgetPrivate->extraData()->glContext;
+ int index = d_ptr->contexts.indexOf(ctxPtrPtr);
+ if (index != -1) {
+ delete *ctxPtrPtr;
+ *ctxPtrPtr = 0;
+ d_ptr->contexts.removeAt(index);
+ }
+ }
+#endif
+ }
+}
+
+void QGLWindowSurface::hijackWindow(QWidget *widget)
+{
+ QWidgetPrivate *widgetPrivate = widget->d_func();
+ widgetPrivate->createExtra();
+ if (widgetPrivate->extraData()->glContext)
+ return;
+
+ QGLContext *ctx = NULL;
+
+ // For translucent top-level widgets we need alpha in the format.
+ if (widget->testAttribute(Qt::WA_TranslucentBackground)) {
+ QGLFormat modFormat(surfaceFormat);
+ modFormat.setSampleBuffers(false);
+ modFormat.setSamples(0);
+ modFormat.setAlpha(true);
+ ctx = new QGLContext(modFormat, widget);
+ } else
+ ctx = new QGLContext(surfaceFormat, widget);
+
+ ctx->create(qt_gl_share_context());
+
+ if (widget != qt_gl_share_widget())
+ ++(_qt_gl_share_widget()->widgetRefCount);
+
+#ifndef QT_NO_EGL
+ static bool checkedForNOKSwapRegion = false;
+ static bool haveNOKSwapRegion = false;
+
+ if (!checkedForNOKSwapRegion) {
+ haveNOKSwapRegion = QEgl::hasExtension("EGL_NOK_swap_region2");
+ checkedForNOKSwapRegion = true;
+
+ if (haveNOKSwapRegion)
+ qDebug() << "Found EGL_NOK_swap_region2 extension. Using partial updates.";
+ }
+
+ d_ptr->destructive_swap_buffers = true;
+ if (ctx->d_func()->eglContext->configAttrib(EGL_SURFACE_TYPE)&EGL_SWAP_BEHAVIOR_PRESERVED_BIT) {
+ EGLint swapBehavior;
+ if (eglQuerySurface(ctx->d_func()->eglContext->display(), ctx->d_func()->eglSurface
+ , EGL_SWAP_BEHAVIOR, &swapBehavior)) {
+ d_ptr->destructive_swap_buffers = (swapBehavior != EGL_BUFFER_PRESERVED);
+ }
+ }
+
+ d_ptr->swap_region_support = haveNOKSwapRegion;
+#endif
+
+ widgetPrivate->extraData()->glContext = ctx;
+
+ union { QGLContext **ctxPtrPtr; void **voidPtrPtr; };
+
+ connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(deleted(QObject*)));
+
+ voidPtrPtr = &widgetPrivate->extraData()->glContext;
+ d_ptr->contexts << ctxPtrPtr;
+#ifndef Q_OS_SYMBIAN
+ qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size();
+#endif
+}
+
+QGLContext *QGLWindowSurface::context() const
+{
+ return d_ptr->ctx;
+}
+
+QPaintDevice *QGLWindowSurface::paintDevice()
+{
+ updateGeometry();
+
+ if (d_ptr->pb)
+ return d_ptr->pb;
+
+ if (d_ptr->ctx)
+ return &d_ptr->glDevice;
+
+ QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
+ ctx->makeCurrent();
+
+ Q_ASSERT(d_ptr->fbo);
+ return d_ptr->fbo;
+}
+
+static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &src = QRectF());
+
+void QGLWindowSurface::beginPaint(const QRegion &)
+{
+ d_ptr->did_paint = true;
+ updateGeometry();
+
+ if (!context())
+ return;
+
+ int clearFlags = 0;
+
+ if (context()->d_func()->workaround_needsFullClearOnEveryFrame)
+ clearFlags = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
+ else if (context()->format().alpha())
+ clearFlags = GL_COLOR_BUFFER_BIT;
+
+ if (clearFlags) {
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(clearFlags);
+ }
+}
+
+void QGLWindowSurface::endPaint(const QRegion &rgn)
+{
+ if (context())
+ d_ptr->paintedRegion |= rgn;
+
+ d_ptr->buffers.clear();
+}
+
+static void blitTexture(QGLContext *ctx, GLuint texture, const QSize &viewport, const QSize &texSize, const QRect &targetRect, const QRect &sourceRect)
+{
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_BLEND);
+
+ glViewport(0, 0, viewport.width(), viewport.height());
+
+ QGLShaderProgram *blitProgram =
+ QGLEngineSharedShaders::shadersForContext(ctx)->blitProgram();
+ blitProgram->bind();
+ blitProgram->setUniformValue("imageTexture", 0 /*QT_IMAGE_TEXTURE_UNIT*/);
+
+ // The shader manager's blit program does not multiply the
+ // vertices by the pmv matrix, so we need to do the effect
+ // of the orthographic projection here ourselves.
+ QRectF r;
+ qreal w = viewport.width();
+ qreal h = viewport.height();
+ r.setLeft((targetRect.left() / w) * 2.0f - 1.0f);
+ if (targetRect.right() == (viewport.width() - 1))
+ r.setRight(1.0f);
+ else
+ r.setRight((targetRect.right() / w) * 2.0f - 1.0f);
+ r.setBottom((targetRect.top() / h) * 2.0f - 1.0f);
+ if (targetRect.bottom() == (viewport.height() - 1))
+ r.setTop(1.0f);
+ else
+ r.setTop((targetRect.bottom() / w) * 2.0f - 1.0f);
+
+ drawTexture(r, texture, texSize, sourceRect);
+}
+
+
+void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
+{
+ //### Find out why d_ptr->geometry_updated isn't always false.
+ // flush() should not be called when d_ptr->geometry_updated is true. It assumes that either
+ // d_ptr->fbo or d_ptr->pb is allocated and has the correct size.
+ if (d_ptr->geometry_updated)
+ return;
+
+ // did_paint is set to true in ::beginPaint. ::beginPaint means that we
+ // at least cleared the background (= painted something). In EGL API it's a
+ // mistake to call swapBuffers if nothing was painted unless
+ // EGL_BUFFER_PRESERVED is set. This check protects the flush func from
+ // being executed if it's for nothing.
+ if (!d_ptr->destructive_swap_buffers && !d_ptr->did_paint)
+ return;
+
+ QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget();
+ Q_ASSERT(parent);
+
+#if !defined(Q_WS_QPA)
+ if (!geometry().isValid())
+ return;
+#else
+ if (!size().isValid())
+ return;
+#endif
+
+ // Needed to support native child-widgets...
+ hijackWindow(parent);
+
+ QRect br = rgn.boundingRect().translated(offset);
+ br = br.intersected(window()->rect());
+ QPoint wOffset = qt_qwidget_data(parent)->wrect.topLeft();
+ QRect rect = br.translated(-offset - wOffset);
+
+ const GLenum target = GL_TEXTURE_2D;
+ Q_UNUSED(target);
+
+ if (QGLWindowSurface::swapBehavior == QGLWindowSurface::KillSwap)
+ return;
+
+ if (context()) {
+ context()->makeCurrent();
+
+ if (context()->format().doubleBuffer()) {
+#if !defined(QT_OPENGL_ES_2)
+ if (d_ptr->destructive_swap_buffers) {
+ glBindTexture(target, d_ptr->tex_id);
+
+ QVector<QRect> rects = d_ptr->paintedRegion.rects();
+ for (int i = 0; i < rects.size(); ++i) {
+ QRect br = rects.at(i);
+ if (br.isEmpty())
+ continue;
+
+ const uint bottom = window()->height() - (br.y() + br.height());
+ glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
+ }
+
+ glBindTexture(target, 0);
+
+ QRegion dirtyRegion = QRegion(window()->rect()) - d_ptr->paintedRegion;
+
+ if (!dirtyRegion.isEmpty()) {
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+#ifndef QT_OPENGL_ES
+ glOrtho(0, window()->width(), window()->height(), 0, -999999, 999999);
+#else
+ glOrthof(0, window()->width(), window()->height(), 0, -999999, 999999);
+#endif
+ glViewport(0, 0, window()->width(), window()->height());
+
+ QVector<QRect> rects = dirtyRegion.rects();
+ glColor4f(1, 1, 1, 1);
+ for (int i = 0; i < rects.size(); ++i) {
+ QRect rect = rects.at(i);
+ if (rect.isEmpty())
+ continue;
+
+ drawTexture(rect, d_ptr->tex_id, window()->size(), rect);
+ }
+ }
+ }
+#endif
+ bool doingPartialUpdate = false;
+ if (d_ptr->swap_region_support) {
+ if (QGLWindowSurface::swapBehavior == QGLWindowSurface::AutomaticSwap)
+ doingPartialUpdate = br.width() * br.height() < parent->geometry().width() * parent->geometry().height() * 0.2;
+ else if (QGLWindowSurface::swapBehavior == QGLWindowSurface::AlwaysPartialSwap)
+ doingPartialUpdate = true;
+ }
+
+ QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext);
+ if (widget != window()) {
+ if (initializeOffscreenTexture(window()->size()))
+ qWarning() << "QGLWindowSurface: Flushing to native child widget, may lead to significant performance loss";
+ glBindTexture(target, d_ptr->tex_id);
+
+ const uint bottom = window()->height() - (br.y() + br.height());
+ glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
+
+ glBindTexture(target, 0);
+
+ ctx->makeCurrent();
+ if (doingPartialUpdate)
+ blitTexture(ctx, d_ptr->tex_id, parent->size(), window()->size(), rect, br);
+ else
+ blitTexture(ctx, d_ptr->tex_id, parent->size(), window()->size(), parent->rect(), parent->rect().translated(offset + wOffset));
+ }
+
+ if (doingPartialUpdate)
+ ctx->d_func()->swapRegion(br);
+ else
+ ctx->swapBuffers();
+
+ d_ptr->paintedRegion = QRegion();
+ } else {
+ glFlush();
+ }
+ return;
+ }
+
+ QGLContext *previous_ctx = const_cast<QGLContext *>(QGLContext::currentContext());
+ QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext);
+
+ // QPainter::end() should have unbound the fbo, otherwise something is very wrong...
+ Q_ASSERT(!d_ptr->fbo || !d_ptr->fbo->isBound());
+
+ if (ctx != previous_ctx) {
+ ctx->makeCurrent();
+ }
+
+ QSize size = widget->rect().size();
+ if (d_ptr->destructive_swap_buffers && ctx->format().doubleBuffer()) {
+ rect = parent->rect();
+ br = rect.translated(wOffset + offset);
+ size = parent->size();
+ }
+
+ glDisable(GL_SCISSOR_TEST);
+
+ if (d_ptr->fbo && (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)) {
+ const int h = d_ptr->fbo->height();
+
+ const int sx0 = br.left();
+ const int sx1 = br.left() + br.width();
+ const int sy0 = h - (br.top() + br.height());
+ const int sy1 = h - br.top();
+
+ const int tx0 = rect.left();
+ const int tx1 = rect.left() + rect.width();
+ const int ty0 = parent->height() - (rect.top() + rect.height());
+ const int ty1 = parent->height() - rect.top();
+
+ if (window() == parent || d_ptr->fbo->format().samples() <= 1) {
+ if (ctx->d_ptr->current_fbo != 0)
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle());
+
+ glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
+ tx0, ty0, tx1, ty1,
+ GL_COLOR_BUFFER_BIT,
+ GL_NEAREST);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
+ } else {
+ // can't do sub-region blits with multisample FBOs
+ QGLFramebufferObject *temp = qgl_fbo_pool()->acquire(d_ptr->fbo->size(), QGLFramebufferObjectFormat());
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, temp->handle());
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle());
+
+ glBlitFramebufferEXT(0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(),
+ 0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(),
+ GL_COLOR_BUFFER_BIT,
+ GL_NEAREST);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, temp->handle());
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
+
+ glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
+ tx0, ty0, tx1, ty1,
+ GL_COLOR_BUFFER_BIT,
+ GL_NEAREST);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
+
+ qgl_fbo_pool()->release(temp);
+ }
+
+ ctx->d_ptr->current_fbo = 0;
+ }
+#if !defined(QT_OPENGL_ES_2)
+ else {
+ GLuint texture;
+ if (d_ptr->fbo) {
+ texture = d_ptr->fbo->texture();
+ } else {
+ d_ptr->pb->makeCurrent();
+ glBindTexture(target, d_ptr->pb_tex_id);
+ const uint bottom = window()->height() - (br.y() + br.height());
+ glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
+ texture = d_ptr->pb_tex_id;
+ glBindTexture(target, 0);
+ }
+
+ glDisable(GL_DEPTH_TEST);
+
+ if (d_ptr->fbo) {
+ d_ptr->fbo->release();
+ } else {
+ ctx->makeCurrent();
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+#ifndef QT_OPENGL_ES
+ glOrtho(0, size.width(), size.height(), 0, -999999, 999999);
+#else
+ glOrthof(0, size.width(), size.height(), 0, -999999, 999999);
+#endif
+ glViewport(0, 0, size.width(), size.height());
+
+ glColor4f(1, 1, 1, 1);
+ drawTexture(rect, texture, window()->size(), br);
+
+ if (d_ptr->fbo)
+ d_ptr->fbo->bind();
+ }
+#else
+ // OpenGL/ES 2.0 version of the fbo blit.
+ else if (d_ptr->fbo) {
+ Q_UNUSED(target);
+
+ if (d_ptr->fbo->isBound())
+ d_ptr->fbo->release();
+
+ blitTexture(ctx, d_ptr->fbo->texture(), size, window()->size(), rect, br);
+ }
+#endif
+
+ if (ctx->format().doubleBuffer())
+ ctx->swapBuffers();
+ else
+ glFlush();
+
+ d_ptr->did_paint = false;
+}
+
+
+#if !defined(Q_WS_QPA)
+void QGLWindowSurface::setGeometry(const QRect &rect)
+{
+ QWindowSurface::setGeometry(rect);
+ d_ptr->geometry_updated = true;
+}
+#else
+void QGLWindowSurface::resize(const QSize &size)
+{
+ QWindowSurface::resize(size);
+ d_ptr->geometry_updated = true;
+}
+#endif
+
+void QGLWindowSurface::updateGeometry() {
+ if (!d_ptr->geometry_updated)
+ return;
+ d_ptr->geometry_updated = false;
+
+ bool hijack(true);
+ QWidgetPrivate *wd = window()->d_func();
+ if (wd->extraData() && wd->extraData()->glContext) {
+#ifdef Q_OS_SYMBIAN // Symbian needs to recreate the context when native window size changes
+ if (d_ptr->size != geometry().size()) {
+ if (window() != qt_gl_share_widget())
+ --(_qt_gl_share_widget()->widgetRefCount);
+
+ delete wd->extraData()->glContext;
+ wd->extraData()->glContext = 0;
+ d_ptr->ctx = 0;
+ }
+ else
+#endif
+ {
+ hijack = false; // we already have gl context for widget
+ }
+ }
+
+ if (hijack)
+ hijackWindow(window());
+
+ QGLContext *ctx = reinterpret_cast<QGLContext *>(wd->extraData()->glContext);
+
+#ifdef Q_WS_MAC
+ ctx->updatePaintDevice();
+#endif
+
+ QSize surfSize = geometry().size();
+
+ if (surfSize.width() <= 0 || surfSize.height() <= 0)
+ return;
+
+ if (d_ptr->size == surfSize)
+ return;
+
+ d_ptr->size = surfSize;
+
+ if (d_ptr->ctx) {
+#ifndef QT_OPENGL_ES_2
+ if (d_ptr->destructive_swap_buffers)
+ initializeOffscreenTexture(surfSize);
+#endif
+ return;
+ }
+
+ const GLenum target = GL_TEXTURE_2D;
+ if (d_ptr->destructive_swap_buffers
+ && (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject)
+ && (d_ptr->fbo || !d_ptr->tried_fbo)
+ && qt_gl_preferGL2Engine())
+ {
+ d_ptr->tried_fbo = true;
+ ctx->d_ptr->internal_context = true;
+ ctx->makeCurrent();
+ delete d_ptr->fbo;
+
+ QGLFramebufferObjectFormat format;
+ format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format.setInternalTextureFormat(GLenum(GL_RGBA));
+ format.setTextureTarget(target);
+
+ if (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)
+ format.setSamples(8);
+
+ d_ptr->fbo = new QGLFramebufferObject(surfSize, format);
+
+ if (d_ptr->fbo->isValid()) {
+ qDebug() << "Created Window Surface FBO" << surfSize
+ << "with samples" << d_ptr->fbo->format().samples();
+ return;
+ } else {
+ qDebug() << "QGLWindowSurface: Failed to create valid FBO, falling back";
+ delete d_ptr->fbo;
+ d_ptr->fbo = 0;
+ }
+ }
+
+#if !defined(QT_OPENGL_ES_2) && !defined(Q_WS_QPA) //QPA doesn't support pixelbuffers
+ if (d_ptr->destructive_swap_buffers && (d_ptr->pb || !d_ptr->tried_pb)) {
+ d_ptr->tried_pb = true;
+
+ if (d_ptr->pb) {
+ d_ptr->pb->makeCurrent();
+ glDeleteTextures(1, &d_ptr->pb_tex_id);
+ }
+
+ delete d_ptr->pb;
+
+ d_ptr->pb = new QGLPixelBuffer(surfSize.width(), surfSize.height(),
+ QGLFormat(QGL::SampleBuffers | QGL::StencilBuffer | QGL::DepthBuffer),
+ qt_gl_share_widget());
+
+ if (d_ptr->pb->isValid()) {
+ qDebug() << "Created Window Surface Pixelbuffer, Sample buffers:" << d_ptr->pb->format().sampleBuffers();
+ d_ptr->pb->makeCurrent();
+
+ glGenTextures(1, &d_ptr->pb_tex_id);
+ glBindTexture(target, d_ptr->pb_tex_id);
+ glTexImage2D(target, 0, GL_RGBA, surfSize.width(), surfSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+
+ glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glBindTexture(target, 0);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999);
+
+ d_ptr->pb->d_ptr->qctx->d_func()->internal_context = true;
+ return;
+ } else {
+ qDebug() << "QGLWindowSurface: Failed to create valid pixelbuffer, falling back";
+ delete d_ptr->pb;
+ d_ptr->pb = 0;
+ }
+ }
+#endif // !defined(QT_OPENGL_ES_2) !defined(Q_WS_QPA)
+
+ ctx->makeCurrent();
+
+#ifndef QT_OPENGL_ES_2
+ if (d_ptr->destructive_swap_buffers)
+ initializeOffscreenTexture(surfSize);
+#endif
+#ifndef Q_OS_SYMBIAN
+ qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;
+#endif
+ d_ptr->ctx = ctx;
+ d_ptr->ctx->d_ptr->internal_context = true;
+}
+
+bool QGLWindowSurface::initializeOffscreenTexture(const QSize &size)
+{
+ if (size == d_ptr->textureSize)
+ return false;
+
+ glGenTextures(1, &d_ptr->tex_id);
+ glBindTexture(GL_TEXTURE_2D, d_ptr->tex_id);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ d_ptr->textureSize = size;
+ return true;
+}
+
+bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ // this code randomly fails currently for unknown reasons
+ return false;
+
+ if (!d_ptr->pb)
+ return false;
+
+ d_ptr->pb->makeCurrent();
+
+ QRect br = area.boundingRect();
+
+#if 0
+ // ## workaround driver issue (scrolling by these deltas is unbearably slow for some reason)
+ // ## maybe we should use glCopyTexSubImage insteadk
+ if (dx == 1 || dx == -1 || dy == 1 || dy == -1 || dy == 2)
+ return false;
+
+ glRasterPos2i(br.x() + dx, br.y() + br.height() + dy);
+ glCopyPixels(br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), GL_COLOR);
+ return true;
+#endif
+
+ const GLenum target = GL_TEXTURE_2D;
+
+ glBindTexture(target, d_ptr->tex_id);
+ glCopyTexImage2D(target, 0, GL_RGBA, br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), 0);
+ glBindTexture(target, 0);
+
+ drawTexture(br.translated(dx, dy), d_ptr->tex_id, window()->size());
+
+ return true;
+}
+
+static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &br)
+{
+ const GLenum target = GL_TEXTURE_2D;
+ QRectF src = br.isEmpty()
+ ? QRectF(QPointF(), texSize)
+ : QRectF(QPointF(br.x(), texSize.height() - br.bottom()), br.size());
+
+ if (target == GL_TEXTURE_2D) {
+ qreal width = texSize.width();
+ qreal height = texSize.height();
+
+ src.setLeft(src.left() / width);
+ src.setRight(src.right() / width);
+ src.setTop(src.top() / height);
+ src.setBottom(src.bottom() / height);
+ }
+
+ const GLfloat tx1 = src.left();
+ const GLfloat tx2 = src.right();
+ const GLfloat ty1 = src.top();
+ const GLfloat ty2 = src.bottom();
+
+ GLfloat texCoordArray[4*2] = {
+ tx1, ty2, tx2, ty2, tx2, ty1, tx1, ty1
+ };
+
+ GLfloat vertexArray[4*2];
+ extern void qt_add_rect_to_array(const QRectF &r, GLfloat *array); // qpaintengine_opengl.cpp
+ qt_add_rect_to_array(rect, vertexArray);
+
+#if !defined(QT_OPENGL_ES_2)
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
+
+ glBindTexture(target, tex_id);
+ glEnable(target);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ glDisable(target);
+ glBindTexture(target, 0);
+#else
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexArray);
+ glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, texCoordArray);
+
+ glBindTexture(target, tex_id);
+
+ glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+ glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+ glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
+
+ glBindTexture(target, 0);
+#endif
+}
+
+QImage *QGLWindowSurface::buffer(const QWidget *widget)
+{
+ QImage image;
+
+ if (d_ptr->pb)
+ image = d_ptr->pb->toImage();
+ else if (d_ptr->fbo)
+ image = d_ptr->fbo->toImage();
+
+ if (image.isNull())
+ return 0;
+
+ QRect rect = widget->rect();
+ rect.translate(widget->mapTo(widget->window(), QPoint()));
+
+ QImage subImage = image.copy(rect);
+ d_ptr->buffers << subImage;
+ return &d_ptr->buffers.last();
+}
+
+QWindowSurface::WindowSurfaceFeatures QGLWindowSurface::features() const
+{
+ WindowSurfaceFeatures features = 0;
+ if (!d_ptr->destructive_swap_buffers || d_ptr->swap_region_support)
+ features |= PartialUpdates;
+ if (!d_ptr->destructive_swap_buffers)
+ features |= PreservedContents;
+ return features;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/opengl/qwindowsurface_gl_p.h b/src/opengl/qwindowsurface_gl_p.h
new file mode 100644
index 0000000000..c71ce59c4d
--- /dev/null
+++ b/src/opengl/qwindowsurface_gl_p.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_GL_P_H
+#define QWINDOWSURFACE_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 <qglobal.h>
+#include <qgl.h>
+#include <private/qwindowsurface_p.h>
+#include <private/qglpaintdevice_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+class QPoint;
+class QRegion;
+class QWidget;
+struct QGLWindowSurfacePrivate;
+
+Q_OPENGL_EXPORT QGLWidget* qt_gl_share_widget();
+Q_OPENGL_EXPORT void qt_destroy_gl_share_widget();
+
+class QGLWindowSurfaceGLPaintDevice : public QGLPaintDevice
+{
+public:
+ QPaintEngine* paintEngine() const;
+ void endPaint();
+ QSize size() const;
+ int metric(PaintDeviceMetric m) const;
+ QGLContext* context() const;
+ QGLWindowSurfacePrivate* d;
+};
+
+class Q_OPENGL_EXPORT QGLWindowSurface : public QObject, public QWindowSurface // , public QPaintDevice
+{
+ Q_OBJECT
+public:
+ QGLWindowSurface(QWidget *window);
+ ~QGLWindowSurface();
+
+ QPaintDevice *paintDevice();
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+
+#if !defined(Q_WS_QPA)
+ void setGeometry(const QRect &rect);
+#else
+ virtual void resize(const QSize &size);
+#endif
+
+ void updateGeometry();
+ bool scroll(const QRegion &area, int dx, int dy);
+
+ void beginPaint(const QRegion &region);
+ void endPaint(const QRegion &region);
+
+ QImage *buffer(const QWidget *widget);
+
+ WindowSurfaceFeatures features() const;
+
+ QGLContext *context() const;
+
+ static QGLFormat surfaceFormat;
+
+ enum SwapMode { AutomaticSwap, AlwaysFullSwap, AlwaysPartialSwap, KillSwap };
+ static SwapMode swapBehavior;
+
+private slots:
+ void deleted(QObject *object);
+
+private:
+ void hijackWindow(QWidget *widget);
+ bool initializeOffscreenTexture(const QSize &size);
+
+ QGLWindowSurfacePrivate *d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_GL_P_H
+
diff --git a/src/opengl/qwindowsurface_x11gl.cpp b/src/opengl/qwindowsurface_x11gl.cpp
new file mode 100644
index 0000000000..78603f9d11
--- /dev/null
+++ b/src/opengl/qwindowsurface_x11gl.cpp
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QTime>
+#include <QDebug>
+
+#include <private/qt_x11_p.h>
+#include <private/qimagepixmapcleanuphooks_p.h>
+
+#include "qwindowsurface_x11gl_p.h"
+#include "qpixmapdata_x11gl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QX11GLWindowSurface::QX11GLWindowSurface(QWidget* window)
+ : QWindowSurface(window), m_windowGC(0), m_pixmapGC(0), m_window(window)
+{
+}
+
+QX11GLWindowSurface::~QX11GLWindowSurface()
+{
+ if (m_windowGC)
+ XFree(m_windowGC);
+ if (m_pixmapGC)
+ XFree(m_pixmapGC);
+}
+
+QPaintDevice *QX11GLWindowSurface::paintDevice()
+{
+ return &m_backBuffer;
+}
+
+extern void *qt_getClipRects(const QRegion &r, int &num); // in qpaintengine_x11.cpp
+
+void QX11GLWindowSurface::flush(QWidget *widget, const QRegion &widgetRegion, const QPoint &offset)
+{
+ // We don't need to know the widget which initiated the flush. Instead we just use the offset
+ // to translate the widgetRegion:
+ Q_UNUSED(widget);
+
+ if (m_backBuffer.isNull()) {
+ qDebug("QX11GLWindowSurface::flush() - backBuffer is null, not flushing anything");
+ return;
+ }
+
+ Q_ASSERT(window()->size() != m_backBuffer.size());
+
+ // Wait for all GL rendering to the back buffer pixmap to complete before trying to
+ // copy it to the window. We do this by making sure the pixmap's context is current
+ // and then call eglWaitClient. The EGL 1.4 spec says eglWaitClient doesn't have to
+ // block, just that "All rendering calls...are guaranteed to be executed before native
+ // rendering calls". This makes it potentially less expensive than glFinish.
+ QGLContext* ctx = static_cast<QX11GLPixmapData*>(m_backBuffer.data_ptr().data())->context();
+ if (QGLContext::currentContext() != ctx && ctx && ctx->isValid())
+ ctx->makeCurrent();
+ eglWaitClient();
+
+ if (m_windowGC == 0) {
+ XGCValues attribs;
+ attribs.graphics_exposures = False;
+ m_windowGC = XCreateGC(X11->display, m_window->handle(), GCGraphicsExposures, &attribs);
+ }
+
+ int rectCount;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(widgetRegion, rectCount);
+ if (rectCount <= 0)
+ return;
+
+ XSetClipRectangles(X11->display, m_windowGC, 0, 0, rects, rectCount, YXBanded);
+
+ QRect dirtyRect = widgetRegion.boundingRect().translated(-offset);
+ XCopyArea(X11->display, m_backBuffer.handle(), m_window->handle(), m_windowGC,
+ dirtyRect.x(), dirtyRect.y(), dirtyRect.width(), dirtyRect.height(),
+ dirtyRect.x(), dirtyRect.y());
+
+ // Make sure the blit of the update from the back buffer to the window completes
+ // before allowing rendering to start again to the back buffer. Otherwise the GPU
+ // might start rendering to the back buffer again while the blit takes place.
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+}
+
+void QX11GLWindowSurface::setGeometry(const QRect &rect)
+{
+ if (rect.width() > m_backBuffer.size().width() || rect.height() > m_backBuffer.size().height()) {
+ QX11GLPixmapData *pd = new QX11GLPixmapData;
+ QSize newSize = rect.size();
+ pd->resize(newSize.width(), newSize.height());
+ m_backBuffer = QPixmap(pd);
+ if (window()->testAttribute(Qt::WA_TranslucentBackground))
+ m_backBuffer.fill(Qt::transparent);
+ if (m_pixmapGC) {
+ XFreeGC(X11->display, m_pixmapGC);
+ m_pixmapGC = 0;
+ }
+ }
+
+ QWindowSurface::setGeometry(rect);
+}
+
+bool QX11GLWindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ if (m_backBuffer.isNull())
+ return false;
+
+ Q_ASSERT(m_backBuffer.data_ptr()->classId() == QPixmapData::X11Class);
+
+ // Make sure all GL rendering is complete before starting the scroll operation:
+ QGLContext* ctx = static_cast<QX11GLPixmapData*>(m_backBuffer.data_ptr().data())->context();
+ if (QGLContext::currentContext() != ctx && ctx && ctx->isValid())
+ ctx->makeCurrent();
+ eglWaitClient();
+
+ if (!m_pixmapGC)
+ m_pixmapGC = XCreateGC(X11->display, m_backBuffer.handle(), 0, 0);
+
+ foreach (const QRect& rect, area.rects()) {
+ XCopyArea(X11->display, m_backBuffer.handle(), m_backBuffer.handle(), m_pixmapGC,
+ rect.x(), rect.y(), rect.width(), rect.height(),
+ rect.x()+dx, rect.y()+dy);
+ }
+
+ // Make sure the scroll operation is complete before allowing GL rendering to resume
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+
+ return true;
+}
+
+
+QPixmap QX11GLWindowSurface::grabWidget(const QWidget *widget, const QRect& rect) const
+{
+ if (!widget || m_backBuffer.isNull())
+ return QPixmap();
+
+ QRect srcRect;
+
+ // make sure the rect is inside the widget & clip to widget's rect
+ if (!rect.isEmpty())
+ srcRect = rect & widget->rect();
+ else
+ srcRect = widget->rect();
+
+ if (srcRect.isEmpty())
+ return QPixmap();
+
+ // If it's a child widget we have to translate the coordinates
+ if (widget != window())
+ srcRect.translate(widget->mapTo(window(), QPoint(0, 0)));
+
+ QPixmap::x11SetDefaultScreen(widget->x11Info().screen());
+
+ QX11PixmapData *pmd = new QX11PixmapData(QPixmapData::PixmapType);
+ pmd->resize(srcRect.width(), srcRect.height());
+ QPixmap px(pmd);
+
+ GC tmpGc = XCreateGC(X11->display, m_backBuffer.handle(), 0, 0);
+
+ // Make sure all GL rendering is complete before copying the window
+ QGLContext* ctx = static_cast<QX11GLPixmapData*>(m_backBuffer.pixmapData())->context();
+ if (QGLContext::currentContext() != ctx && ctx && ctx->isValid())
+ ctx->makeCurrent();
+ eglWaitClient();
+
+ // Copy srcRect from the backing store to the new pixmap
+ XSetGraphicsExposures(X11->display, tmpGc, False);
+ XCopyArea(X11->display, m_backBuffer.handle(), px.handle(), tmpGc,
+ srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(), 0, 0);
+ XFreeGC(X11->display, tmpGc);
+
+ // Wait until the copy has finised before allowing more rendering into the back buffer
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+
+ return px;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qwindowsurface_x11gl_p.h b/src/opengl/qwindowsurface_x11gl_p.h
new file mode 100644
index 0000000000..bb65133c94
--- /dev/null
+++ b/src/opengl/qwindowsurface_x11gl_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_X11GL_P_H
+#define QWINDOWSURFACE_X11GL_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 <private/qwindowsurface_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QX11GLWindowSurface : public QWindowSurface
+{
+public:
+ QX11GLWindowSurface(QWidget* window);
+ virtual ~QX11GLWindowSurface();
+
+ // Inherreted from QWindowSurface
+ QPaintDevice *paintDevice();
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+ void setGeometry(const QRect &rect);
+ bool scroll(const QRegion &area, int dx, int dy);
+ QPixmap grabWidget(const QWidget *widget, const QRect& rectangle = QRect()) const;
+
+private:
+ GC m_windowGC;
+ GC m_pixmapGC;
+ QPixmap m_backBuffer;
+ QWidget *m_window;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_X11GL_P_H
diff --git a/src/opengl/util/README-GLSL b/src/opengl/util/README-GLSL
new file mode 100644
index 0000000000..ff20eb45b2
--- /dev/null
+++ b/src/opengl/util/README-GLSL
@@ -0,0 +1,18 @@
+Use of GLSL for vertex and fragment programs in Qt
+---------------------------------------------------
+
+We don't compile the *.glsl files because we don't want the build process of
+Qt to require cgc from nVidia to build the fragment programs.
+
+The script src/opengl/util/glsl_to_include.sh will compile a GLSL program to a file
+that can be included in a C(++) program. The file is the output from cgc
+quoted as a string.
+
+This can be done manually by:
+
+./glsl_to_include.sh radial.glsl
+./glsl_to_include.sh conical.glsl
+
+This will produce the files radial.frag and radial.glsl_quoted.
+(and also conical.frag and conical.glsl_quoted)
+These files are included by qpaintengine_opengl.cpp
diff --git a/src/opengl/util/brush_painter.glsl b/src/opengl/util/brush_painter.glsl
new file mode 100644
index 0000000000..6c4acdf924
--- /dev/null
+++ b/src/opengl/util/brush_painter.glsl
@@ -0,0 +1,7 @@
+// fast brush painter for composition modes which can be implemented with blendfuncs
+// no mask, used for fast filling of aliased primitives (or multisampled)
+
+void main()
+{
+ gl_FragColor = brush();
+}
diff --git a/src/opengl/util/brushes.conf b/src/opengl/util/brushes.conf
new file mode 100644
index 0000000000..93e2b3b295
--- /dev/null
+++ b/src/opengl/util/brushes.conf
@@ -0,0 +1,6 @@
+FRAGMENT_PROGRAM_BRUSH_SOLID solid_brush.glsl
+FRAGMENT_PROGRAM_BRUSH_RADIAL radial_brush.glsl
+FRAGMENT_PROGRAM_BRUSH_CONICAL conical_brush.glsl
+FRAGMENT_PROGRAM_BRUSH_LINEAR linear_brush.glsl
+FRAGMENT_PROGRAM_BRUSH_TEXTURE texture_brush.glsl
+FRAGMENT_PROGRAM_BRUSH_PATTERN pattern_brush.glsl
diff --git a/src/opengl/util/composition_mode_colorburn.glsl b/src/opengl/util/composition_mode_colorburn.glsl
new file mode 100644
index 0000000000..c913b9737f
--- /dev/null
+++ b/src/opengl/util/composition_mode_colorburn.glsl
@@ -0,0 +1,13 @@
+// Dca' = Sca.Da + Dca.Sa <= Sa.Da ?
+// Sca.(1 - Da) + Dca.(1 - Sa)
+// Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = mix(src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a),
+ src.a * (src.rgb * dst.a + dst.rgb * src.a - src.a * dst.a) / max(src.rgb, 0.00001) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a),
+ step(src.a * dst.a, src.rgb * dst.a + dst.rgb * src.a));
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_colordodge.glsl b/src/opengl/util/composition_mode_colordodge.glsl
new file mode 100644
index 0000000000..b75e83cf4e
--- /dev/null
+++ b/src/opengl/util/composition_mode_colordodge.glsl
@@ -0,0 +1,15 @@
+// Dca' = Sca.Da + Dca.Sa <= Sa.Da ?
+// Dca.Sa/(1 - Sca/Sa) + Sca.(1 - Da) + Dca.(1 - Sa) :
+// Sa.Da + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ vec3 temp = src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
+ result.rgb = mix(dst.rgb * src.a / max(1.0 - src.rgb / max(src.a, 0.000001), 0.000001) + temp,
+ src.a * dst.a + temp,
+ step(src.a * dst.a, src.rgb * dst.a + dst.rgb * src.a));
+
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_darken.glsl b/src/opengl/util/composition_mode_darken.glsl
new file mode 100644
index 0000000000..8bbb82b3ce
--- /dev/null
+++ b/src/opengl/util/composition_mode_darken.glsl
@@ -0,0 +1,9 @@
+// Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = min(src.rgb * dst.a, dst.rgb * src.a) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_difference.glsl b/src/opengl/util/composition_mode_difference.glsl
new file mode 100644
index 0000000000..3c46ec71f2
--- /dev/null
+++ b/src/opengl/util/composition_mode_difference.glsl
@@ -0,0 +1,9 @@
+// Dca' = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = src.rgb + dst.rgb - 2.0 * min(src.rgb * dst.a, dst.rgb * src.a);
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_exclusion.glsl b/src/opengl/util/composition_mode_exclusion.glsl
new file mode 100644
index 0000000000..59c2da99ea
--- /dev/null
+++ b/src/opengl/util/composition_mode_exclusion.glsl
@@ -0,0 +1,9 @@
+// Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = (src.rgb * dst.a + dst.rgb * src.a - 2.0 * src.rgb * dst.rgb) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_hardlight.glsl b/src/opengl/util/composition_mode_hardlight.glsl
new file mode 100644
index 0000000000..4ea355029d
--- /dev/null
+++ b/src/opengl/util/composition_mode_hardlight.glsl
@@ -0,0 +1,14 @@
+// Dca' = 2.Sca < Sa ?
+// 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) :
+// Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = mix(2.0 * src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a),
+ src.a * dst.a - 2.0 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a),
+ step(src.a, 2.0 * src.rgb));
+ result.a = src.a + dst.a - src.a * dst.a;
+
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_lighten.glsl b/src/opengl/util/composition_mode_lighten.glsl
new file mode 100644
index 0000000000..13ef507a4c
--- /dev/null
+++ b/src/opengl/util/composition_mode_lighten.glsl
@@ -0,0 +1,9 @@
+// Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = max(src.rgb * dst.a, dst.rgb * src.a) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_multiply.glsl b/src/opengl/util/composition_mode_multiply.glsl
new file mode 100644
index 0000000000..f90b7f00ad
--- /dev/null
+++ b/src/opengl/util/composition_mode_multiply.glsl
@@ -0,0 +1,9 @@
+// Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_overlay.glsl b/src/opengl/util/composition_mode_overlay.glsl
new file mode 100644
index 0000000000..f621bdee96
--- /dev/null
+++ b/src/opengl/util/composition_mode_overlay.glsl
@@ -0,0 +1,13 @@
+// Dca' = 2.Dca < Da ?
+// 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+// Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = mix(2.0 * src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a),
+ src.a * dst.a - 2.0 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a),
+ step(dst.a, 2.0 * dst.rgb));
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_screen.glsl b/src/opengl/util/composition_mode_screen.glsl
new file mode 100644
index 0000000000..8f4f010032
--- /dev/null
+++ b/src/opengl/util/composition_mode_screen.glsl
@@ -0,0 +1,6 @@
+// Dca' = Sca + Dca - Sca.Dca
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ return src + dst - src * dst;
+}
diff --git a/src/opengl/util/composition_mode_softlight.glsl b/src/opengl/util/composition_mode_softlight.glsl
new file mode 100644
index 0000000000..e4c1f89156
--- /dev/null
+++ b/src/opengl/util/composition_mode_softlight.glsl
@@ -0,0 +1,22 @@
+// if 2.Sca <= Sa
+// Dca' = Dca.(Sa + (2.Sca - Sa).(1 - Dca/Da)) + Sca.(1 - Da) + Dca.(1 - Sa)
+// otherwise if 2.Sca > Sa and 4.Dca <= Da
+// Dca' = Dca.Sa + Da.(2.Sca - Sa).(4.Dca/Da.(4.Dca/Da + 1).(Dca/Da - 1) + 7.Dca/Da) + Sca.(1 - Da) + Dca.(1 - Sa)
+// otherwise if 2.Sca > Sa and 4.Dca > Da
+// Dca' = Dca.Sa + Da.(2.Sca - Sa).((Dca/Da)^0.5 - Dca/Da) + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ float da = max(dst.a, 0.00001);
+ vec3 dst_np = dst.rgb / da;
+ result.rgb = mix(dst.rgb * (src.a + (2.0 * src.rgb - src.a) * (1.0 - dst_np)),
+ mix(dst.rgb * src.a + dst.a * (2.0 * src.rgb - src.a) * ((16.0 * dst_np - 12.0) * dst_np + 3.0) * dst_np,
+ dst.rgb * src.a + dst.a * (2.0 * src.rgb - src.a) * (sqrt(dst_np) - dst_np),
+ step(dst.a, 4.0 * dst.rgb)),
+ step(src.a, 2.0 * src.rgb))
+ + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_modes.conf b/src/opengl/util/composition_modes.conf
new file mode 100644
index 0000000000..df52830b99
--- /dev/null
+++ b/src/opengl/util/composition_modes.conf
@@ -0,0 +1,12 @@
+COMPOSITION_MODES_SIMPLE_PORTER_DUFF simple_porter_duff.glsl
+COMPOSITION_MODES_MULTIPLY composition_mode_multiply.glsl
+COMPOSITION_MODES_SCREEN composition_mode_screen.glsl
+COMPOSITION_MODES_OVERLAY composition_mode_overlay.glsl
+COMPOSITION_MODES_DARKEN composition_mode_darken.glsl
+COMPOSITION_MODES_LIGHTEN composition_mode_lighten.glsl
+COMPOSITION_MODES_COLORDODGE composition_mode_colordodge.glsl
+COMPOSITION_MODES_COLORBURN composition_mode_colorburn.glsl
+COMPOSITION_MODES_HARDLIGHT composition_mode_hardlight.glsl
+COMPOSITION_MODES_SOFTLIGHT composition_mode_softlight.glsl
+COMPOSITION_MODES_DIFFERENCE composition_mode_difference.glsl
+COMPOSITION_MODES_EXCLUSION composition_mode_exclusion.glsl
diff --git a/src/opengl/util/conical_brush.glsl b/src/opengl/util/conical_brush.glsl
new file mode 100644
index 0000000000..b3ec1d7efe
--- /dev/null
+++ b/src/opengl/util/conical_brush.glsl
@@ -0,0 +1,27 @@
+// conical gradient shader
+#define M_PI 3.14159265358979323846
+uniform sampler1D palette;
+uniform float angle;
+uniform vec3 inv_matrix_m0;
+uniform vec3 inv_matrix_m1;
+uniform vec3 inv_matrix_m2;
+
+vec4 brush()
+{
+ mat3 mat;
+
+ mat[0] = inv_matrix_m0;
+ mat[1] = inv_matrix_m1;
+ mat[2] = inv_matrix_m2;
+
+ vec3 hcoords = mat * vec3(gl_FragCoord.xy, 1);
+ vec2 A = hcoords.xy / hcoords.z;
+
+/* float val = fmod((atan2(-A.y, A.x) + angle) / (2.0 * M_PI), 1); */
+ if (abs(A.y) == abs(A.x))
+ A.y += 0.002;
+ float t = (atan(-A.y, A.x) + angle) / (2.0 * M_PI);
+ float val = t - floor(t);
+ return texture1D(palette, val);
+}
+
diff --git a/src/opengl/util/ellipse_aa.glsl b/src/opengl/util/ellipse_aa.glsl
new file mode 100644
index 0000000000..257e3bbd47
--- /dev/null
+++ b/src/opengl/util/ellipse_aa.glsl
@@ -0,0 +1,58 @@
+uniform vec3 inv_matrix_m0;
+uniform vec3 inv_matrix_m1;
+uniform vec3 inv_matrix_m2;
+
+uniform vec2 ellipse_offset;
+
+// ellipse equation
+
+// s^2/a^2 + t^2/b^2 = 1
+//
+// implicit equation:
+// g(s,t) = 1 - s^2/r_s^2 - t^2/r_t^2
+
+// distance from ellipse:
+// grad = [dg/dx dg/dy]
+// d(s, t) ~= g(s, t) / |grad|
+
+// dg/dx = dg/ds * ds/dx + dg/dt * dt/dx
+// dg/dy = dg/ds * ds/dy + dg/dt * dt/dy
+
+float ellipse_aa()
+{
+ mat3 mat;
+
+ mat[0] = inv_matrix_m0;
+ mat[1] = inv_matrix_m1;
+ mat[2] = inv_matrix_m2;
+
+ vec3 hcoords = mat * vec3(gl_FragCoord.xy + ellipse_offset, 1);
+ float inv_w = 1.0 / hcoords.z;
+ vec2 st = hcoords.xy * inv_w;
+
+ vec4 xy = vec4(mat[0].xy, mat[1].xy);
+ vec2 h = vec2(mat[0].z, mat[1].z);
+
+ vec4 dstdxy = (xy.xzyw - h.xyxy * st.xxyy) * inv_w;
+
+ //dstdxy.x = (mat[0].x - mat[0].z * st.x) * inv_w; // ds/dx
+ //dstdxy.y = (mat[1].x - mat[1].z * st.x) * inv_w; // ds/dy
+ //dstdxy.z = (mat[0].y - mat[0].z * st.y) * inv_w; // dt/dx
+ //dstdxy.w = (mat[1].y - mat[1].z * st.y) * inv_w; // dt/dy
+
+ vec2 inv_r = gl_TexCoord[0].xy;
+ vec2 n = st * inv_r;
+ float g = 1.0 - dot(n, n);
+
+ vec2 dgdst = -2.0 * n * inv_r;
+
+ vec2 grad = vec2(dot(dgdst, dstdxy.xz),
+ dot(dgdst, dstdxy.yw));
+
+ return smoothstep(-0.5, 0.5, g * inversesqrt(dot(grad, grad)));
+}
+
+void main()
+{
+ gl_FragColor = ellipse_aa().xxxx;
+}
diff --git a/src/opengl/util/fast_painter.glsl b/src/opengl/util/fast_painter.glsl
new file mode 100644
index 0000000000..63f5e5f3be
--- /dev/null
+++ b/src/opengl/util/fast_painter.glsl
@@ -0,0 +1,19 @@
+// fast painter for composition modes which can be implemented with blendfuncs
+
+uniform sampler2D mask_texture;
+uniform vec2 inv_mask_size;
+uniform vec2 mask_offset;
+uniform vec4 mask_channel;
+
+float mask()
+{
+ return dot(mask_channel, texture2D(mask_texture, (gl_FragCoord.xy + mask_offset) * inv_mask_size));
+}
+
+void main()
+{
+ // combine clip and coverage channels
+ float mask_alpha = mask();
+
+ gl_FragColor = brush() * mask_alpha;
+}
diff --git a/src/opengl/util/fragmentprograms_p.h b/src/opengl/util/fragmentprograms_p.h
new file mode 100644
index 0000000000..023b3ac7e8
--- /dev/null
+++ b/src/opengl/util/fragmentprograms_p.h
@@ -0,0 +1,7287 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FRAGMENTPROGRAMS_P_H
+#define FRAGMENTPROGRAMS_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.
+//
+
+enum FragmentVariable {
+ VAR_BRUSH_TEXTURE,
+ VAR_LINEAR,
+ VAR_INV_MATRIX_M1,
+ VAR_INV_MASK_SIZE,
+ VAR_INV_MATRIX_M2,
+ VAR_PORTERDUFF_AB,
+ VAR_MASK_CHANNEL,
+ VAR_ELLIPSE_OFFSET,
+ VAR_PORTERDUFF_XYZ,
+ VAR_INV_DST_SIZE,
+ VAR_MASK_TEXTURE,
+ VAR_DST_TEXTURE,
+ VAR_PALETTE,
+ VAR_MASK_OFFSET,
+ VAR_INV_BRUSH_TEXTURE_SIZE,
+ VAR_FMP2_M_RADIUS2,
+ VAR_FMP,
+ VAR_INV_MATRIX_M0,
+ VAR_ANGLE
+};
+
+enum FragmentBrushType {
+ FRAGMENT_PROGRAM_BRUSH_SOLID,
+ FRAGMENT_PROGRAM_BRUSH_RADIAL,
+ FRAGMENT_PROGRAM_BRUSH_CONICAL,
+ FRAGMENT_PROGRAM_BRUSH_LINEAR,
+ FRAGMENT_PROGRAM_BRUSH_TEXTURE,
+ FRAGMENT_PROGRAM_BRUSH_PATTERN
+};
+
+enum FragmentCompositionModeType {
+ COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ COMPOSITION_MODES_MULTIPLY,
+ COMPOSITION_MODES_SCREEN,
+ COMPOSITION_MODES_OVERLAY,
+ COMPOSITION_MODES_DARKEN,
+ COMPOSITION_MODES_LIGHTEN,
+ COMPOSITION_MODES_COLORDODGE,
+ COMPOSITION_MODES_COLORBURN,
+ COMPOSITION_MODES_HARDLIGHT,
+ COMPOSITION_MODES_SOFTLIGHT,
+ COMPOSITION_MODES_DIFFERENCE,
+ COMPOSITION_MODES_EXCLUSION,
+ COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ COMPOSITION_MODES_MULTIPLY_NOMASK,
+ COMPOSITION_MODES_SCREEN_NOMASK,
+ COMPOSITION_MODES_OVERLAY_NOMASK,
+ COMPOSITION_MODES_DARKEN_NOMASK,
+ COMPOSITION_MODES_LIGHTEN_NOMASK,
+ COMPOSITION_MODES_COLORDODGE_NOMASK,
+ COMPOSITION_MODES_COLORBURN_NOMASK,
+ COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ COMPOSITION_MODES_EXCLUSION_NOMASK,
+ COMPOSITION_MODE_BLEND_MODE_MASK,
+ COMPOSITION_MODE_BLEND_MODE_NOMASK
+};
+
+enum FragmentMaskType {
+ FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA,
+ FRAGMENT_PROGRAM_MASK_ELLIPSE_AA
+};
+
+static const unsigned int num_fragment_variables = 19;
+
+static const unsigned int num_fragment_brushes = 6;
+static const unsigned int num_fragment_composition_modes = 26;
+static const unsigned int num_fragment_masks = 2;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA =
+ "!!ARBfp1.0\n"
+ "PARAM c[1] = { { 0.5, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "ADD R3.z, fragment.position.x, c[0].x;\n"
+ "ADD R0.y, fragment.position, -c[0].x;\n"
+ "MAX R4.x, fragment.texcoord[0].y, R0.y;\n"
+ "ADD R0.x, fragment.position.y, c[0];\n"
+ "MIN R3.w, R0.x, fragment.texcoord[0].x;\n"
+ "ADD R2.z, fragment.position.x, -c[0].x;\n"
+ "MOV R2.w, R3.z;\n"
+ "MOV R0.yw, R4.x;\n"
+ "MOV R0.xz, R3.w;\n"
+ "MAD R0, fragment.texcoord[1].xxzz, R0, fragment.texcoord[1].yyww;\n"
+ "MAD R0.zw, fragment.position.x, c[0].y, -R0;\n"
+ "MOV R2.x, R0;\n"
+ "MOV R2.y, R0.z;\n"
+ "MOV R1.w, R0;\n"
+ "MOV R1.z, R0.y;\n"
+ "MIN R1.xy, R2, R1.zwzw;\n"
+ "SGE R0.xy, R1.zwzw, R2;\n"
+ "ADD R0.zw, -fragment.texcoord[0], -fragment.texcoord[0];\n"
+ "MAD R3.xy, R0, R0.zwzw, fragment.texcoord[0].zwzw;\n"
+ "ADD R0, -R1.xxyy, R2.zwzw;\n"
+ "MAD R0, R0, R3.xxyy, R4.x;\n"
+ "ADD R3.xy, R0.ywzw, R0.xzzw;\n"
+ "ADD R4.zw, R3.w, -R0.xyxz;\n"
+ "ADD R0.zw, -R4.x, R0.xyyw;\n"
+ "ADD R0.xy, R3.z, -R1;\n"
+ "MAX R1.zw, R2.xyxy, R1;\n"
+ "MUL R0.xy, R0, R0.zwzw;\n"
+ "MAD R3.xy, -R3, c[0].x, R3.w;\n"
+ "ADD R2.w, R3.z, -R2.z;\n"
+ "MUL R2.xy, R3, R2.w;\n"
+ "ADD R2.w, R3, -R4.x;\n"
+ "ADD R3.xy, -R2.z, R1.zwzw;\n"
+ "MUL R3.xy, R4.zwzw, R3;\n"
+ "ADD R4.zw, R1.xyxy, R1;\n"
+ "MAD R0.zw, R4, c[0].x, -R2.z;\n"
+ "MAD R0.xy, -R0, c[0].x, R2.w;\n"
+ "MAD R4.zw, R0, R2.w, -R0.xyxy;\n"
+ "SGE R0.zw, R3.z, R1;\n"
+ "MAD R0.xy, R0.zwzw, R4.zwzw, R0;\n"
+ "MAD R3.xy, R3, c[0].x, -R2;\n"
+ "MAD R0.zw, R0, R3.xyxy, R2.xyxy;\n"
+ "ADD R2.xy, R0.zwzw, -R0;\n"
+ "SGE R0.zw, R2.z, R1.xyxy;\n"
+ "MAD R0.xy, R0.zwzw, R2, R0;\n"
+ "SGE R0.zw, R1, R2.z;\n"
+ "ADD R0.xy, R0, -R2.w;\n"
+ "SGE R1.xy, R3.z, R1;\n"
+ "MAD R0.xy, R1, R0, R2.w;\n"
+ "MAD R0.x, -R0, R0.z, R2.w;\n"
+ "SGE R0.z, R3.w, R4.x;\n"
+ "MAD R0.x, -R0.y, R0.w, R0;\n"
+ "MUL result.color, R0.x, R0.z;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_MASK_ELLIPSE_AA =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..3],\n"
+ " { -2, 1, -0.5, 2 },\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "ADD R0.xy, fragment.position, c[3];\n"
+ "MUL R1.xyz, R0.y, c[1];\n"
+ "MAD R0.xyz, R0.x, c[0], R1;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R2.z, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R2.z;\n"
+ "MUL R2.xy, R0.zwzw, fragment.texcoord[0];\n"
+ "MOV R1.xy, c[0];\n"
+ "MOV R1.zw, c[1].xyxy;\n"
+ "MOV R0.x, c[0].z;\n"
+ "MOV R0.y, c[1].z;\n"
+ "MAD R0, R0.zzww, -R0.xyxy, R1.xzyw;\n"
+ "MUL R1.xy, R2, fragment.texcoord[0];\n"
+ "MUL R0, R2.z, R0;\n"
+ "MUL R1.xy, R1, c[4].x;\n"
+ "MUL R1.zw, R1.xyxy, R0.xyxz;\n"
+ "MUL R0.zw, R1.xyxy, R0.xyyw;\n"
+ "ADD R0.y, R0.z, R0.w;\n"
+ "ADD R0.x, R1.z, R1.w;\n"
+ "MUL R0.xy, R0, R0;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, R2.xyxy, R2.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.y, -R0.z, c[4];\n"
+ "RSQ R0.x, R0.x;\n"
+ "MAD_SAT R0.x, R0, R0.y, -c[4].z;\n"
+ "MUL R0.y, -R0.x, c[4].w;\n"
+ "ADD R0.y, R0, c[5].x;\n"
+ "MUL R0.x, R0, R0;\n"
+ "MUL result.color, R0.x, R0.y;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SIMPLE_PORTER_DUFF =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..5],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.xyz, R0, c[0].y;\n"
+ "MUL R2.xyz, fragment.color.primary.w, R1;\n"
+ "MUL R1.xyz, fragment.color.primary, c[0].x;\n"
+ "MAD R2.xyz, R0.w, R1, R2;\n"
+ "ADD R3.xy, fragment.position, c[4];\n"
+ "ADD R1.w, -R0, c[6].x;\n"
+ "MUL R1.xyz, fragment.color.primary, c[1].y;\n"
+ "MAD R2.xyz, R1.w, R1, R2;\n"
+ "MUL R1.xyz, R0, c[1].z;\n"
+ "ADD R2.w, -fragment.color.primary, c[6].x;\n"
+ "MAD R2.xyz, R2.w, R1, R2;\n"
+ "MUL R1.z, R0.w, R2.w;\n"
+ "MUL R1.x, fragment.color.primary.w, R0.w;\n"
+ "MUL R1.y, fragment.color.primary.w, R1.w;\n"
+ "DP3 R2.w, R1, c[1];\n"
+ "MUL R3.xy, R3, c[2];\n"
+ "TEX R1, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[5];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_MULTIPLY =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[1];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, -R0.w, c[4];\n"
+ "MUL R1.xyz, fragment.color.primary, R1.x;\n"
+ "MAD R1.xyz, fragment.color.primary, R0, R1;\n"
+ "ADD R1.w, -fragment.color.primary, c[4].x;\n"
+ "MAD R2.xyz, R0, R1.w, R1;\n"
+ "ADD R1.z, fragment.color.primary.w, R0.w;\n"
+ "MAD R2.w, -fragment.color.primary, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[2];\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[3];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SCREEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[4] = { program.local[0..3] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[1];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.xy, fragment.position, c[2];\n"
+ "ADD R2, fragment.color.primary, R0;\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "MAD R2, -fragment.color.primary, R0, R2;\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[3];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_OVERLAY =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[1];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "ADD R0.w, -R1, c[4].y;\n"
+ "MUL R3.xyz, fragment.color.primary, R0.w;\n"
+ "ADD R2.xyz, fragment.color.primary.w, -fragment.color.primary;\n"
+ "ADD R0.xyz, R1.w, -R1;\n"
+ "MUL R0.xyz, R0, R2;\n"
+ "MUL R0.xyz, R0, c[4].x;\n"
+ "MAD R0.xyz, fragment.color.primary.w, R1.w, -R0;\n"
+ "MAD R0.xyz, fragment.color.primary, R0.w, R0;\n"
+ "MUL R2.xyz, fragment.color.primary, R1;\n"
+ "MAD R2.xyz, R2, c[4].x, R3;\n"
+ "ADD R0.w, -fragment.color.primary, c[4].y;\n"
+ "MAD R3.xyz, R1, R0.w, R0;\n"
+ "MAD R2.xyz, R1, R0.w, R2;\n"
+ "MUL R0.xyz, R1, c[4].x;\n"
+ "SGE R0.xyz, R0, R1.w;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "MAD R2.xyz, R0, R3, R2;\n"
+ "ADD R0.z, fragment.color.primary.w, R1.w;\n"
+ "MAD R2.w, -fragment.color.primary, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[2];\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[3];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DARKEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[1];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, fragment.color.primary.w, R0;\n"
+ "MUL R1.xyz, fragment.color.primary, R0.w;\n"
+ "MIN R1.xyz, R1, R2;\n"
+ "ADD R1.w, -R0, c[4].x;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, -fragment.color.primary, c[4].x;\n"
+ "MAD R2.xyz, R0, R1.w, R1;\n"
+ "ADD R1.z, fragment.color.primary.w, R0.w;\n"
+ "MAD R2.w, -fragment.color.primary, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[2];\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[3];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_LIGHTEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[1];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, fragment.color.primary.w, R0;\n"
+ "MUL R1.xyz, fragment.color.primary, R0.w;\n"
+ "MAX R1.xyz, R1, R2;\n"
+ "ADD R1.w, -R0, c[4].x;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, -fragment.color.primary, c[4].x;\n"
+ "MAD R2.xyz, R0, R1.w, R1;\n"
+ "ADD R1.z, fragment.color.primary.w, R0.w;\n"
+ "MAD R2.w, -fragment.color.primary, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[2];\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[3];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORDODGE =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 1, 1e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[1];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, -fragment.color.primary.w, c[4];\n"
+ "MAX R1.y, fragment.color.primary.w, c[4];\n"
+ "MUL R2.xyz, R0, R1.x;\n"
+ "ADD R1.w, -R0, c[4].x;\n"
+ "MAD R3.xyz, fragment.color.primary, R1.w, R2;\n"
+ "RCP R1.y, R1.y;\n"
+ "MAD R1.xyz, -fragment.color.primary, R1.y, c[4].x;\n"
+ "MAX R1.xyz, R1, c[4].y;\n"
+ "MUL R2.xyz, fragment.color.primary.w, R0;\n"
+ "MUL R1.w, fragment.color.primary, R0;\n"
+ "RCP R1.x, R1.x;\n"
+ "RCP R1.y, R1.y;\n"
+ "RCP R1.z, R1.z;\n"
+ "MAD R1.xyz, R2, R1, R3;\n"
+ "MAD R3.xyz, fragment.color.primary.w, R0.w, R3;\n"
+ "MAD R2.xyz, fragment.color.primary, R0.w, R2;\n"
+ "ADD R3.xyz, R3, -R1;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "MAD R2.xyz, R2, R3, R1;\n"
+ "ADD R1.z, fragment.color.primary.w, R0.w;\n"
+ "MAD R2.w, -fragment.color.primary, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[2];\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[3];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORBURN =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 1, 9.9999997e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xy, fragment.position, c[1];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.w, -R0, c[4].x;\n"
+ "MUL R1.xyz, fragment.color.primary.w, R0;\n"
+ "MAD R2.xyz, fragment.color.primary, R0.w, R1;\n"
+ "MAD R1.xyz, -fragment.color.primary.w, R0.w, R2;\n"
+ "MUL R3.xyz, fragment.color.primary.w, R1;\n"
+ "MAX R1.xyz, fragment.color.primary, c[4].y;\n"
+ "ADD R2.w, -fragment.color.primary, c[4].x;\n"
+ "MUL R4.xyz, fragment.color.primary, R1.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "RCP R1.y, R1.y;\n"
+ "RCP R1.z, R1.z;\n"
+ "MAD R3.xyz, R3, R1, R4;\n"
+ "MUL R1.xyz, R0, R2.w;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "MAD R3.xyz, R0, R2.w, R3;\n"
+ "MUL R1.w, fragment.color.primary, R0;\n"
+ "ADD R3.xyz, R3, -R1;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "MAD R2.xyz, R2, R3, R1;\n"
+ "ADD R1.z, fragment.color.primary.w, R0.w;\n"
+ "MAD R2.w, -fragment.color.primary, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[2];\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[3];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_HARDLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[1];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "ADD R0.w, -R1, c[4].y;\n"
+ "MUL R3.xyz, fragment.color.primary, R0.w;\n"
+ "ADD R2.xyz, fragment.color.primary.w, -fragment.color.primary;\n"
+ "ADD R0.xyz, R1.w, -R1;\n"
+ "MUL R0.xyz, R0, R2;\n"
+ "MUL R0.xyz, R0, c[4].x;\n"
+ "MAD R0.xyz, fragment.color.primary.w, R1.w, -R0;\n"
+ "MAD R0.xyz, fragment.color.primary, R0.w, R0;\n"
+ "MUL R2.xyz, fragment.color.primary, R1;\n"
+ "MAD R2.xyz, R2, c[4].x, R3;\n"
+ "ADD R0.w, -fragment.color.primary, c[4].y;\n"
+ "MAD R3.xyz, R1, R0.w, R0;\n"
+ "MAD R2.xyz, R1, R0.w, R2;\n"
+ "MUL R0.xyz, fragment.color.primary, c[4].x;\n"
+ "SGE R0.xyz, R0, fragment.color.primary.w;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "MAD R2.xyz, R0, R3, R2;\n"
+ "ADD R0.z, fragment.color.primary.w, R1.w;\n"
+ "MAD R2.w, -fragment.color.primary, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[2];\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[3];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SOFTLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..3],\n"
+ " { 1, 2, 9.9999997e-006, 4 },\n"
+ " { 16, 12, 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xy, fragment.position, c[1];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MAX R1.x, R0.w, c[4].z;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R2.xyz, R0, R1.x;\n"
+ "MAD R1.xyz, R2, c[5].x, -c[5].y;\n"
+ "MAD R3.xyz, R2, R1, c[5].z;\n"
+ "MAD R1.xyz, fragment.color.primary, c[4].y, -fragment.color.primary.w;\n"
+ "MUL R4.xyz, R0.w, R1;\n"
+ "MUL R5.xyz, R4, R3;\n"
+ "RSQ R1.w, R2.x;\n"
+ "RSQ R2.w, R2.z;\n"
+ "RCP R3.x, R1.w;\n"
+ "RSQ R1.w, R2.y;\n"
+ "MUL R5.xyz, R2, R5;\n"
+ "RCP R3.z, R2.w;\n"
+ "RCP R3.y, R1.w;\n"
+ "ADD R3.xyz, -R2, R3;\n"
+ "MUL R3.xyz, R4, R3;\n"
+ "ADD R2.xyz, -R2, c[4].x;\n"
+ "MAD R1.xyz, R1, R2, fragment.color.primary.w;\n"
+ "MUL R2.xyz, fragment.color.primary, c[4].y;\n"
+ "MAD R4.xyz, fragment.color.primary.w, R0, R5;\n"
+ "MAD R3.xyz, fragment.color.primary.w, R0, R3;\n"
+ "ADD R5.xyz, R3, -R4;\n"
+ "MUL R3.xyz, R0, c[4].w;\n"
+ "SGE R3.xyz, R3, R0.w;\n"
+ "MAD R3.xyz, R3, R5, R4;\n"
+ "MAD R3.xyz, -R0, R1, R3;\n"
+ "MUL R1.xyz, R0, R1;\n"
+ "SGE R2.xyz, R2, fragment.color.primary.w;\n"
+ "MAD R2.xyz, R2, R3, R1;\n"
+ "ADD R1.x, -R0.w, c[4];\n"
+ "MAD R2.xyz, fragment.color.primary, R1.x, R2;\n"
+ "ADD R1.x, -fragment.color.primary.w, c[4];\n"
+ "MAD R2.xyz, R0, R1.x, R2;\n"
+ "ADD R1.z, fragment.color.primary.w, R0.w;\n"
+ "MAD R2.w, -fragment.color.primary, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[2];\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[3];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DIFFERENCE =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[1];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.xyz, fragment.color.primary, R0;\n"
+ "MUL R3.xyz, fragment.color.primary.w, R0;\n"
+ "MUL R2.xyz, fragment.color.primary, R0.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "MAD R2.xyz, -R2, c[4].x, R1;\n"
+ "ADD R1.z, fragment.color.primary.w, R0.w;\n"
+ "MAD R2.w, -fragment.color.primary, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[2];\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[3];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_EXCLUSION =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[1];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.xyz, fragment.color.primary.w, R0;\n"
+ "MAD R2.xyz, fragment.color.primary, R0.w, R1;\n"
+ "MUL R1.xyz, fragment.color.primary, R0;\n"
+ "MAD R1.xyz, -R1, c[4].x, R2;\n"
+ "ADD R1.w, -R0, c[4].y;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, -fragment.color.primary, c[4].y;\n"
+ "MAD R2.xyz, R0, R1.w, R1;\n"
+ "ADD R1.z, fragment.color.primary.w, R0.w;\n"
+ "MAD R2.w, -fragment.color.primary, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[2];\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[3];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[4] = { program.local[0..2],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[2];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.xyz, R0, c[0].y;\n"
+ "MUL R2.xyz, fragment.color.primary.w, R1;\n"
+ "MUL R1.xyz, fragment.color.primary, c[0].x;\n"
+ "MAD R2.xyz, R0.w, R1, R2;\n"
+ "MUL R0.xyz, R0, c[1].z;\n"
+ "ADD R1.w, -R0, c[3].x;\n"
+ "MUL R1.xyz, fragment.color.primary, c[1].y;\n"
+ "MAD R1.xyz, R1.w, R1, R2;\n"
+ "ADD R2.x, -fragment.color.primary.w, c[3];\n"
+ "MAD result.color.xyz, R2.x, R0, R1;\n"
+ "MUL R0.x, fragment.color.primary.w, R0.w;\n"
+ "MUL R0.z, R0.w, R2.x;\n"
+ "MUL R0.y, fragment.color.primary.w, R1.w;\n"
+ "DP3 result.color.w, R0, c[1];\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_MULTIPLY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, -R0.w, c[1];\n"
+ "MUL R1.xyz, fragment.color.primary, R1.x;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "MAD R1.xyz, fragment.color.primary, R0, R1;\n"
+ "ADD R2.x, -fragment.color.primary.w, c[1];\n"
+ "MAD result.color.xyz, R0, R2.x, R1;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SCREEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[1] = { program.local[0] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1, fragment.color.primary, R0;\n"
+ "MAD result.color, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_OVERLAY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.w, -R0, c[1].y;\n"
+ "ADD R2.xyz, fragment.color.primary.w, -fragment.color.primary;\n"
+ "ADD R1.xyz, R0.w, -R0;\n"
+ "MUL R1.xyz, R1, R2;\n"
+ "MUL R1.xyz, R1, c[1].x;\n"
+ "MAD R1.xyz, fragment.color.primary.w, R0.w, -R1;\n"
+ "MUL R3.xyz, fragment.color.primary, R1.w;\n"
+ "MUL R2.xyz, fragment.color.primary, R0;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, -fragment.color.primary, c[1].y;\n"
+ "MAD R2.xyz, R2, c[1].x, R3;\n"
+ "MAD R2.xyz, R0, R1.w, R2;\n"
+ "MAD R1.xyz, R0, R1.w, R1;\n"
+ "MUL R0.xyz, R0, c[1].x;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "ADD R1.xyz, R1, -R2;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD result.color.xyz, R0, R1, R2;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DARKEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, fragment.color.primary.w, R0;\n"
+ "MUL R1.xyz, fragment.color.primary, R0.w;\n"
+ "MIN R1.xyz, R1, R2;\n"
+ "ADD R1.w, -R0, c[1].x;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "ADD R2.x, -fragment.color.primary.w, c[1];\n"
+ "MAD result.color.xyz, R0, R2.x, R1;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_LIGHTEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, fragment.color.primary.w, R0;\n"
+ "MUL R1.xyz, fragment.color.primary, R0.w;\n"
+ "MAX R1.xyz, R1, R2;\n"
+ "ADD R1.w, -R0, c[1].x;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "ADD R2.x, -fragment.color.primary.w, c[1];\n"
+ "MAD result.color.xyz, R0, R2.x, R1;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORDODGE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 1, 1e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MAX R1.y, fragment.color.primary.w, c[1];\n"
+ "RCP R2.x, R1.y;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, -fragment.color.primary.w, c[1];\n"
+ "MUL R1.xyz, R0, R1.x;\n"
+ "ADD R1.w, -R0, c[1].x;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "MAD R2.xyz, -fragment.color.primary, R2.x, c[1].x;\n"
+ "MAX R2.xyz, R2, c[1].y;\n"
+ "MUL R0.xyz, fragment.color.primary.w, R0;\n"
+ "MUL R1.w, fragment.color.primary, R0;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R0, R2, R1;\n"
+ "MAD R1.xyz, fragment.color.primary.w, R0.w, R1;\n"
+ "MAD R0.xyz, fragment.color.primary, R0.w, R0;\n"
+ "SGE R0.xyz, R0, R1.w;\n"
+ "ADD R1.xyz, R1, -R2;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "MAD result.color.xyz, R0, R1, R2;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORBURN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 1, 9.9999997e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.xyz, fragment.color.primary.w, R0;\n"
+ "MAD R2.xyz, fragment.color.primary, R0.w, R1;\n"
+ "MAD R1.xyz, -fragment.color.primary.w, R0.w, R2;\n"
+ "MUL R3.xyz, fragment.color.primary.w, R1;\n"
+ "MAX R1.xyz, fragment.color.primary, c[1].y;\n"
+ "ADD R1.w, -R0, c[1].x;\n"
+ "MUL R4.xyz, fragment.color.primary, R1.w;\n"
+ "ADD R2.w, -fragment.color.primary, c[1].x;\n"
+ "RCP R1.x, R1.x;\n"
+ "RCP R1.y, R1.y;\n"
+ "RCP R1.z, R1.z;\n"
+ "MAD R1.xyz, R3, R1, R4;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "MAD R0.xyz, R0, R2.w, R1;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R3;\n"
+ "MUL R1.w, fragment.color.primary, R0;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "ADD R0.xyz, R0, -R1;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "MAD result.color.xyz, R2, R0, R1;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_HARDLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.w, -R0, c[1].y;\n"
+ "ADD R2.xyz, fragment.color.primary.w, -fragment.color.primary;\n"
+ "ADD R1.xyz, R0.w, -R0;\n"
+ "MUL R1.xyz, R1, R2;\n"
+ "MUL R1.xyz, R1, c[1].x;\n"
+ "MAD R1.xyz, fragment.color.primary.w, R0.w, -R1;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "MUL R3.xyz, fragment.color.primary, R1.w;\n"
+ "MUL R2.xyz, fragment.color.primary, R0;\n"
+ "ADD R1.w, -fragment.color.primary, c[1].y;\n"
+ "MAD R2.xyz, R2, c[1].x, R3;\n"
+ "MAD R2.xyz, R0, R1.w, R2;\n"
+ "MAD R0.xyz, R0, R1.w, R1;\n"
+ "ADD R1.xyz, R0, -R2;\n"
+ "MUL R0.xyz, fragment.color.primary, c[1].x;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "SGE R0.xyz, R0, fragment.color.primary.w;\n"
+ "MAD result.color.xyz, R0, R1, R2;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SOFTLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[3] = { program.local[0],\n"
+ " { 1, 2, 9.9999997e-006, 4 },\n"
+ " { 16, 12, 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MAX R1.x, R0.w, c[1].z;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R2.xyz, R0, R1.x;\n"
+ "MAD R1.xyz, R2, c[2].x, -c[2].y;\n"
+ "MAD R3.xyz, R2, R1, c[2].z;\n"
+ "MAD R1.xyz, fragment.color.primary, c[1].y, -fragment.color.primary.w;\n"
+ "MUL R4.xyz, R0.w, R1;\n"
+ "MUL R5.xyz, R4, R3;\n"
+ "RSQ R1.w, R2.x;\n"
+ "RCP R3.x, R1.w;\n"
+ "RSQ R2.w, R2.z;\n"
+ "RSQ R1.w, R2.y;\n"
+ "MUL R5.xyz, R2, R5;\n"
+ "RCP R3.z, R2.w;\n"
+ "RCP R3.y, R1.w;\n"
+ "ADD R3.xyz, -R2, R3;\n"
+ "MUL R3.xyz, R4, R3;\n"
+ "ADD R2.xyz, -R2, c[1].x;\n"
+ "MAD R1.xyz, R1, R2, fragment.color.primary.w;\n"
+ "MUL R2.xyz, fragment.color.primary, c[1].y;\n"
+ "MAD R4.xyz, fragment.color.primary.w, R0, R5;\n"
+ "MAD R3.xyz, fragment.color.primary.w, R0, R3;\n"
+ "ADD R5.xyz, R3, -R4;\n"
+ "MUL R3.xyz, R0, c[1].w;\n"
+ "SGE R3.xyz, R3, R0.w;\n"
+ "MAD R3.xyz, R3, R5, R4;\n"
+ "MAD R3.xyz, -R0, R1, R3;\n"
+ "MUL R1.xyz, R0, R1;\n"
+ "SGE R2.xyz, R2, fragment.color.primary.w;\n"
+ "MAD R2.xyz, R2, R3, R1;\n"
+ "ADD R1.x, -R0.w, c[1];\n"
+ "MAD R2.xyz, fragment.color.primary, R1.x, R2;\n"
+ "ADD R1.x, fragment.color.primary.w, R0.w;\n"
+ "ADD R1.y, -fragment.color.primary.w, c[1].x;\n"
+ "MAD result.color.xyz, R0, R1.y, R2;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DIFFERENCE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, fragment.color.primary.w, R0;\n"
+ "MUL R1.xyz, fragment.color.primary, R0.w;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "MIN R1.xyz, R1, R2;\n"
+ "ADD R0.xyz, fragment.color.primary, R0;\n"
+ "MAD result.color.xyz, -R1, c[1].x, R0;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_EXCLUSION_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.xyz, fragment.color.primary.w, R0;\n"
+ "MAD R2.xyz, fragment.color.primary, R0.w, R1;\n"
+ "MUL R1.xyz, fragment.color.primary, R0;\n"
+ "MAD R1.xyz, -R1, c[1].x, R2;\n"
+ "ADD R1.w, -R0, c[1].y;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "ADD R2.x, -fragment.color.primary.w, c[1].y;\n"
+ "MAD result.color.xyz, R0, R2.x, R1;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODE_BLEND_MODE_MASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[3] = { program.local[0..2] };\n"
+ "TEMP R0;\n"
+ "ADD R0.xy, fragment.position, c[1];\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MUL result.color, fragment.color.primary, R0.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODE_BLEND_MODE_NOMASK =
+ "!!ARBfp1.0\n"
+ "MOV result.color, fragment.color.primary;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF =
+ "!!ARBfp1.0\n"
+ "PARAM c[12] = { program.local[0..10],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[11].x;\n"
+ "MUL R0.z, R0, c[11].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.x, R0.x;\n"
+ "RCP R0.z, R0.x;\n"
+ "ADD R1.x, -R0.y, R0.z;\n"
+ "MOV R0.x, c[11];\n"
+ "MUL R0.z, R0.x, c[1].x;\n"
+ "RCP R1.y, R0.z;\n"
+ "MUL R0.xy, fragment.position, c[8];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "MUL R2.xyz, R0, c[5].y;\n"
+ "MUL R3.xyz, R1.w, R2;\n"
+ "MUL R2.xyz, R1, c[5].x;\n"
+ "MAD R2.xyz, R0.w, R2, R3;\n"
+ "ADD R3.xy, fragment.position, c[9];\n"
+ "ADD R2.w, -R0, c[11].z;\n"
+ "MUL R1.xyz, R1, c[6].y;\n"
+ "MAD R2.xyz, R2.w, R1, R2;\n"
+ "MUL R1.xyz, R0, c[6].z;\n"
+ "ADD R3.z, -R1.w, c[11];\n"
+ "MAD R2.xyz, R3.z, R1, R2;\n"
+ "MUL R1.y, R1.w, R2.w;\n"
+ "MUL R1.x, R1.w, R0.w;\n"
+ "MUL R1.z, R0.w, R3;\n"
+ "DP3 R2.w, R1, c[6];\n"
+ "MUL R3.xy, R3, c[7];\n"
+ "TEX R1, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[10];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_MULTIPLY =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..8],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[9].x;\n"
+ "MUL R0.z, R0, c[9].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.x, R0.x;\n"
+ "RCP R0.z, R0.x;\n"
+ "ADD R1.x, -R0.y, R0.z;\n"
+ "MOV R0.x, c[9];\n"
+ "MUL R0.z, R0.x, c[1].x;\n"
+ "RCP R1.y, R0.z;\n"
+ "MUL R0.xy, fragment.position, c[6];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "ADD R2.x, -R0.w, c[9].z;\n"
+ "MUL R2.xyz, R1, R2.x;\n"
+ "MAD R1.xyz, R1, R0, R2;\n"
+ "ADD R2.x, -R1.w, c[9].z;\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[7];\n"
+ "MUL R1.xy, R1, c[5];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[8];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SCREEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..8],\n"
+ " { 2, 4 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.y, R0.x, c[9].x;\n"
+ "MOV R0.x, c[9];\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.z, R0, c[9].y;\n"
+ "MAD R0.z, R0.y, R0.y, -R0;\n"
+ "ADD R3.xy, fragment.position, c[7];\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RSQ R0.z, R0.z;\n"
+ "RCP R0.x, R0.z;\n"
+ "RCP R0.z, R0.w;\n"
+ "ADD R0.x, -R0.y, R0;\n"
+ "MUL R0.z, R0.x, R0;\n"
+ "TEX R1, R0.z, texture[2], 1D;\n"
+ "MUL R0.xy, fragment.position, c[6];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R2, R1, R0;\n"
+ "MAD R2, -R1, R0, R2;\n"
+ "MUL R3.xy, R3, c[5];\n"
+ "TEX R1, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[8];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_OVERLAY =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..8],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.y, R0.x, c[9].x;\n"
+ "MOV R0.x, c[9];\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.z, R0, c[9].y;\n"
+ "MAD R0.z, R0.y, R0.y, -R0;\n"
+ "MUL R1.xy, fragment.position, c[6];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RSQ R0.z, R0.z;\n"
+ "RCP R0.x, R0.z;\n"
+ "ADD R2.w, -R1, c[9].z;\n"
+ "RCP R0.z, R0.w;\n"
+ "ADD R0.x, -R0.y, R0;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[9].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[9].z;\n"
+ "MAD R3.xyz, R3, c[9].x, R4;\n"
+ "MAD R3.xyz, R1, R2.x, R3;\n"
+ "MAD R0.xyz, R1, R2.x, R0;\n"
+ "MUL R2.xyz, R1, c[9].x;\n"
+ "ADD R0.xyz, R0, -R3;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "MAD R2.xyz, R2, R0, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[7];\n"
+ "MUL R0.xy, R0, c[5];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[8];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DARKEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..8],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[9].x;\n"
+ "MUL R0.z, R0, c[9].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.z, R0.x;\n"
+ "MOV R0.x, c[9];\n"
+ "MUL R0.x, R0, c[1];\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.z, -R0.y, R0;\n"
+ "RCP R0.w, R0.x;\n"
+ "MUL R1.x, R0.z, R0.w;\n"
+ "MUL R0.xy, fragment.position, c[6];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[9].z;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[9].z;\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[7];\n"
+ "MUL R1.xy, R1, c[5];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[8];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_LIGHTEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..8],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[9].x;\n"
+ "MUL R0.z, R0, c[9].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.z, R0.x;\n"
+ "MOV R0.x, c[9];\n"
+ "MUL R0.x, R0, c[1];\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.z, -R0.y, R0;\n"
+ "RCP R0.w, R0.x;\n"
+ "MUL R1.x, R0.z, R0.w;\n"
+ "MUL R0.xy, fragment.position, c[6];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[9].z;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[9].z;\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[7];\n"
+ "MUL R1.xy, R1, c[5];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[8];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORDODGE =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..8],\n"
+ " { 2, 4, 1, 1e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.y, R0.x, c[9].x;\n"
+ "MOV R0.x, c[9];\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.z, R0, c[9].y;\n"
+ "MAD R0.z, R0.y, R0.y, -R0;\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RSQ R0.z, R0.z;\n"
+ "RCP R0.x, R0.z;\n"
+ "RCP R0.z, R0.w;\n"
+ "ADD R0.x, -R0.y, R0;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MAX R1.x, R0.w, c[9].w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R1.xyz, -R0, R1.x, c[9].z;\n"
+ "MAX R2.xyz, R1, c[9].w;\n"
+ "MUL R1.xy, fragment.position, c[6];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[9].z;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[9].z;\n"
+ "MAD R4.xyz, R0, R2.w, R3;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R0.xyz, R0, R1.w, R3;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R3, R2, R4;\n"
+ "MAD R4.xyz, R0.w, R1.w, R4;\n"
+ "ADD R4.xyz, R4, -R2;\n"
+ "MAD R2.xyz, R0, R4, R2;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[7];\n"
+ "MUL R0.xy, R0, c[5];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[8];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORBURN =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..8],\n"
+ " { 2, 4, 1, 9.9999997e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[9].x;\n"
+ "MUL R0.z, R0, c[9].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.z, R0.x;\n"
+ "MOV R0.x, c[9];\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.x, -R0.y, R0.z;\n"
+ "RCP R0.y, R0.w;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[6].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R2.xyz, R0.w, R1;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[9].w;\n"
+ "ADD R2.w, -R1, c[9].z;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[9].z;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R2.xyz, R1, R3.w, R2;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R3.xyz, R3, R2.w;\n"
+ "MAD R2.xyz, R3, R2, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[7];\n"
+ "MUL R0.xy, R0, c[5];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[8];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_HARDLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..8],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.y, R0.x, c[9].x;\n"
+ "MOV R0.x, c[9];\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.z, R0, c[9].y;\n"
+ "MAD R0.z, R0.y, R0.y, -R0;\n"
+ "MUL R1.xy, fragment.position, c[6];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RSQ R0.z, R0.z;\n"
+ "RCP R0.x, R0.z;\n"
+ "ADD R2.w, -R1, c[9].z;\n"
+ "RCP R0.z, R0.w;\n"
+ "ADD R0.x, -R0.y, R0;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[9].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[9].z;\n"
+ "MAD R3.xyz, R3, c[9].x, R4;\n"
+ "MUL R0.xyz, R0, c[9].x;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD R3.xyz, R1, R2.w, R3;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MAD R2.xyz, R0, R2, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[7];\n"
+ "MUL R0.xy, R0, c[5];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[8];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SOFTLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..8],\n"
+ " { 2, 4, 1, 9.9999997e-006 },\n"
+ " { 16, 12, 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.z, c[9];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[6].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "ADD R0.y, -R0.x, R0;\n"
+ "MOV R0.x, c[9];\n"
+ "MUL R0.x, R0, c[1];\n"
+ "MAX R0.z, R1.w, c[9].w;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R3.xyz, R1, R0.z;\n"
+ "MAD R4.xyz, R3, c[10].x, -c[10].y;\n"
+ "RCP R0.x, R0.x;\n"
+ "MUL R0.x, R0.y, R0;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MAD R2.xyz, R0, c[9].x, -R0.w;\n"
+ "MAD R4.xyz, R3, R4, c[10].z;\n"
+ "MUL R5.xyz, R1.w, R2;\n"
+ "MUL R6.xyz, R5, R4;\n"
+ "RSQ R2.w, R3.x;\n"
+ "RCP R4.x, R2.w;\n"
+ "RSQ R2.w, R3.y;\n"
+ "RSQ R3.w, R3.z;\n"
+ "RCP R4.y, R2.w;\n"
+ "RCP R4.z, R3.w;\n"
+ "ADD R4.xyz, -R3, R4;\n"
+ "MUL R6.xyz, R3, R6;\n"
+ "MUL R4.xyz, R5, R4;\n"
+ "ADD R3.xyz, -R3, c[9].z;\n"
+ "MAD R2.xyz, R2, R3, R0.w;\n"
+ "MUL R3.xyz, R0, c[9].x;\n"
+ "MAD R5.xyz, R0.w, R1, R6;\n"
+ "MAD R4.xyz, R0.w, R1, R4;\n"
+ "ADD R6.xyz, R4, -R5;\n"
+ "MUL R4.xyz, R1, c[9].y;\n"
+ "SGE R4.xyz, R4, R1.w;\n"
+ "MAD R4.xyz, R4, R6, R5;\n"
+ "MAD R4.xyz, -R1, R2, R4;\n"
+ "SGE R3.xyz, R3, R0.w;\n"
+ "MUL R2.xyz, R1, R2;\n"
+ "ADD R2.w, -R1, c[9].z;\n"
+ "MAD R2.xyz, R3, R4, R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R0.x, -R0.w, c[9].z;\n"
+ "MAD R2.xyz, R1, R0.x, R2;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[7];\n"
+ "MUL R0.xy, R0, c[5];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[8];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DIFFERENCE =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..8],\n"
+ " { 2, 4 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[9].x;\n"
+ "MUL R0.z, R0, c[9].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.z, R0.x;\n"
+ "MOV R0.x, c[9];\n"
+ "MUL R0.x, R0, c[1];\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.z, -R0.y, R0;\n"
+ "RCP R0.w, R0.x;\n"
+ "MUL R1.x, R0.z, R0.w;\n"
+ "MUL R0.xy, fragment.position, c[6];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "ADD R2.xyz, R1, R0;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R1.xyz, R1, R0.w;\n"
+ "MIN R1.xyz, R1, R3;\n"
+ "MAD R2.xyz, -R1, c[9].x, R2;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[7];\n"
+ "MUL R1.xy, R1, c[5];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[8];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_EXCLUSION =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..8],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[9].x;\n"
+ "MUL R0.z, R0, c[9].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.z, R0.x;\n"
+ "MOV R0.x, c[9];\n"
+ "MUL R0.x, R0, c[1];\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.z, -R0.y, R0;\n"
+ "RCP R0.w, R0.x;\n"
+ "MUL R1.x, R0.z, R0.w;\n"
+ "MUL R0.xy, fragment.position, c[6];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "MUL R2.xyz, R1.w, R0;\n"
+ "MAD R3.xyz, R1, R0.w, R2;\n"
+ "MUL R2.xyz, R1, R0;\n"
+ "MAD R2.xyz, -R2, c[9].x, R3;\n"
+ "ADD R2.w, -R0, c[9].z;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[9].z;\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[7];\n"
+ "MUL R1.xy, R1, c[5];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[8];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[8].x;\n"
+ "MUL R0.z, R0, c[8].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.x, R0.x;\n"
+ "RCP R0.z, R0.x;\n"
+ "ADD R0.y, -R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, c[5].y;\n"
+ "MOV R0.x, c[8];\n"
+ "MUL R0.x, R0, c[1];\n"
+ "RCP R0.x, R0.x;\n"
+ "MUL R0.x, R0.y, R0;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R3.xyz, R0.w, R2;\n"
+ "MUL R2.xyz, R0, c[5].x;\n"
+ "MAD R2.xyz, R1.w, R2, R3;\n"
+ "ADD R2.w, -R1, c[8].z;\n"
+ "MUL R0.xyz, R0, c[6].y;\n"
+ "MAD R0.xyz, R2.w, R0, R2;\n"
+ "ADD R2.x, -R0.w, c[8].z;\n"
+ "MUL R1.xyz, R1, c[6].z;\n"
+ "MAD result.color.xyz, R2.x, R1, R0;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.z, R1.w, R2.x;\n"
+ "MUL R0.y, R0.w, R2.w;\n"
+ "DP3 result.color.w, R0, c[6];\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_MULTIPLY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..5],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[6].x;\n"
+ "MUL R0.z, R0, c[6].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.x, R0.x;\n"
+ "RCP R0.z, R0.x;\n"
+ "ADD R0.y, -R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MOV R0.x, c[6];\n"
+ "MUL R0.x, R0, c[1];\n"
+ "RCP R0.x, R0.x;\n"
+ "MUL R0.x, R0.y, R0;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2.x, -R1.w, c[6].z;\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[6].z;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SCREEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..5],\n"
+ " { 2, 4 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[6].x;\n"
+ "MUL R0.z, R0, c[6].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.z, R0.x;\n"
+ "MOV R0.x, c[6];\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.x, -R0.y, R0.z;\n"
+ "RCP R0.y, R0.w;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2, R0, R1;\n"
+ "MAD result.color, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_OVERLAY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..5],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.y, R0.x, c[6].x;\n"
+ "MOV R0.x, c[6];\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.z, R0, c[6].y;\n"
+ "MAD R0.z, R0.y, R0.y, -R0;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RSQ R0.z, R0.z;\n"
+ "RCP R0.x, R0.z;\n"
+ "ADD R2.w, -R1, c[6].z;\n"
+ "RCP R0.z, R0.w;\n"
+ "ADD R0.x, -R0.y, R0;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[6].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "MUL R0.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[6].z;\n"
+ "MAD R0.xyz, R0, c[6].x, R3;\n"
+ "MAD R0.xyz, R1, R2.w, R0;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R1.xyz, R1, c[6].x;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R1.xyz, R1, R1.w;\n"
+ "MAD result.color.xyz, R1, R2, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DARKEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..5],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[6].x;\n"
+ "MUL R0.z, R0, c[6].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.z, R0.x;\n"
+ "MOV R0.x, c[6];\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.x, -R0.y, R0.z;\n"
+ "RCP R0.y, R0.w;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[6].z;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[6].z;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_LIGHTEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..5],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[6].x;\n"
+ "MUL R0.z, R0, c[6].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.z, R0.x;\n"
+ "MOV R0.x, c[6];\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.x, -R0.y, R0.z;\n"
+ "RCP R0.y, R0.w;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[6].z;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[6].z;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORDODGE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..5],\n"
+ " { 2, 4, 1, 1e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.y, R0.x, c[6].x;\n"
+ "MOV R0.x, c[6];\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.z, R0, c[6].y;\n"
+ "MAD R0.z, R0.y, R0.y, -R0;\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RSQ R0.z, R0.z;\n"
+ "RCP R0.x, R0.z;\n"
+ "RCP R0.z, R0.w;\n"
+ "ADD R0.x, -R0.y, R0;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MAX R1.x, R0.w, c[6].w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R1.xyz, -R0, R1.x, c[6].z;\n"
+ "MAX R2.xyz, R1, c[6].w;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[6].z;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[6].z;\n"
+ "MAD R3.xyz, R0, R2.w, R3;\n"
+ "MUL R1.xyz, R0.w, R1;\n"
+ "MAD R0.xyz, R0, R1.w, R1;\n"
+ "MUL R2.w, R0, R1;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R1, R2, R3;\n"
+ "MAD R3.xyz, R0.w, R1.w, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "MAD result.color.xyz, R0, R3, R2;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORBURN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..5],\n"
+ " { 2, 4, 1, 9.9999997e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[6].x;\n"
+ "MUL R0.z, R0, c[6].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.z, R0.x;\n"
+ "MOV R0.x, c[6];\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.x, -R0.y, R0.z;\n"
+ "RCP R0.y, R0.w;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0.w, R1;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "ADD R2.w, -R1, c[6].z;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[6].w;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[6].z;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R1.xyz, R1, R3.w, R2;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.x, R0.w, R1.w;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R1.xyz, R1, -R0;\n"
+ "SGE R2.xyz, R3, R2.x;\n"
+ "MAD result.color.xyz, R2, R1, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_HARDLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..5],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.y, R0.x, c[6].x;\n"
+ "MOV R0.x, c[6];\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.z, R0, c[6].y;\n"
+ "MAD R0.z, R0.y, R0.y, -R0;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RSQ R0.z, R0.z;\n"
+ "RCP R0.x, R0.z;\n"
+ "ADD R2.w, -R1, c[6].z;\n"
+ "RCP R0.z, R0.w;\n"
+ "ADD R0.x, -R0.y, R0;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[6].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R2.w, -R0, c[6].z;\n"
+ "MUL R0.xyz, R0, c[6].x;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MAD R3.xyz, R3, c[6].x, R4;\n"
+ "MAD R1.xyz, R1, R2.w, R3;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R1;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD result.color.xyz, R0, R2, R1;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SOFTLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..5],\n"
+ " { 2, 4, 1, 9.9999997e-006 },\n"
+ " { 16, 12, 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.z, c[6];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "ADD R0.y, -R0.x, R0;\n"
+ "MOV R0.x, c[6];\n"
+ "MUL R0.x, R0, c[1];\n"
+ "MAX R0.z, R1.w, c[6].w;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R3.xyz, R1, R0.z;\n"
+ "MAD R4.xyz, R3, c[7].x, -c[7].y;\n"
+ "RCP R0.x, R0.x;\n"
+ "MUL R0.x, R0.y, R0;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MAD R2.xyz, R0, c[6].x, -R0.w;\n"
+ "MAD R4.xyz, R3, R4, c[7].z;\n"
+ "MUL R5.xyz, R1.w, R2;\n"
+ "MUL R6.xyz, R5, R4;\n"
+ "RSQ R2.w, R3.x;\n"
+ "RCP R4.x, R2.w;\n"
+ "RSQ R2.w, R3.y;\n"
+ "RSQ R3.w, R3.z;\n"
+ "RCP R4.y, R2.w;\n"
+ "RCP R4.z, R3.w;\n"
+ "ADD R4.xyz, -R3, R4;\n"
+ "MUL R6.xyz, R3, R6;\n"
+ "MUL R4.xyz, R5, R4;\n"
+ "ADD R3.xyz, -R3, c[6].z;\n"
+ "MAD R2.xyz, R2, R3, R0.w;\n"
+ "MUL R3.xyz, R0, c[6].x;\n"
+ "MAD R5.xyz, R0.w, R1, R6;\n"
+ "MAD R4.xyz, R0.w, R1, R4;\n"
+ "ADD R6.xyz, R4, -R5;\n"
+ "MUL R4.xyz, R1, c[6].y;\n"
+ "SGE R4.xyz, R4, R1.w;\n"
+ "MAD R4.xyz, R4, R6, R5;\n"
+ "MAD R4.xyz, -R1, R2, R4;\n"
+ "MUL R2.xyz, R1, R2;\n"
+ "SGE R3.xyz, R3, R0.w;\n"
+ "MAD R2.xyz, R3, R4, R2;\n"
+ "ADD R2.w, -R1, c[6].z;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R0.x, R0.w, R1.w;\n"
+ "ADD R0.y, -R0.w, c[6].z;\n"
+ "MAD result.color.xyz, R1, R0.y, R2;\n"
+ "MAD result.color.w, -R0, R1, R0.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DIFFERENCE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..5],\n"
+ " { 2, 4 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[6].x;\n"
+ "MUL R0.z, R0, c[6].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.z, R0.x;\n"
+ "MOV R0.x, c[6];\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.x, -R0.y, R0.z;\n"
+ "RCP R0.y, R0.w;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "ADD R0.xyz, R0, R1;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "MAD result.color.xyz, -R2, c[6].x, R0;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_EXCLUSION_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..5],\n"
+ " { 2, 4, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.x, c[6].x;\n"
+ "MUL R0.z, R0, c[6].y;\n"
+ "MAD R0.x, R0.y, R0.y, -R0.z;\n"
+ "RSQ R0.z, R0.x;\n"
+ "MOV R0.x, c[6];\n"
+ "MUL R0.w, R0.x, c[1].x;\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.x, -R0.y, R0.z;\n"
+ "RCP R0.y, R0.w;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0.w, R1;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[6].x, R3;\n"
+ "ADD R2.w, -R1, c[6].z;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[6].z;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODE_BLEND_MODE_MASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 4 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.z, c[8];\n"
+ "MUL R0.x, R0, c[8];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "ADD R1.x, -R0, R0.y;\n"
+ "MOV R0.x, c[8];\n"
+ "MUL R0.x, R0, c[1];\n"
+ "RCP R1.y, R0.x;\n"
+ "ADD R0.zw, fragment.position.xyxy, c[6].xyxy;\n"
+ "MUL R0.zw, R0, c[5].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "DP4 R1.y, R0, c[7];\n"
+ "TEX R0, R1, texture[1], 1D;\n"
+ "MUL result.color, R0, R1.y;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODE_BLEND_MODE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 4 } };\n"
+ "TEMP R0;\n"
+ "MUL R0.xyz, fragment.position.y, c[3];\n"
+ "MAD R0.xyz, fragment.position.x, c[2], R0;\n"
+ "ADD R0.xyz, R0, c[4];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, -R0, c[1].x;\n"
+ "MUL R0.y, R0.z, c[5];\n"
+ "MUL R0.x, R0, c[5];\n"
+ "MAD R0.z, R0.x, R0.x, -R0.y;\n"
+ "MOV R0.y, c[5].x;\n"
+ "RSQ R0.z, R0.z;\n"
+ "MUL R0.w, R0.y, c[1].x;\n"
+ "RCP R0.y, R0.z;\n"
+ "RCP R0.z, R0.w;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX result.color, R0, texture[0], 1D;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF =
+ "!!ARBfp1.0\n"
+ "PARAM c[13] = { program.local[0..9],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[10].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[11].x, c[11].y;\n"
+ "MAD R1.z, R1, R1.y, -c[11];\n"
+ "MAD R1.z, R1, R1.y, c[11].w;\n"
+ "MAD R1.z, R1, R1.y, -c[12].x;\n"
+ "MAD R1.y, R1.z, R1, c[12];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[10].w;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[10].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R1.x, R0, c[10];\n"
+ "FLR R1.y, R1.x;\n"
+ "MUL R0.xy, fragment.position, c[7];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, R1, -R1.y;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "MUL R2.xyz, R0, c[4].y;\n"
+ "MUL R3.xyz, R1.w, R2;\n"
+ "MUL R2.xyz, R1, c[4].x;\n"
+ "MAD R2.xyz, R0.w, R2, R3;\n"
+ "ADD R3.xy, fragment.position, c[8];\n"
+ "ADD R2.w, -R0, c[12].z;\n"
+ "MUL R1.xyz, R1, c[5].y;\n"
+ "MAD R2.xyz, R2.w, R1, R2;\n"
+ "MUL R1.xyz, R0, c[5].z;\n"
+ "ADD R3.z, -R1.w, c[12];\n"
+ "MAD R2.xyz, R3.z, R1, R2;\n"
+ "MUL R1.y, R1.w, R2.w;\n"
+ "MUL R1.x, R1.w, R0.w;\n"
+ "MUL R1.z, R0.w, R3;\n"
+ "DP3 R2.w, R1, c[5];\n"
+ "MUL R3.xy, R3, c[6];\n"
+ "TEX R1, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[9];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_MULTIPLY =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[8].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[9].x, c[9].y;\n"
+ "MAD R1.z, R1, R1.y, -c[9];\n"
+ "MAD R1.z, R1, R1.y, c[9].w;\n"
+ "MAD R1.z, R1, R1.y, -c[10].x;\n"
+ "MAD R1.y, R1.z, R1, c[10];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[8].w;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[8].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R1.x, R0, c[8];\n"
+ "FLR R1.y, R1.x;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, R1, -R1.y;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "ADD R2.x, -R0.w, c[10].z;\n"
+ "MUL R2.xyz, R1, R2.x;\n"
+ "MAD R1.xyz, R1, R0, R2;\n"
+ "ADD R2.x, -R1.w, c[10].z;\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SCREEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ADD R3.xy, fragment.position, c[6];\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[8].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[9].x, c[9].y;\n"
+ "MAD R1.z, R1, R1.y, -c[9];\n"
+ "MAD R1.z, R1, R1.y, c[9].w;\n"
+ "MAD R1.z, R1, R1.y, -c[10].x;\n"
+ "MAD R1.y, R1.z, R1, c[10];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[8].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[8].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[8];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.z, R0.x, -R0.y;\n"
+ "TEX R1, R0.z, texture[2], 1D;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R2, R1, R0;\n"
+ "MAD R2, -R1, R0, R2;\n"
+ "MUL R3.xy, R3, c[4];\n"
+ "TEX R1, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_OVERLAY =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 0.0020000001, -0.01348047, 0.05747731, 0.1212391 },\n"
+ " { 0.1956359, 0.33299461, 0.99999559, 1.570796 },\n"
+ " { 3.141593, 0.15915494, 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[8];\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[8].y, c[8];\n"
+ "MAD R1.z, R1, R1.y, -c[8].w;\n"
+ "MAD R1.z, R1, R1.y, c[9].x;\n"
+ "MAD R1.z, R1, R1.y, -c[9].y;\n"
+ "MAD R1.y, R1.z, R1, c[9].z;\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[9].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[10].x;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[10].y;\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[10];\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[10].z;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[10].w;\n"
+ "MAD R3.xyz, R3, c[10].z, R4;\n"
+ "MAD R3.xyz, R1, R2.x, R3;\n"
+ "MAD R0.xyz, R1, R2.x, R0;\n"
+ "MUL R2.xyz, R1, c[10].z;\n"
+ "ADD R0.xyz, R0, -R3;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "MAD R2.xyz, R2, R0, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DARKEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[8].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[9].x, c[9].y;\n"
+ "MAD R1.z, R1, R1.y, -c[9];\n"
+ "MAD R1.z, R1, R1.y, c[9].w;\n"
+ "MAD R1.z, R1, R1.y, -c[10].x;\n"
+ "MAD R1.y, R1.z, R1, c[10];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[8].w;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[8].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.z, R0.x, c[8].x;\n"
+ "FLR R0.w, R0.z;\n"
+ "ADD R1.x, R0.z, -R0.w;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[10].z;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[10].z;\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_LIGHTEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[8].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[9].x, c[9].y;\n"
+ "MAD R1.z, R1, R1.y, -c[9];\n"
+ "MAD R1.z, R1, R1.y, c[9].w;\n"
+ "MAD R1.z, R1, R1.y, -c[10].x;\n"
+ "MAD R1.y, R1.z, R1, c[10];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[8].w;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[8].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.z, R0.x, c[8].x;\n"
+ "FLR R0.w, R0.z;\n"
+ "ADD R1.x, R0.z, -R0.w;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[10].z;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[10].z;\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORDODGE =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 0.0020000001, -0.01348047, 0.05747731, 0.1212391 },\n"
+ " { 0.1956359, 0.33299461, 0.99999559, 1.570796 },\n"
+ " { 3.141593, 0.15915494, 1, 1e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[8];\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[8].y, c[8];\n"
+ "MAD R1.z, R1, R1.y, -c[8].w;\n"
+ "MAD R1.z, R1, R1.y, c[9].x;\n"
+ "MAD R1.z, R1, R1.y, -c[9].y;\n"
+ "MAD R1.y, R1.z, R1, c[9].z;\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[9].w;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[10].x;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[10].y;\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MAX R1.x, R0.w, c[10].w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R1.xyz, -R0, R1.x, c[10].z;\n"
+ "MAX R2.xyz, R1, c[10].w;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[10].z;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[10].z;\n"
+ "MAD R4.xyz, R0, R2.w, R3;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R0.xyz, R0, R1.w, R3;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R3, R2, R4;\n"
+ "MAD R4.xyz, R0.w, R1.w, R4;\n"
+ "ADD R4.xyz, R4, -R2;\n"
+ "MAD R2.xyz, R0, R4, R2;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORBURN =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 0.0020000001, -0.01348047, 0.05747731, 0.1212391 },\n"
+ " { 0.1956359, 0.33299461, 0.99999559, 1.570796 },\n"
+ " { 3.141593, 0.15915494, 1, 9.9999997e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R1.x, R0.y, c[8];\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R1.x;\n"
+ "ABS R0.z, -R0.y;\n"
+ "MAX R1.x, R0.w, R0.z;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.w, R0.z;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[8].y, c[8];\n"
+ "MAD R1.z, R1, R1.y, -c[8].w;\n"
+ "MAD R1.z, R1, R1.y, c[9].x;\n"
+ "MAD R1.z, R1, R1.y, -c[9].y;\n"
+ "MAD R1.y, R1.z, R1, c[9].z;\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[9].w;\n"
+ "ADD R0.z, -R0.w, R0;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[10].x;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[10].y;\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R2.xyz, R0.w, R1;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[10].w;\n"
+ "ADD R2.w, -R1, c[10].z;\n"
+ "ADD R3.w, -R0, c[10].z;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R2.xyz, R1, R3.w, R2;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R3.xyz, R3, R2.w;\n"
+ "MAD R2.xyz, R3, R2, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_HARDLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 0.0020000001, -0.01348047, 0.05747731, 0.1212391 },\n"
+ " { 0.1956359, 0.33299461, 0.99999559, 1.570796 },\n"
+ " { 3.141593, 0.15915494, 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[8];\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[8].y, c[8];\n"
+ "MAD R1.z, R1, R1.y, -c[8].w;\n"
+ "MAD R1.z, R1, R1.y, c[9].x;\n"
+ "MAD R1.z, R1, R1.y, -c[9].y;\n"
+ "MAD R1.y, R1.z, R1, c[9].z;\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[9].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[10].x;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[10].y;\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[10];\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[10].z;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[10];\n"
+ "MAD R3.xyz, R3, c[10].z, R4;\n"
+ "MUL R0.xyz, R0, c[10].z;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD R3.xyz, R1, R2.w, R3;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MAD R2.xyz, R0, R2, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SOFTLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[13] = { program.local[0..7],\n"
+ " { 0.0020000001, -0.01348047, 0.05747731, 0.1212391 },\n"
+ " { 0.1956359, 0.33299461, 0.99999559, 1.570796 },\n"
+ " { 3.141593, 0.15915494, 1, 2 },\n"
+ " { 9.9999997e-006, 4, 16, 12 },\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R1.x, R0.y, c[8];\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R1.x;\n"
+ "ABS R0.z, -R0.y;\n"
+ "MAX R1.x, R0.w, R0.z;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.w, R0.z;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[8].y, c[8];\n"
+ "MAD R1.z, R1, R1.y, -c[8].w;\n"
+ "MAD R1.z, R1, R1.y, c[9].x;\n"
+ "MAD R1.z, R1, R1.y, -c[9].y;\n"
+ "MAD R1.y, R1.z, R1, c[9].z;\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[9].w;\n"
+ "ADD R0.z, -R0.w, R0;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[10].x;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "MAX R0.z, R1.w, c[11].x;\n"
+ "RCP R2.x, R0.z;\n"
+ "MUL R3.xyz, R1, R2.x;\n"
+ "MAD R4.xyz, R3, c[11].z, -c[11].w;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[10].y;\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MAD R2.xyz, R0, c[10].w, -R0.w;\n"
+ "MAD R4.xyz, R3, R4, c[12].x;\n"
+ "MUL R5.xyz, R1.w, R2;\n"
+ "MUL R6.xyz, R5, R4;\n"
+ "RSQ R2.w, R3.x;\n"
+ "RCP R4.x, R2.w;\n"
+ "RSQ R2.w, R3.y;\n"
+ "RSQ R3.w, R3.z;\n"
+ "RCP R4.y, R2.w;\n"
+ "RCP R4.z, R3.w;\n"
+ "ADD R4.xyz, -R3, R4;\n"
+ "MUL R6.xyz, R3, R6;\n"
+ "MUL R4.xyz, R5, R4;\n"
+ "ADD R3.xyz, -R3, c[10].z;\n"
+ "MAD R2.xyz, R2, R3, R0.w;\n"
+ "MUL R3.xyz, R0, c[10].w;\n"
+ "MAD R5.xyz, R0.w, R1, R6;\n"
+ "MAD R4.xyz, R0.w, R1, R4;\n"
+ "ADD R6.xyz, R4, -R5;\n"
+ "MUL R4.xyz, R1, c[11].y;\n"
+ "SGE R4.xyz, R4, R1.w;\n"
+ "MAD R4.xyz, R4, R6, R5;\n"
+ "MAD R4.xyz, -R1, R2, R4;\n"
+ "SGE R3.xyz, R3, R0.w;\n"
+ "MUL R2.xyz, R1, R2;\n"
+ "ADD R2.w, -R1, c[10].z;\n"
+ "MAD R2.xyz, R3, R4, R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R0.x, -R0.w, c[10].z;\n"
+ "MAD R2.xyz, R1, R0.x, R2;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DIFFERENCE =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[8].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[9].x, c[9].y;\n"
+ "MAD R1.z, R1, R1.y, -c[9];\n"
+ "MAD R1.z, R1, R1.y, c[9].w;\n"
+ "MAD R1.z, R1, R1.y, -c[10].x;\n"
+ "MAD R1.y, R1.z, R1, c[10];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[8].w;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[8].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.z, R0.x, c[8].x;\n"
+ "FLR R0.w, R0.z;\n"
+ "ADD R1.x, R0.z, -R0.w;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "ADD R2.xyz, R1, R0;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R1.xyz, R1, R0.w;\n"
+ "MIN R1.xyz, R1, R3;\n"
+ "MAD R2.xyz, -R1, c[10].z, R2;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_EXCLUSION =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559, 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[8].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[9].x, c[9].y;\n"
+ "MAD R1.z, R1, R1.y, -c[9];\n"
+ "MAD R1.z, R1, R1.y, c[9].w;\n"
+ "MAD R1.z, R1, R1.y, -c[10].x;\n"
+ "MAD R1.y, R1.z, R1, c[10];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[8].w;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[8].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.z, R0.x, c[8].x;\n"
+ "FLR R0.w, R0.z;\n"
+ "ADD R1.x, R0.z, -R0.w;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "MUL R2.xyz, R1.w, R0;\n"
+ "MAD R3.xyz, R1, R0.w, R2;\n"
+ "MUL R2.xyz, R1, R0;\n"
+ "MAD R2.xyz, -R2, c[10].z, R3;\n"
+ "ADD R2.w, -R0, c[10];\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[10].w;\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..6],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[7].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[8].x, c[8].y;\n"
+ "MAD R1.z, R1, R1.y, -c[8];\n"
+ "MAD R1.z, R1, R1.y, c[8].w;\n"
+ "MAD R1.z, R1, R1.y, -c[9].x;\n"
+ "MAD R1.y, R1.z, R1, c[9];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[7].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[7].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[6].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, c[4].y;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[7];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R3.xyz, R0.w, R2;\n"
+ "MUL R2.xyz, R0, c[4].x;\n"
+ "MAD R2.xyz, R1.w, R2, R3;\n"
+ "ADD R2.w, -R1, c[9].z;\n"
+ "MUL R0.xyz, R0, c[5].y;\n"
+ "MAD R0.xyz, R2.w, R0, R2;\n"
+ "ADD R2.x, -R0.w, c[9].z;\n"
+ "MUL R1.xyz, R1, c[5].z;\n"
+ "MAD result.color.xyz, R2.x, R1, R0;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.z, R1.w, R2.x;\n"
+ "MUL R0.y, R0.w, R2.w;\n"
+ "DP3 result.color.w, R0, c[5];\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_MULTIPLY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[5].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[6].x, c[6].y;\n"
+ "MAD R1.z, R1, R1.y, -c[6];\n"
+ "MAD R1.z, R1, R1.y, c[6].w;\n"
+ "MAD R1.z, R1, R1.y, -c[7].x;\n"
+ "MAD R1.y, R1.z, R1, c[7];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[5].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[5].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[5];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2.x, -R1.w, c[7].z;\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[7].z;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SCREEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[5].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[6].x, c[6].y;\n"
+ "MAD R1.z, R1, R1.y, -c[6];\n"
+ "MAD R1.z, R1, R1.y, c[6].w;\n"
+ "MAD R1.z, R1, R1.y, -c[7].x;\n"
+ "MAD R1.y, R1.z, R1, c[7];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[5].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[5].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[5];\n"
+ "FLR R0.y, R0.x;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2, R0, R1;\n"
+ "MAD result.color, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_OVERLAY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 0.0020000001, -0.01348047, 0.05747731, 0.1212391 },\n"
+ " { 0.1956359, 0.33299461, 0.99999559, 1.570796 },\n"
+ " { 3.141593, 0.15915494, 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[5];\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[5].y, c[5];\n"
+ "MAD R1.z, R1, R1.y, -c[5].w;\n"
+ "MAD R1.z, R1, R1.y, c[6].x;\n"
+ "MAD R1.z, R1, R1.y, -c[6].y;\n"
+ "MAD R1.y, R1.z, R1, c[6].z;\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[6].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[7].x;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[7].y;\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[7];\n"
+ "MUL R2.xyz, R2, c[7].z;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "MUL R0.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[7];\n"
+ "MAD R0.xyz, R0, c[7].z, R3;\n"
+ "MAD R0.xyz, R1, R2.w, R0;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R1.xyz, R1, c[7].z;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R1.xyz, R1, R1.w;\n"
+ "MAD result.color.xyz, R1, R2, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DARKEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[5].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[6].x, c[6].y;\n"
+ "MAD R1.z, R1, R1.y, -c[6];\n"
+ "MAD R1.z, R1, R1.y, c[6].w;\n"
+ "MAD R1.z, R1, R1.y, -c[7].x;\n"
+ "MAD R1.y, R1.z, R1, c[7];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[5].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[5].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[5];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[7].z;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_LIGHTEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[5].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[6].x, c[6].y;\n"
+ "MAD R1.z, R1, R1.y, -c[6];\n"
+ "MAD R1.z, R1, R1.y, c[6].w;\n"
+ "MAD R1.z, R1, R1.y, -c[7].x;\n"
+ "MAD R1.y, R1.z, R1, c[7];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[5].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[5].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[5];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[7].z;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORDODGE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 0.0020000001, -0.01348047, 0.05747731, 0.1212391 },\n"
+ " { 0.1956359, 0.33299461, 0.99999559, 1.570796 },\n"
+ " { 3.141593, 0.15915494, 1, 1e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[5];\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[5].y, c[5];\n"
+ "MAD R1.z, R1, R1.y, -c[5].w;\n"
+ "MAD R1.z, R1, R1.y, c[6].x;\n"
+ "MAD R1.z, R1, R1.y, -c[6].y;\n"
+ "MAD R1.y, R1.z, R1, c[6].z;\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[6].w;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[7].x;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[7].y;\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MAX R1.x, R0.w, c[7].w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R1.xyz, -R0, R1.x, c[7].z;\n"
+ "MAX R2.xyz, R1, c[7].w;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[7].z;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "MAD R3.xyz, R0, R2.w, R3;\n"
+ "MUL R1.xyz, R0.w, R1;\n"
+ "MAD R0.xyz, R0, R1.w, R1;\n"
+ "MUL R2.w, R0, R1;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R1, R2, R3;\n"
+ "MAD R3.xyz, R0.w, R1.w, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "MAD result.color.xyz, R0, R3, R2;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORBURN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 0.0020000001, -0.01348047, 0.05747731, 0.1212391 },\n"
+ " { 0.1956359, 0.33299461, 0.99999559, 1.570796 },\n"
+ " { 3.141593, 0.15915494, 1, 9.9999997e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R1.x, R0.y, c[5];\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R1.x;\n"
+ "ABS R0.z, -R0.y;\n"
+ "MAX R1.x, R0.w, R0.z;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.w, R0.z;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[5].y, c[5];\n"
+ "MAD R1.z, R1, R1.y, -c[5].w;\n"
+ "MAD R1.z, R1, R1.y, c[6].x;\n"
+ "MAD R1.z, R1, R1.y, -c[6].y;\n"
+ "MAD R1.y, R1.z, R1, c[6].z;\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[6].w;\n"
+ "ADD R0.z, -R0.w, R0;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[7].x;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[7].y;\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0.w, R1;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[7].w;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[7].z;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R1.xyz, R1, R3.w, R2;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.x, R0.w, R1.w;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R1.xyz, R1, -R0;\n"
+ "SGE R2.xyz, R3, R2.x;\n"
+ "MAD result.color.xyz, R2, R1, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_HARDLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 0.0020000001, -0.01348047, 0.05747731, 0.1212391 },\n"
+ " { 0.1956359, 0.33299461, 0.99999559, 1.570796 },\n"
+ " { 3.141593, 0.15915494, 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[5];\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[5].y, c[5];\n"
+ "MAD R1.z, R1, R1.y, -c[5].w;\n"
+ "MAD R1.z, R1, R1.y, c[6].x;\n"
+ "MAD R1.z, R1, R1.y, -c[6].y;\n"
+ "MAD R1.y, R1.z, R1, c[6].z;\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[6].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[7].x;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[7].y;\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[7];\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[7].z;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R2.w, -R0, c[7];\n"
+ "MUL R0.xyz, R0, c[7].z;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MAD R3.xyz, R3, c[7].z, R4;\n"
+ "MAD R1.xyz, R1, R2.w, R3;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R1;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD result.color.xyz, R0, R2, R1;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SOFTLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..4],\n"
+ " { 0.0020000001, -0.01348047, 0.05747731, 0.1212391 },\n"
+ " { 0.1956359, 0.33299461, 0.99999559, 1.570796 },\n"
+ " { 3.141593, 0.15915494, 1, 2 },\n"
+ " { 9.9999997e-006, 4, 16, 12 },\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R1.x, R0.y, c[5];\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R1.x;\n"
+ "ABS R0.z, -R0.y;\n"
+ "MAX R1.x, R0.w, R0.z;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.w, R0.z;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[5].y, c[5];\n"
+ "MAD R1.z, R1, R1.y, -c[5].w;\n"
+ "MAD R1.z, R1, R1.y, c[6].x;\n"
+ "MAD R1.z, R1, R1.y, -c[6].y;\n"
+ "MAD R1.y, R1.z, R1, c[6].z;\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[6].w;\n"
+ "ADD R0.z, -R0.w, R0;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[7].x;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "MAX R0.z, R1.w, c[8].x;\n"
+ "RCP R2.x, R0.z;\n"
+ "MUL R3.xyz, R1, R2.x;\n"
+ "MAD R4.xyz, R3, c[8].z, -c[8].w;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[7].y;\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MAD R2.xyz, R0, c[7].w, -R0.w;\n"
+ "MAD R4.xyz, R3, R4, c[9].x;\n"
+ "MUL R5.xyz, R1.w, R2;\n"
+ "MUL R6.xyz, R5, R4;\n"
+ "RSQ R2.w, R3.x;\n"
+ "RCP R4.x, R2.w;\n"
+ "RSQ R2.w, R3.y;\n"
+ "RSQ R3.w, R3.z;\n"
+ "RCP R4.y, R2.w;\n"
+ "RCP R4.z, R3.w;\n"
+ "ADD R4.xyz, -R3, R4;\n"
+ "MUL R6.xyz, R3, R6;\n"
+ "MUL R4.xyz, R5, R4;\n"
+ "ADD R3.xyz, -R3, c[7].z;\n"
+ "MAD R2.xyz, R2, R3, R0.w;\n"
+ "MUL R3.xyz, R0, c[7].w;\n"
+ "MAD R5.xyz, R0.w, R1, R6;\n"
+ "MAD R4.xyz, R0.w, R1, R4;\n"
+ "ADD R6.xyz, R4, -R5;\n"
+ "MUL R4.xyz, R1, c[8].y;\n"
+ "SGE R4.xyz, R4, R1.w;\n"
+ "MAD R4.xyz, R4, R6, R5;\n"
+ "MAD R4.xyz, -R1, R2, R4;\n"
+ "MUL R2.xyz, R1, R2;\n"
+ "SGE R3.xyz, R3, R0.w;\n"
+ "MAD R2.xyz, R3, R4, R2;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R0.x, R0.w, R1.w;\n"
+ "ADD R0.y, -R0.w, c[7].z;\n"
+ "MAD result.color.xyz, R1, R0.y, R2;\n"
+ "MAD result.color.w, -R0, R1, R0.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DIFFERENCE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[5].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[6].x, c[6].y;\n"
+ "MAD R1.z, R1, R1.y, -c[6];\n"
+ "MAD R1.z, R1, R1.y, c[6].w;\n"
+ "MAD R1.z, R1, R1.y, -c[7].x;\n"
+ "MAD R1.y, R1.z, R1, c[7];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[5].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[5].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[5];\n"
+ "FLR R0.y, R0.x;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "ADD R0.xyz, R0, R1;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "MAD result.color.xyz, -R2, c[7].z, R0;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_EXCLUSION_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559, 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[5].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[6].x, c[6].y;\n"
+ "MAD R1.z, R1, R1.y, -c[6];\n"
+ "MAD R1.z, R1, R1.y, c[6].w;\n"
+ "MAD R1.z, R1, R1.y, -c[7].x;\n"
+ "MAD R1.y, R1.z, R1, c[7];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[5].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[5].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[5];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0.w, R1;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[7].z, R3;\n"
+ "ADD R2.w, -R1, c[7];\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[7].w;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODE_BLEND_MODE_MASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..6],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[7].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[8].x, c[8].y;\n"
+ "MAD R1.z, R1, R1.y, -c[8];\n"
+ "MAD R1.z, R1, R1.y, c[8].w;\n"
+ "MAD R1.z, R1, R1.y, -c[9].x;\n"
+ "MAD R1.y, R1.z, R1, c[9];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R1.y, -R1.x, c[7].w;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[7].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R1.x, R0, c[7];\n"
+ "FLR R1.y, R1.x;\n"
+ "ADD R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "MUL R0.xy, R0.zwzw, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, R1, -R1.y;\n"
+ "DP4 R1.y, R0, c[6];\n"
+ "TEX R0, R1, texture[1], 1D;\n"
+ "MUL result.color, R0, R1.y;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODE_BLEND_MODE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..3],\n"
+ " { 0.15915494, 0.0020000001, 3.141593, 1.570796 },\n"
+ " { -0.01348047, 0.05747731, 0.1212391, 0.1956359 },\n"
+ " { 0.33299461, 0.99999559 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.z, R0.x;\n"
+ "ABS R0.w, R0.y;\n"
+ "ADD R0.w, R0, -R0.z;\n"
+ "ADD R1.x, R0.y, c[4].y;\n"
+ "ABS R0.w, R0;\n"
+ "CMP R0.y, -R0.w, R0, R1.x;\n"
+ "ABS R0.w, -R0.y;\n"
+ "MAX R1.x, R0.z, R0.w;\n"
+ "RCP R1.y, R1.x;\n"
+ "MIN R1.x, R0.z, R0.w;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "MUL R1.y, R1.x, R1.x;\n"
+ "MAD R1.z, R1.y, c[5].x, c[5].y;\n"
+ "MAD R1.z, R1, R1.y, -c[5];\n"
+ "MAD R1.z, R1, R1.y, c[5].w;\n"
+ "MAD R1.z, R1, R1.y, -c[6].x;\n"
+ "MAD R1.y, R1.z, R1, c[6];\n"
+ "MUL R1.x, R1.y, R1;\n"
+ "ADD R0.z, -R0, R0.w;\n"
+ "ADD R1.y, -R1.x, c[4].w;\n"
+ "CMP R0.z, -R0, R1.y, R1.x;\n"
+ "ADD R0.w, -R0.z, c[4].z;\n"
+ "CMP R0.x, R0, R0.w, R0.z;\n"
+ "CMP R0.x, -R0.y, -R0, R0;\n"
+ "ADD R0.x, R0, c[0];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX result.color, R0, texture[0], 1D;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SIMPLE_PORTER_DUFF =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..9],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, c[0].xyxy;\n"
+ "ADD R1.x, R0.z, R0.w;\n"
+ "MUL R0.xy, fragment.position, c[7];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.x, R1, c[0].z;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "MUL R2.xyz, R0, c[4].y;\n"
+ "MUL R3.xyz, R1.w, R2;\n"
+ "MUL R2.xyz, R1, c[4].x;\n"
+ "MAD R2.xyz, R0.w, R2, R3;\n"
+ "ADD R3.xy, fragment.position, c[8];\n"
+ "ADD R2.w, -R0, c[10].x;\n"
+ "MUL R1.xyz, R1, c[5].y;\n"
+ "MAD R2.xyz, R2.w, R1, R2;\n"
+ "MUL R1.xyz, R0, c[5].z;\n"
+ "ADD R3.z, -R1.w, c[10].x;\n"
+ "MAD R2.xyz, R3.z, R1, R2;\n"
+ "MUL R1.y, R1.w, R2.w;\n"
+ "MUL R1.x, R1.w, R0.w;\n"
+ "MUL R1.z, R0.w, R3;\n"
+ "DP3 R2.w, R1, c[5];\n"
+ "MUL R3.xy, R3, c[6];\n"
+ "TEX R1, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[9];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_MULTIPLY =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, c[0].xyxy;\n"
+ "ADD R1.x, R0.z, R0.w;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.x, R1, c[0].z;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MUL R2.xyz, R1, R2.x;\n"
+ "MAD R1.xyz, R1, R0, R2;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SCREEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, R0.x, c[0];\n"
+ "ADD R3.xy, fragment.position, c[6];\n"
+ "TEX R1, R0.z, texture[2], 1D;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R2, R1, R0;\n"
+ "MAD R2, -R1, R0, R2;\n"
+ "MUL R3.xy, R3, c[4];\n"
+ "TEX R1, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_OVERLAY =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[8].y;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[8].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8].y;\n"
+ "MAD R3.xyz, R3, c[8].x, R4;\n"
+ "MAD R3.xyz, R1, R2.x, R3;\n"
+ "MAD R0.xyz, R1, R2.x, R0;\n"
+ "MUL R2.xyz, R1, c[8].x;\n"
+ "ADD R0.xyz, R0, -R3;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "MAD R2.xyz, R2, R0, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DARKEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.z, R0.x, R0.y;\n"
+ "MUL R1.x, R0.z, c[0].z;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_LIGHTEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.z, R0.x, R0.y;\n"
+ "MUL R1.x, R0.z, c[0].z;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORDODGE =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 1e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MAX R1.x, R0.w, c[8].y;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R2.xyz, -R0, R1.x, c[8].x;\n"
+ "MAX R2.xyz, R2, c[8].y;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R4.xyz, R0, R2.w, R3;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R0.xyz, R0, R1.w, R3;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R3, R2, R4;\n"
+ "MAD R4.xyz, R0.w, R1.w, R4;\n"
+ "ADD R4.xyz, R4, -R2;\n"
+ "MAD R2.xyz, R0, R4, R2;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORBURN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 9.9999997e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R2.xyz, R0.w, R1;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[8].y;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[8].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R2.xyz, R1, R3.w, R2;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R3.xyz, R3, R2.w;\n"
+ "MAD R2.xyz, R3, R2, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_HARDLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[8].y;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[8].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[8].y;\n"
+ "MAD R3.xyz, R3, c[8].x, R4;\n"
+ "MUL R0.xyz, R0, c[8].x;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD R3.xyz, R1, R2.w, R3;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MAD R2.xyz, R0, R2, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SOFTLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..7],\n"
+ " { 1, 2, 9.9999997e-006, 4 },\n"
+ " { 16, 12, 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MAX R0.z, R1.w, c[8];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R3.xyz, R1, R0.z;\n"
+ "MAD R2.xyz, R3, c[9].x, -c[9].y;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MAD R4.xyz, R3, R2, c[9].z;\n"
+ "MAD R2.xyz, R0, c[8].y, -R0.w;\n"
+ "MUL R5.xyz, R1.w, R2;\n"
+ "MUL R6.xyz, R5, R4;\n"
+ "RSQ R2.w, R3.x;\n"
+ "RCP R4.x, R2.w;\n"
+ "RSQ R2.w, R3.y;\n"
+ "RSQ R3.w, R3.z;\n"
+ "RCP R4.y, R2.w;\n"
+ "RCP R4.z, R3.w;\n"
+ "ADD R4.xyz, -R3, R4;\n"
+ "MUL R6.xyz, R3, R6;\n"
+ "MUL R4.xyz, R5, R4;\n"
+ "ADD R3.xyz, -R3, c[8].x;\n"
+ "MAD R2.xyz, R2, R3, R0.w;\n"
+ "MUL R3.xyz, R0, c[8].y;\n"
+ "MAD R5.xyz, R0.w, R1, R6;\n"
+ "MAD R4.xyz, R0.w, R1, R4;\n"
+ "ADD R6.xyz, R4, -R5;\n"
+ "MUL R4.xyz, R1, c[8].w;\n"
+ "SGE R4.xyz, R4, R1.w;\n"
+ "MAD R4.xyz, R4, R6, R5;\n"
+ "MAD R4.xyz, -R1, R2, R4;\n"
+ "SGE R3.xyz, R3, R0.w;\n"
+ "MUL R2.xyz, R1, R2;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R2.xyz, R3, R4, R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R0.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R0.x, R2;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DIFFERENCE =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.z, R0.x, R0.y;\n"
+ "MUL R1.x, R0.z, c[0].z;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "ADD R2.xyz, R1, R0;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R1.xyz, R1, R0.w;\n"
+ "MIN R1.xyz, R1, R3;\n"
+ "MAD R2.xyz, -R1, c[8].x, R2;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_EXCLUSION =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.z, R0.x, R0.y;\n"
+ "MUL R1.x, R0.z, c[0].z;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 1D;\n"
+ "MUL R2.xyz, R1.w, R0;\n"
+ "MAD R3.xyz, R1, R0.w, R2;\n"
+ "MUL R2.xyz, R1, R0;\n"
+ "MAD R2.xyz, -R2, c[8].x, R3;\n"
+ "ADD R2.w, -R0, c[8].y;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[8].y;\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..6],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[6].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, c[4].y;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R3.xyz, R0.w, R2;\n"
+ "MUL R2.xyz, R0, c[4].x;\n"
+ "MAD R2.xyz, R1.w, R2, R3;\n"
+ "ADD R2.w, -R1, c[7].x;\n"
+ "MUL R0.xyz, R0, c[5].y;\n"
+ "MAD R0.xyz, R2.w, R0, R2;\n"
+ "ADD R2.x, -R0.w, c[7];\n"
+ "MUL R1.xyz, R1, c[5].z;\n"
+ "MAD result.color.xyz, R2.x, R1, R0;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.z, R1.w, R2.x;\n"
+ "MUL R0.y, R0.w, R2.w;\n"
+ "DP3 result.color.w, R0, c[5];\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_MULTIPLY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2.x, -R1.w, c[5];\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SCREEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..4] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2, R0, R1;\n"
+ "MAD result.color, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_OVERLAY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].y;\n"
+ "MUL R2.xyz, R2, c[5].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "MUL R0.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[5].y;\n"
+ "MAD R0.xyz, R0, c[5].x, R3;\n"
+ "MAD R0.xyz, R1, R2.w, R0;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R1.xyz, R1, c[5].x;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R1.xyz, R1, R1.w;\n"
+ "MAD result.color.xyz, R1, R2, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DARKEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_LIGHTEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORDODGE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 1e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MAX R1.x, R0.w, c[5].y;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R3.xyz, -R0, R1.x, c[5].x;\n"
+ "MAX R3.xyz, R3, c[5].y;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.x, -R0.w, c[5];\n"
+ "MUL R2.xyz, R1, R2.x;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R1.xyz, R0.w, R1;\n"
+ "MAD R0.xyz, R0, R1.w, R1;\n"
+ "MUL R2.w, R0, R1;\n"
+ "RCP R3.x, R3.x;\n"
+ "RCP R3.y, R3.y;\n"
+ "RCP R3.z, R3.z;\n"
+ "MAD R3.xyz, R1, R3, R2;\n"
+ "MAD R2.xyz, R0.w, R1.w, R2;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "MAD result.color.xyz, R0, R2, R3;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORBURN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 9.9999997e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0.w, R1;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[5].y;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[5].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R1.xyz, R1, R3.w, R2;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.x, R0.w, R1.w;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R1.xyz, R1, -R0;\n"
+ "SGE R2.xyz, R3, R2.x;\n"
+ "MAD result.color.xyz, R2, R1, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_HARDLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[5].y;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[5].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MUL R0.xyz, R0, c[5].x;\n"
+ "ADD R2.w, -R0, c[5].y;\n"
+ "MAD R3.xyz, R3, c[5].x, R4;\n"
+ "MAD R3.xyz, R1, R2.w, R3;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R1.xyz, R1, -R3;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD result.color.xyz, R0, R1, R3;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SOFTLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..4],\n"
+ " { 1, 2, 9.9999997e-006, 4 },\n"
+ " { 16, 12, 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MAX R0.z, R1.w, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R3.xyz, R1, R0.z;\n"
+ "MAD R2.xyz, R3, c[6].x, -c[6].y;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MAD R4.xyz, R3, R2, c[6].z;\n"
+ "MAD R2.xyz, R0, c[5].y, -R0.w;\n"
+ "MUL R5.xyz, R1.w, R2;\n"
+ "MUL R6.xyz, R5, R4;\n"
+ "RSQ R2.w, R3.x;\n"
+ "RCP R4.x, R2.w;\n"
+ "RSQ R2.w, R3.y;\n"
+ "RSQ R3.w, R3.z;\n"
+ "RCP R4.y, R2.w;\n"
+ "RCP R4.z, R3.w;\n"
+ "ADD R4.xyz, -R3, R4;\n"
+ "MUL R6.xyz, R3, R6;\n"
+ "MUL R4.xyz, R5, R4;\n"
+ "ADD R3.xyz, -R3, c[5].x;\n"
+ "MAD R2.xyz, R2, R3, R0.w;\n"
+ "MUL R3.xyz, R0, c[5].y;\n"
+ "MAD R5.xyz, R0.w, R1, R6;\n"
+ "MAD R4.xyz, R0.w, R1, R4;\n"
+ "ADD R6.xyz, R4, -R5;\n"
+ "MUL R4.xyz, R1, c[5].w;\n"
+ "SGE R4.xyz, R4, R1.w;\n"
+ "MAD R4.xyz, R4, R6, R5;\n"
+ "MAD R4.xyz, -R1, R2, R4;\n"
+ "MUL R2.xyz, R1, R2;\n"
+ "SGE R3.xyz, R3, R0.w;\n"
+ "MAD R2.xyz, R3, R4, R2;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R0.x, R0.w, R1.w;\n"
+ "ADD R0.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R0.y, R2;\n"
+ "MAD result.color.w, -R0, R1, R0.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DIFFERENCE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "ADD R0.xyz, R0, R1;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "MAD result.color.xyz, -R2, c[5].x, R0;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_EXCLUSION_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0.w, R1;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[5].x, R3;\n"
+ "ADD R2.w, -R1, c[5].y;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5];\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODE_BLEND_MODE_MASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.z;\n"
+ "MUL R0.zw, R0, c[0].xyxy;\n"
+ "ADD R1.x, R0.z, R0.w;\n"
+ "ADD R0.xy, fragment.position, c[5];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "DP4 R1.y, R0, c[6];\n"
+ "MUL R1.x, R1, c[0].z;\n"
+ "TEX R0, R1, texture[1], 1D;\n"
+ "MUL result.color, R0, R1.y;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODE_BLEND_MODE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[4] = { program.local[0..3] };\n"
+ "TEMP R0;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[0].z;\n"
+ "TEX result.color, R0, texture[0], 1D;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SIMPLE_PORTER_DUFF =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..9],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R1.xyz, R0, c[3];\n"
+ "RCP R0.z, R1.z;\n"
+ "MUL R1.xy, R1, R0.z;\n"
+ "MUL R0.xy, fragment.position, c[7];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R1, R1, texture[2], 2D;\n"
+ "MUL R2.xyz, R0, c[4].y;\n"
+ "MUL R3.xyz, R1.w, R2;\n"
+ "MUL R2.xyz, R1, c[4].x;\n"
+ "MAD R2.xyz, R0.w, R2, R3;\n"
+ "ADD R3.xy, fragment.position, c[8];\n"
+ "ADD R2.w, -R0, c[10].x;\n"
+ "MUL R1.xyz, R1, c[5].y;\n"
+ "MAD R2.xyz, R2.w, R1, R2;\n"
+ "MUL R1.xyz, R0, c[5].z;\n"
+ "ADD R3.z, -R1.w, c[10].x;\n"
+ "MAD R2.xyz, R3.z, R1, R2;\n"
+ "MUL R1.y, R1.w, R2.w;\n"
+ "MUL R1.x, R1.w, R0.w;\n"
+ "MUL R1.z, R0.w, R3;\n"
+ "DP3 R2.w, R1, c[5];\n"
+ "MUL R3.xy, R3, c[6];\n"
+ "TEX R1, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[9];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_MULTIPLY =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R1.xyz, R0, c[3];\n"
+ "RCP R0.z, R1.z;\n"
+ "MUL R1.xy, R1, R0.z;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R1, R1, texture[2], 2D;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MUL R2.xyz, R1, R2.x;\n"
+ "MAD R1.xyz, R1, R0, R2;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SCREEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, c[0].xyxy;\n"
+ "ADD R3.xy, fragment.position, c[6];\n"
+ "TEX R1, R0.zwzw, texture[2], 2D;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R2, R1, R0;\n"
+ "MAD R2, -R1, R0, R2;\n"
+ "MUL R3.xy, R3, c[4];\n"
+ "TEX R1, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_OVERLAY =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[8].y;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[8].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8].y;\n"
+ "MAD R3.xyz, R3, c[8].x, R4;\n"
+ "MAD R3.xyz, R1, R2.x, R3;\n"
+ "MAD R0.xyz, R1, R2.x, R0;\n"
+ "MUL R2.xyz, R1, c[8].x;\n"
+ "ADD R0.xyz, R0, -R3;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "MAD R2.xyz, R2, R0, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DARKEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.z;\n"
+ "MUL R1.xy, R0.zwzw, c[0];\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 2D;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_LIGHTEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.z;\n"
+ "MUL R1.xy, R0.zwzw, c[0];\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 2D;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORDODGE =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 1e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MAX R1.x, R0.w, c[8].y;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R2.xyz, -R0, R1.x, c[8].x;\n"
+ "MAX R2.xyz, R2, c[8].y;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R4.xyz, R0, R2.w, R3;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R0.xyz, R0, R1.w, R3;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R3, R2, R4;\n"
+ "MAD R4.xyz, R0.w, R1.w, R4;\n"
+ "ADD R4.xyz, R4, -R2;\n"
+ "MAD R2.xyz, R0, R4, R2;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORBURN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 9.9999997e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MUL R2.xyz, R0.w, R1;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[8].y;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[8].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R2.xyz, R1, R3.w, R2;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R3.xyz, R3, R2.w;\n"
+ "MAD R2.xyz, R3, R2, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_HARDLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[8].y;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[8].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[8].y;\n"
+ "MAD R3.xyz, R3, c[8].x, R4;\n"
+ "MUL R0.xyz, R0, c[8].x;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD R3.xyz, R1, R2.w, R3;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MAD R2.xyz, R0, R2, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SOFTLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..7],\n"
+ " { 1, 2, 9.9999997e-006, 4 },\n"
+ " { 16, 12, 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MUL R1.xy, fragment.position, c[5];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MAX R0.w, R1, c[8].z;\n"
+ "RCP R0.w, R0.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MAD R2.xyz, R3, c[9].x, -c[9].y;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MAD R4.xyz, R3, R2, c[9].z;\n"
+ "MAD R2.xyz, R0, c[8].y, -R0.w;\n"
+ "MUL R5.xyz, R1.w, R2;\n"
+ "MUL R6.xyz, R5, R4;\n"
+ "RSQ R2.w, R3.x;\n"
+ "RCP R4.x, R2.w;\n"
+ "RSQ R2.w, R3.y;\n"
+ "RSQ R3.w, R3.z;\n"
+ "RCP R4.y, R2.w;\n"
+ "RCP R4.z, R3.w;\n"
+ "ADD R4.xyz, -R3, R4;\n"
+ "MUL R6.xyz, R3, R6;\n"
+ "MUL R4.xyz, R5, R4;\n"
+ "ADD R3.xyz, -R3, c[8].x;\n"
+ "MAD R2.xyz, R2, R3, R0.w;\n"
+ "MUL R3.xyz, R0, c[8].y;\n"
+ "MAD R5.xyz, R0.w, R1, R6;\n"
+ "MAD R4.xyz, R0.w, R1, R4;\n"
+ "ADD R6.xyz, R4, -R5;\n"
+ "MUL R4.xyz, R1, c[8].w;\n"
+ "SGE R4.xyz, R4, R1.w;\n"
+ "MAD R4.xyz, R4, R6, R5;\n"
+ "MAD R4.xyz, -R1, R2, R4;\n"
+ "SGE R3.xyz, R3, R0.w;\n"
+ "MUL R2.xyz, R1, R2;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R2.xyz, R3, R4, R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R0.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R0.x, R2;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[7];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DIFFERENCE =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.z;\n"
+ "MUL R1.xy, R0.zwzw, c[0];\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 2D;\n"
+ "ADD R2.xyz, R1, R0;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R1.xyz, R1, R0.w;\n"
+ "MIN R1.xyz, R1, R3;\n"
+ "MAD R2.xyz, -R1, c[8].x, R2;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_EXCLUSION =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.z;\n"
+ "MUL R1.xy, R0.zwzw, c[0];\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1, R1, texture[2], 2D;\n"
+ "MUL R2.xyz, R1.w, R0;\n"
+ "MAD R3.xyz, R1, R0.w, R2;\n"
+ "MUL R2.xyz, R1, R0;\n"
+ "MAD R2.xyz, -R2, c[8].x, R3;\n"
+ "ADD R2.w, -R0, c[8].y;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[8].y;\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..6],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R1.xy, fragment.position, c[6];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, c[4].y;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R3.xyz, R0.w, R2;\n"
+ "MUL R2.xyz, R0, c[4].x;\n"
+ "MAD R2.xyz, R1.w, R2, R3;\n"
+ "ADD R2.w, -R1, c[7].x;\n"
+ "MUL R0.xyz, R0, c[5].y;\n"
+ "MAD R0.xyz, R2.w, R0, R2;\n"
+ "ADD R2.x, -R0.w, c[7];\n"
+ "MUL R1.xyz, R1, c[5].z;\n"
+ "MAD result.color.xyz, R2.x, R1, R0;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.z, R1.w, R2.x;\n"
+ "MUL R0.y, R0.w, R2.w;\n"
+ "DP3 result.color.w, R0, c[5];\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_MULTIPLY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2.x, -R1.w, c[5];\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SCREEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..4] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R0, R1;\n"
+ "MAD result.color, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_OVERLAY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].y;\n"
+ "MUL R2.xyz, R2, c[5].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "MUL R0.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[5].y;\n"
+ "MAD R0.xyz, R0, c[5].x, R3;\n"
+ "MAD R0.xyz, R1, R2.w, R0;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R1.xyz, R1, c[5].x;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R1.xyz, R1, R1.w;\n"
+ "MAD result.color.xyz, R1, R2, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DARKEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_LIGHTEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORDODGE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 1e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MAX R1.x, R0.w, c[5].y;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R3.xyz, -R0, R1.x, c[5].x;\n"
+ "MAX R3.xyz, R3, c[5].y;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.x, -R0.w, c[5];\n"
+ "MUL R2.xyz, R1, R2.x;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R1.xyz, R0.w, R1;\n"
+ "MAD R0.xyz, R0, R1.w, R1;\n"
+ "MUL R2.w, R0, R1;\n"
+ "RCP R3.x, R3.x;\n"
+ "RCP R3.y, R3.y;\n"
+ "RCP R3.z, R3.z;\n"
+ "MAD R3.xyz, R1, R3, R2;\n"
+ "MAD R2.xyz, R0.w, R1.w, R2;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "MAD result.color.xyz, R0, R2, R3;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORBURN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 9.9999997e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R2.xyz, R0.w, R1;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[5].y;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[5].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R1.xyz, R1, R3.w, R2;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.x, R0.w, R1.w;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R1.xyz, R1, -R0;\n"
+ "SGE R2.xyz, R3, R2.x;\n"
+ "MAD result.color.xyz, R2, R1, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_HARDLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[5].y;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[5].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MUL R0.xyz, R0, c[5].x;\n"
+ "ADD R2.w, -R0, c[5].y;\n"
+ "MAD R3.xyz, R3, c[5].x, R4;\n"
+ "MAD R3.xyz, R1, R2.w, R3;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R1.xyz, R1, -R3;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD result.color.xyz, R0, R1, R3;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SOFTLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..4],\n"
+ " { 1, 2, 9.9999997e-006, 4 },\n"
+ " { 16, 12, 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MAX R0.w, R1, c[5].z;\n"
+ "RCP R0.w, R0.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MAD R2.xyz, R3, c[6].x, -c[6].y;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MAD R4.xyz, R3, R2, c[6].z;\n"
+ "MAD R2.xyz, R0, c[5].y, -R0.w;\n"
+ "MUL R5.xyz, R1.w, R2;\n"
+ "MUL R6.xyz, R5, R4;\n"
+ "RSQ R2.w, R3.x;\n"
+ "RCP R4.x, R2.w;\n"
+ "RSQ R2.w, R3.y;\n"
+ "RSQ R3.w, R3.z;\n"
+ "RCP R4.y, R2.w;\n"
+ "RCP R4.z, R3.w;\n"
+ "ADD R4.xyz, -R3, R4;\n"
+ "MUL R6.xyz, R3, R6;\n"
+ "MUL R4.xyz, R5, R4;\n"
+ "ADD R3.xyz, -R3, c[5].x;\n"
+ "MAD R2.xyz, R2, R3, R0.w;\n"
+ "MUL R3.xyz, R0, c[5].y;\n"
+ "MAD R5.xyz, R0.w, R1, R6;\n"
+ "MAD R4.xyz, R0.w, R1, R4;\n"
+ "ADD R6.xyz, R4, -R5;\n"
+ "MUL R4.xyz, R1, c[5].w;\n"
+ "SGE R4.xyz, R4, R1.w;\n"
+ "MAD R4.xyz, R4, R6, R5;\n"
+ "MAD R4.xyz, -R1, R2, R4;\n"
+ "MUL R2.xyz, R1, R2;\n"
+ "SGE R3.xyz, R3, R0.w;\n"
+ "MAD R2.xyz, R3, R4, R2;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R0.x, R0.w, R1.w;\n"
+ "ADD R0.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R0.y, R2;\n"
+ "MAD result.color.w, -R0, R1, R0.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DIFFERENCE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R0.w, R1;\n"
+ "ADD R0.xyz, R0, R1;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "MAD result.color.xyz, -R2, c[5].x, R0;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_EXCLUSION_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R2.xyz, R0.w, R1;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[5].x, R3;\n"
+ "ADD R2.w, -R1, c[5].y;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5];\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODE_BLEND_MODE_MASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R1.xyz, R0, c[3];\n"
+ "RCP R0.z, R1.z;\n"
+ "MUL R1.xy, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[5];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "DP4 R1.z, R0, c[6];\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R0, R1, texture[1], 2D;\n"
+ "MUL result.color, R0, R1.z;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODE_BLEND_MODE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[4] = { program.local[0..3] };\n"
+ "TEMP R0;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX result.color, R0, texture[0], 2D;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SIMPLE_PORTER_DUFF =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..9],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, c[0].xyxy;\n"
+ "TEX R1.x, R0.zwzw, texture[2], 2D;\n"
+ "MUL R0.xy, fragment.position, c[7];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, -R1, c[10];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R2.xyz, R0, c[4].y;\n"
+ "MUL R3.xyz, R1.w, R2;\n"
+ "MUL R2.xyz, R1, c[4].x;\n"
+ "MAD R2.xyz, R0.w, R2, R3;\n"
+ "ADD R3.xy, fragment.position, c[8];\n"
+ "ADD R2.w, -R0, c[10].x;\n"
+ "MUL R1.xyz, R1, c[5].y;\n"
+ "MAD R2.xyz, R2.w, R1, R2;\n"
+ "MUL R1.xyz, R0, c[5].z;\n"
+ "ADD R3.z, -R1.w, c[10].x;\n"
+ "MAD R2.xyz, R3.z, R1, R2;\n"
+ "MUL R1.y, R1.w, R2.w;\n"
+ "MUL R1.x, R1.w, R0.w;\n"
+ "MUL R1.z, R0.w, R3;\n"
+ "DP3 R2.w, R1, c[5];\n"
+ "MUL R3.xy, R3, c[6];\n"
+ "TEX R1, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[9];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_MULTIPLY =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, c[0].xyxy;\n"
+ "TEX R1.x, R0.zwzw, texture[2], 2D;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, -R1, c[8];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MUL R2.xyz, R1, R2.x;\n"
+ "MAD R1.xyz, R1, R0, R2;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SCREEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[2], 2D;\n"
+ "ADD R0.z, -R0.x, c[8].x;\n"
+ "ADD R3.xy, fragment.position, c[6];\n"
+ "MUL R1, fragment.color.primary, R0.z;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R2, R1, R0;\n"
+ "MAD R2, -R1, R0, R2;\n"
+ "MUL R3.xy, R3, c[4];\n"
+ "TEX R1, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_OVERLAY =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[2], 2D;\n"
+ "ADD R0.x, -R0, c[8];\n"
+ "MUL R1, fragment.color.primary, R0.x;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "ADD R3.xyz, R1.w, -R1;\n"
+ "ADD R2.xyz, R0.w, -R0;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[8].y;\n"
+ "MAD R2.xyz, R1.w, R0.w, -R2;\n"
+ "MUL R4.xyz, R1, R2.w;\n"
+ "MUL R3.xyz, R1, R0;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MAD R3.xyz, R3, c[8].y, R4;\n"
+ "MAD R3.xyz, R0, R2.x, R3;\n"
+ "MAD R1.xyz, R0, R2.x, R1;\n"
+ "MUL R2.xyz, R0, c[8].y;\n"
+ "ADD R1.xyz, R1, -R3;\n"
+ "SGE R2.xyz, R2, R0.w;\n"
+ "MAD R2.xyz, R2, R1, R3;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DARKEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R1.x, R0, texture[2], 2D;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, -R1, c[8];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_LIGHTEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R1.x, R0, texture[2], 2D;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, -R1, c[8];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORDODGE =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 1e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[2], 2D;\n"
+ "ADD R0.x, -R0, c[8];\n"
+ "MUL R1, fragment.color.primary, R0.x;\n"
+ "MAX R0.x, R1.w, c[8].y;\n"
+ "RCP R0.x, R0.x;\n"
+ "MAD R2.xyz, -R1, R0.x, c[8].x;\n"
+ "MAX R2.xyz, R2, c[8].y;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MAD R4.xyz, R1, R2.w, R3;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R2.w, R1, R0;\n"
+ "MAD R1.xyz, R1, R0.w, R3;\n"
+ "SGE R1.xyz, R1, R2.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R3, R2, R4;\n"
+ "MAD R4.xyz, R1.w, R0.w, R4;\n"
+ "ADD R4.xyz, R4, -R2;\n"
+ "MAD R2.xyz, R1, R4, R2;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORBURN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 9.9999997e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[2], 2D;\n"
+ "ADD R1.x, -R0, c[8];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "MUL R2.xyz, R1.w, R0;\n"
+ "MAD R3.xyz, R1, R0.w, R2;\n"
+ "MAD R2.xyz, -R1.w, R0.w, R3;\n"
+ "MUL R4.xyz, R1.w, R2;\n"
+ "MAX R2.xyz, R1, c[8].y;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MUL R5.xyz, R1, R2.w;\n"
+ "ADD R3.w, -R1, c[8].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R0, R3.w;\n"
+ "MAD R1.xyz, R1, R2.w, R4;\n"
+ "MUL R2.w, R1, R0;\n"
+ "MAD R2.xyz, R0, R3.w, R2;\n"
+ "ADD R2.xyz, R2, -R1;\n"
+ "SGE R3.xyz, R3, R2.w;\n"
+ "MAD R2.xyz, R3, R2, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_HARDLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[2], 2D;\n"
+ "ADD R0.x, -R0, c[8];\n"
+ "MUL R1, fragment.color.primary, R0.x;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[5].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "ADD R3.xyz, R1.w, -R1;\n"
+ "ADD R2.xyz, R0.w, -R0;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[8].y;\n"
+ "MAD R2.xyz, R1.w, R0.w, -R2;\n"
+ "MUL R4.xyz, R1, R2.w;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R3.xyz, R1, R0;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R3.xyz, R3, c[8].y, R4;\n"
+ "MUL R1.xyz, R1, c[8].y;\n"
+ "SGE R1.xyz, R1, R1.w;\n"
+ "MAD R3.xyz, R0, R2.w, R3;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MAD R2.xyz, R1, R2, R3;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SOFTLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..7],\n"
+ " { 1, 2, 9.9999997e-006, 4 },\n"
+ " { 16, 12, 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R1.xyz, R0, c[3];\n"
+ "RCP R1.z, R1.z;\n"
+ "MUL R1.xy, R1, R1.z;\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R1.x, R1, texture[2], 2D;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MAX R1.z, R0.w, c[8];\n"
+ "RCP R1.z, R1.z;\n"
+ "MUL R3.xyz, R0, R1.z;\n"
+ "MAD R2.xyz, R3, c[9].x, -c[9].y;\n"
+ "ADD R1.x, -R1, c[8];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MAD R4.xyz, R3, R2, c[9].z;\n"
+ "MAD R2.xyz, R1, c[8].y, -R1.w;\n"
+ "MUL R5.xyz, R0.w, R2;\n"
+ "MUL R6.xyz, R5, R4;\n"
+ "RSQ R2.w, R3.x;\n"
+ "RCP R4.x, R2.w;\n"
+ "RSQ R2.w, R3.y;\n"
+ "RSQ R3.w, R3.z;\n"
+ "RCP R4.y, R2.w;\n"
+ "RCP R4.z, R3.w;\n"
+ "ADD R4.xyz, -R3, R4;\n"
+ "MUL R6.xyz, R3, R6;\n"
+ "MUL R4.xyz, R5, R4;\n"
+ "ADD R3.xyz, -R3, c[8].x;\n"
+ "MAD R2.xyz, R2, R3, R1.w;\n"
+ "MUL R3.xyz, R1, c[8].y;\n"
+ "MAD R5.xyz, R1.w, R0, R6;\n"
+ "MAD R4.xyz, R1.w, R0, R4;\n"
+ "ADD R6.xyz, R4, -R5;\n"
+ "MUL R4.xyz, R0, c[8].w;\n"
+ "SGE R4.xyz, R4, R0.w;\n"
+ "MAD R4.xyz, R4, R6, R5;\n"
+ "MAD R4.xyz, -R0, R2, R4;\n"
+ "SGE R3.xyz, R3, R1.w;\n"
+ "MUL R2.xyz, R0, R2;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MAD R2.xyz, R3, R4, R2;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "ADD R1.x, -R1.w, c[8];\n"
+ "MAD R2.xyz, R0, R1.x, R2;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DIFFERENCE =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R1.x, R0, texture[2], 2D;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "ADD R1.x, -R1, c[8];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "ADD R2.xyz, R1, R0;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R1.xyz, R1, R0.w;\n"
+ "MIN R1.xyz, R1, R3;\n"
+ "MAD R2.xyz, -R1, c[8].y, R2;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_EXCLUSION =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R1.x, R0, texture[2], 2D;\n"
+ "MUL R0.xy, fragment.position, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, -R1, c[8];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R2.xyz, R1.w, R0;\n"
+ "MAD R3.xyz, R1, R0.w, R2;\n"
+ "MUL R2.xyz, R1, R0;\n"
+ "MAD R2.xyz, -R2, c[8].y, R3;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[6];\n"
+ "MUL R1.xy, R1, c[4];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[7];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..6],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R1.x, R0, texture[1], 2D;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[6].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "MUL R2.xyz, R0, c[4].y;\n"
+ "ADD R1.x, -R1, c[7];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R3.xyz, R1.w, R2;\n"
+ "MUL R2.xyz, R1, c[4].x;\n"
+ "MUL R0.xyz, R0, c[5].z;\n"
+ "MAD R2.xyz, R0.w, R2, R3;\n"
+ "ADD R2.w, -R0, c[7].x;\n"
+ "MUL R1.xyz, R1, c[5].y;\n"
+ "MAD R1.xyz, R2.w, R1, R2;\n"
+ "ADD R2.x, -R1.w, c[7];\n"
+ "MAD result.color.xyz, R2.x, R0, R1;\n"
+ "MUL R0.x, R1.w, R0.w;\n"
+ "MUL R0.z, R0.w, R2.x;\n"
+ "MUL R0.y, R1.w, R2.w;\n"
+ "DP3 result.color.w, R0, c[5];\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_MULTIPLY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R1.x, R0, texture[1], 2D;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "ADD R1.x, -R1, c[5];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "ADD R2.x, -R0.w, c[5];\n"
+ "MUL R2.xyz, R1, R2.x;\n"
+ "MAD R1.xyz, R1, R0, R2;\n"
+ "ADD R2.x, R1.w, R0.w;\n"
+ "ADD R2.y, -R1.w, c[5].x;\n"
+ "MAD result.color.xyz, R0, R2.y, R1;\n"
+ "MAD result.color.w, -R1, R0, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SCREEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[1], 2D;\n"
+ "ADD R1.x, -R0, c[5];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "ADD R2, R1, R0;\n"
+ "MAD result.color, -R1, R0, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_OVERLAY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[1], 2D;\n"
+ "ADD R0.x, -R0, c[5];\n"
+ "MUL R1, fragment.color.primary, R0.x;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "ADD R3.xyz, R1.w, -R1;\n"
+ "ADD R2.xyz, R0.w, -R0;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MUL R2.xyz, R2, c[5].y;\n"
+ "MAD R2.xyz, R1.w, R0.w, -R2;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "MUL R1.xyz, R1, R0;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R1.xyz, R1, c[5].y, R3;\n"
+ "MAD R1.xyz, R0, R2.w, R1;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R0.xyz, R0, c[5].y;\n"
+ "ADD R2.w, R1, R0;\n"
+ "ADD R2.xyz, R2, -R1;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD result.color.xyz, R0, R2, R1;\n"
+ "MAD result.color.w, -R1, R0, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DARKEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[1], 2D;\n"
+ "ADD R1.x, -R0, c[5];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, R1.w, R0.w;\n"
+ "ADD R2.y, -R1.w, c[5].x;\n"
+ "MAD result.color.xyz, R0, R2.y, R1;\n"
+ "MAD result.color.w, -R1, R0, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_LIGHTEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[1], 2D;\n"
+ "ADD R1.x, -R0, c[5];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, R1.w, R0.w;\n"
+ "ADD R2.y, -R1.w, c[5].x;\n"
+ "MAD result.color.xyz, R0, R2.y, R1;\n"
+ "MAD result.color.w, -R1, R0, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORDODGE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 1e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[1], 2D;\n"
+ "ADD R0.x, -R0, c[5];\n"
+ "MUL R1, fragment.color.primary, R0.x;\n"
+ "MAX R0.x, R1.w, c[5].y;\n"
+ "RCP R0.x, R0.x;\n"
+ "MAD R3.xyz, -R1, R0.x, c[5].x;\n"
+ "MAX R3.xyz, R3, c[5].y;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R2.x, -R1.w, c[5];\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R0.xyz, R1.w, R0;\n"
+ "RCP R3.x, R3.x;\n"
+ "RCP R3.y, R3.y;\n"
+ "RCP R3.z, R3.z;\n"
+ "MAD R3.xyz, R0, R3, R2;\n"
+ "MAD R0.xyz, R1, R0.w, R0;\n"
+ "MAD R2.xyz, R1.w, R0.w, R2;\n"
+ "MUL R2.w, R1, R0;\n"
+ "ADD R1.x, R1.w, R0.w;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "MAD result.color.xyz, R0, R2, R3;\n"
+ "MAD result.color.w, -R1, R0, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORBURN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 9.9999997e-006 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[1], 2D;\n"
+ "ADD R1.x, -R0, c[5];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "MUL R2.xyz, R1.w, R0;\n"
+ "MAD R3.xyz, R1, R0.w, R2;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R2.xyz, -R1.w, R0.w, R3;\n"
+ "MUL R4.xyz, R1.w, R2;\n"
+ "MAX R2.xyz, R1, c[5].y;\n"
+ "MUL R5.xyz, R1, R2.w;\n"
+ "ADD R3.w, -R1, c[5].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R0, R3.w;\n"
+ "MAD R0.xyz, R0, R3.w, R2;\n"
+ "MAD R1.xyz, R1, R2.w, R4;\n"
+ "MUL R2.x, R1.w, R0.w;\n"
+ "ADD R2.w, R1, R0;\n"
+ "ADD R0.xyz, R0, -R1;\n"
+ "SGE R2.xyz, R3, R2.x;\n"
+ "MAD result.color.xyz, R2, R0, R1;\n"
+ "MAD result.color.w, -R1, R0, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_HARDLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[1], 2D;\n"
+ "ADD R0.x, -R0, c[5];\n"
+ "MUL R1, fragment.color.primary, R0.x;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "ADD R3.xyz, R1.w, -R1;\n"
+ "ADD R2.xyz, R0.w, -R0;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[5].y;\n"
+ "MAD R2.xyz, R1.w, R0.w, -R2;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R4.xyz, R1, R2.w;\n"
+ "MUL R3.xyz, R1, R0;\n"
+ "MUL R1.xyz, R1, c[5].y;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R3.xyz, R3, c[5].y, R4;\n"
+ "MAD R3.xyz, R0, R2.w, R3;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R1.w, R0.w;\n"
+ "ADD R0.xyz, R0, -R3;\n"
+ "SGE R1.xyz, R1, R1.w;\n"
+ "MAD result.color.xyz, R1, R0, R3;\n"
+ "MAD result.color.w, -R1, R0, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SOFTLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..4],\n"
+ " { 1, 2, 9.9999997e-006, 4 },\n"
+ " { 16, 12, 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R1.xyz, R0, c[3];\n"
+ "RCP R1.z, R1.z;\n"
+ "MUL R1.xy, R1, R1.z;\n"
+ "MUL R1.xy, R1, c[0];\n"
+ "TEX R1.x, R1, texture[1], 2D;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MAX R1.z, R0.w, c[5];\n"
+ "RCP R1.z, R1.z;\n"
+ "MUL R3.xyz, R0, R1.z;\n"
+ "MAD R2.xyz, R3, c[6].x, -c[6].y;\n"
+ "ADD R1.x, -R1, c[5];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MAD R4.xyz, R3, R2, c[6].z;\n"
+ "MAD R2.xyz, R1, c[5].y, -R1.w;\n"
+ "MUL R5.xyz, R0.w, R2;\n"
+ "MUL R6.xyz, R5, R4;\n"
+ "RSQ R2.w, R3.x;\n"
+ "RCP R4.x, R2.w;\n"
+ "RSQ R2.w, R3.y;\n"
+ "RSQ R3.w, R3.z;\n"
+ "RCP R4.y, R2.w;\n"
+ "RCP R4.z, R3.w;\n"
+ "ADD R4.xyz, -R3, R4;\n"
+ "MUL R6.xyz, R3, R6;\n"
+ "MUL R4.xyz, R5, R4;\n"
+ "ADD R3.xyz, -R3, c[5].x;\n"
+ "MAD R2.xyz, R2, R3, R1.w;\n"
+ "MUL R3.xyz, R1, c[5].y;\n"
+ "MAD R5.xyz, R1.w, R0, R6;\n"
+ "MAD R4.xyz, R1.w, R0, R4;\n"
+ "ADD R6.xyz, R4, -R5;\n"
+ "MUL R4.xyz, R0, c[5].w;\n"
+ "SGE R4.xyz, R4, R0.w;\n"
+ "MAD R4.xyz, R4, R6, R5;\n"
+ "MAD R4.xyz, -R0, R2, R4;\n"
+ "MUL R2.xyz, R0, R2;\n"
+ "SGE R3.xyz, R3, R1.w;\n"
+ "MAD R2.xyz, R3, R4, R2;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "ADD R1.x, R1.w, R0.w;\n"
+ "ADD R1.y, -R1.w, c[5].x;\n"
+ "MAD result.color.xyz, R0, R1.y, R2;\n"
+ "MAD result.color.w, -R1, R0, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DIFFERENCE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[1], 2D;\n"
+ "ADD R1.x, -R0, c[5];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R3.xyz, R1.w, R0;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "ADD R0.xyz, R1, R0;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R1.x, R1.w, R0.w;\n"
+ "MAD result.color.xyz, -R2, c[5].y, R0;\n"
+ "MAD result.color.w, -R1, R0, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_EXCLUSION_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[1], 2D;\n"
+ "ADD R1.x, -R0, c[5];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R0, R0.zwzw, texture[0], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R2.xyz, R1.w, R0;\n"
+ "MAD R3.xyz, R1, R0.w, R2;\n"
+ "MUL R2.xyz, R1, R0;\n"
+ "MAD R2.xyz, -R2, c[5].y, R3;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, R1.w, R0.w;\n"
+ "ADD R2.y, -R1.w, c[5].x;\n"
+ "MAD result.color.xyz, R0, R2.y, R1;\n"
+ "MAD result.color.w, -R1, R0, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODE_BLEND_MODE_MASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..6],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.z;\n"
+ "MUL R0.zw, R0, c[0].xyxy;\n"
+ "TEX R1.x, R0.zwzw, texture[1], 2D;\n"
+ "ADD R0.xy, fragment.position, c[5];\n"
+ "MUL R0.xy, R0, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "DP4 R1.y, R0, c[6];\n"
+ "ADD R1.x, -R1, c[7];\n"
+ "MUL R0, fragment.color.primary, R1.x;\n"
+ "MUL result.color, R0, R1.y;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODE_BLEND_MODE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "MUL R0.xyz, fragment.position.y, c[2];\n"
+ "MAD R0.xyz, fragment.position.x, c[1], R0;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[0];\n"
+ "TEX R0.x, R0, texture[0], 2D;\n"
+ "ADD R0.x, -R0, c[4];\n"
+ "MUL result.color, fragment.color.primary, R0.x;\n"
+ "END\n"
+ ;
+
+static const char *mask_fragment_program_sources[num_fragment_masks] = {
+ FragmentProgram_FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA,
+ FragmentProgram_FRAGMENT_PROGRAM_MASK_ELLIPSE_AA,
+};
+
+static const char *painter_fragment_program_sources[num_fragment_brushes][num_fragment_composition_modes] = {
+ {
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_MULTIPLY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SCREEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_OVERLAY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DARKEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_LIGHTEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORDODGE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORBURN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_HARDLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SOFTLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DIFFERENCE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_EXCLUSION,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_MULTIPLY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SCREEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_OVERLAY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DARKEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_LIGHTEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORDODGE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORBURN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_EXCLUSION_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODE_BLEND_MODE_MASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ },
+ {
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_MULTIPLY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SCREEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_OVERLAY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DARKEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_LIGHTEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORDODGE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORBURN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_HARDLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SOFTLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DIFFERENCE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_EXCLUSION,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_MULTIPLY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SCREEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_OVERLAY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DARKEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_LIGHTEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORDODGE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORBURN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_EXCLUSION_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODE_BLEND_MODE_MASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ },
+ {
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_MULTIPLY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SCREEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_OVERLAY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DARKEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_LIGHTEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORDODGE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORBURN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_HARDLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SOFTLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DIFFERENCE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_EXCLUSION,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_MULTIPLY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SCREEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_OVERLAY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DARKEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_LIGHTEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORDODGE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORBURN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_EXCLUSION_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODE_BLEND_MODE_MASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ },
+ {
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_MULTIPLY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SCREEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_OVERLAY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DARKEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_LIGHTEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORDODGE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORBURN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_HARDLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SOFTLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DIFFERENCE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_EXCLUSION,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_MULTIPLY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SCREEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_OVERLAY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DARKEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_LIGHTEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORDODGE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORBURN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_EXCLUSION_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODE_BLEND_MODE_MASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ },
+ {
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_MULTIPLY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SCREEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_OVERLAY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DARKEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_LIGHTEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORDODGE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORBURN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_HARDLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SOFTLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DIFFERENCE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_EXCLUSION,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_MULTIPLY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SCREEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_OVERLAY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DARKEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_LIGHTEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORDODGE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORBURN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_EXCLUSION_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODE_BLEND_MODE_MASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ },
+ {
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_MULTIPLY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SCREEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_OVERLAY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DARKEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_LIGHTEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORDODGE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORBURN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_HARDLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SOFTLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DIFFERENCE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_EXCLUSION,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_MULTIPLY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SCREEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_OVERLAY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DARKEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_LIGHTEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORDODGE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORBURN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_EXCLUSION_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODE_BLEND_MODE_MASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ },
+};
+
+static int painter_variable_locations[num_fragment_brushes][num_fragment_composition_modes][num_fragment_variables] = {
+ {
+ { -1, -1, -1, 2, -1, 0, 5, -1, 1, 3, 1, 0, -1, 4, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 0, -1, -1, 3, -1, -1, 1, 1, 0, -1, 2, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 0, -1, -1, 3, -1, -1, 1, 1, 0, -1, 2, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 0, -1, -1, 3, -1, -1, 1, 1, 0, -1, 2, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 0, -1, -1, 3, -1, -1, 1, 1, 0, -1, 2, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 0, -1, -1, 3, -1, -1, 1, 1, 0, -1, 2, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 0, -1, -1, 3, -1, -1, 1, 1, 0, -1, 2, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 0, -1, -1, 3, -1, -1, 1, 1, 0, -1, 2, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 0, -1, -1, 3, -1, -1, 1, 1, 0, -1, 2, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 0, -1, -1, 3, -1, -1, 1, 1, 0, -1, 2, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 0, -1, -1, 3, -1, -1, 1, 1, 0, -1, 2, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 0, -1, -1, 3, -1, -1, 1, 1, 0, -1, 2, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, 0, -1, -1, 1, 2, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 0, -1, -1, 2, -1, -1, -1, 0, -1, -1, 1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ },
+ {
+ { -1, -1, 3, 7, 4, 5, 10, -1, 6, 8, 1, 0, 2, 9, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, 5, 4, -1, 8, -1, -1, 6, 1, 0, 2, 7, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, 5, 4, -1, 8, -1, -1, 6, 1, 0, 2, 7, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, 5, 4, -1, 8, -1, -1, 6, 1, 0, 2, 7, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, 5, 4, -1, 8, -1, -1, 6, 1, 0, 2, 7, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, 5, 4, -1, 8, -1, -1, 6, 1, 0, 2, 7, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, 5, 4, -1, 8, -1, -1, 6, 1, 0, 2, 7, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, 5, 4, -1, 8, -1, -1, 6, 1, 0, 2, 7, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, 5, 4, -1, 8, -1, -1, 6, 1, 0, 2, 7, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, 5, 4, -1, 8, -1, -1, 6, 1, 0, 2, 7, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, 5, 4, -1, 8, -1, -1, 6, 1, 0, 2, 7, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, 5, 4, -1, 8, -1, -1, 6, 1, 0, 2, 7, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, 5, -1, -1, 6, 7, -1, 0, 1, -1, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, -1, -1, -1, -1, 5, -1, 0, 1, -1, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, -1, -1, -1, -1, 5, -1, 0, 1, -1, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, -1, -1, -1, -1, 5, -1, 0, 1, -1, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, -1, -1, -1, -1, 5, -1, 0, 1, -1, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, -1, -1, -1, -1, 5, -1, 0, 1, -1, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, -1, -1, -1, -1, 5, -1, 0, 1, -1, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, -1, -1, -1, -1, 5, -1, 0, 1, -1, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, -1, -1, -1, -1, 5, -1, 0, 1, -1, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, -1, -1, -1, -1, 5, -1, 0, 1, -1, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, -1, -1, -1, -1, 5, -1, 0, 1, -1, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, -1, -1, -1, -1, 5, -1, 0, 1, -1, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, 5, 4, -1, 7, -1, -1, -1, 0, -1, 1, 6, -1, 1, 0, 2, -1, },
+ { -1, -1, 3, -1, 4, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, 1, 0, 2, -1, },
+ },
+ {
+ { -1, -1, 2, 6, 3, 4, 9, -1, 5, 7, 1, 0, 2, 8, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, 4, -1, -1, 5, 6, -1, 0, 1, -1, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, 4, 3, -1, 6, -1, -1, -1, 0, -1, 1, 5, -1, -1, -1, 1, 0, },
+ { -1, -1, 2, -1, 3, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, 1, 0, },
+ },
+ {
+ { -1, 0, 2, 6, 3, 4, 9, -1, 5, 7, 1, 0, 2, 8, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, 2, 6, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, 4, -1, -1, 5, 6, -1, 0, 1, -1, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, 4, 3, -1, 6, -1, -1, -1, 0, -1, 1, 5, -1, -1, -1, 1, -1, },
+ { -1, 0, 2, -1, 3, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, 1, -1, },
+ },
+ {
+ { 2, -1, 2, 6, 3, 4, 9, -1, 5, 7, 1, 0, -1, 8, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, 4, -1, -1, 5, 6, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, 4, 3, -1, 6, -1, -1, -1, 0, -1, -1, 5, 0, -1, -1, 1, -1, },
+ { 0, -1, 2, -1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, 1, -1, },
+ },
+ {
+ { 2, -1, 2, 6, 3, 4, 9, -1, 5, 7, 1, 0, -1, 8, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 2, -1, 2, 4, 3, -1, 7, -1, -1, 5, 1, 0, -1, 6, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, 4, -1, -1, 5, 6, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, -1, 3, -1, -1, -1, -1, 4, -1, 0, -1, -1, 0, -1, -1, 1, -1, },
+ { 1, -1, 2, 4, 3, -1, 6, -1, -1, -1, 0, -1, -1, 5, 0, -1, -1, 1, -1, },
+ { 0, -1, 2, -1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, 1, -1, },
+ },
+};
+
+static int mask_variable_locations[num_fragment_masks][num_fragment_variables] = {
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, },
+};
+
+#endif
diff --git a/src/opengl/util/generator.cpp b/src/opengl/util/generator.cpp
new file mode 100644
index 0000000000..34ff0478c7
--- /dev/null
+++ b/src/opengl/util/generator.cpp
@@ -0,0 +1,500 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QFile>
+#include <QList>
+#include <QMap>
+#include <QPair>
+#include <QSet>
+#include <QString>
+#include <QTextStream>
+
+#include <QtDebug>
+#include <cstdlib>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+#define TAB " "
+
+typedef QPair<QString, QString> QStringPair;
+
+QString readSourceFile(const QString &sourceFile, bool fragmentProgram = false)
+{
+ QFile file(sourceFile);
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qDebug() << "Missing source file" << sourceFile;
+ exit(0);
+ }
+
+ QString source;
+
+ QTextStream in(&file);
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+
+ if (fragmentProgram && line[0] == '#' && !line.startsWith("#var"))
+ continue;
+
+ if (fragmentProgram)
+ source.append(" \"");
+
+ source.append(line);
+
+ if (fragmentProgram)
+ source.append("\\n\"");
+
+ source.append('\n');
+ }
+
+ if (fragmentProgram)
+ source.append(" ;\n");
+
+ return source;
+}
+
+QList<QStringPair> readConf(const QString &confFile)
+{
+ QFile file(confFile);
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qDebug() << "Missing file" << confFile;
+ exit(0);
+ }
+
+ QList<QStringPair> result;
+
+ QTextStream in(&file);
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+
+ if (line.startsWith('#'))
+ continue;
+
+ QTextStream lineStream(&line);
+
+ QString enumerator;
+ QString sourceFile;
+
+ lineStream >> enumerator;
+
+ if (lineStream.atEnd()) {
+ qDebug() << "Error in file" << confFile << '(' << enumerator << ')';
+ exit(0);
+ }
+
+ lineStream >> sourceFile;
+
+ result << QStringPair(enumerator, readSourceFile(sourceFile));
+ }
+
+ return result;
+}
+
+QString compileSource(const QString &source)
+{
+ {
+ QFile tempSourceFile("__tmp__.glsl");
+ if (!tempSourceFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qDebug() << "Failed opening __tmp__.glsl";
+ exit(0);
+ }
+
+ QTextStream out(&tempSourceFile);
+ out << source;
+ }
+
+ if (std::system("cgc -quiet -oglsl -profile arbfp1 __tmp__.glsl >__tmp__.frag") == -1) {
+ qDebug() << "Failed running cgc";
+ exit(0);
+ }
+
+ return readSourceFile("__tmp__.frag", true);
+}
+
+QString getWord(QString line, int word)
+{
+ QTextStream in(&line);
+
+ QString result;
+
+ for (int i = 0; i < word; ++i)
+ in >> result;
+
+ return result;
+}
+
+static int toInt(const QByteArray &str)
+{
+ int value = 0;
+
+ for (int i = 0; i < str.size(); ++i) {
+ if (str[i] < '0' || str[i] > '9')
+ break;
+
+ value *= 10;
+ value += (str[i] - '0');
+ }
+
+ return value;
+}
+QList<int> getLocations(const QSet<QString> &variables, QString source)
+{
+ QTextStream in(&source);
+
+ QMap<QString, int> locations;
+
+ foreach (QString variable, variables)
+ locations[variable] = -1;
+
+ while (!in.atEnd()) {
+ QString line = in.readLine().trimmed();
+
+ line = line.right(line.size() - 1);
+
+ if (line.startsWith("#var")) {
+ QByteArray temp;
+ QByteArray name;
+
+ QTextStream lineStream(&line);
+
+ lineStream >> temp >> temp >> name;
+
+ int location = -1;
+
+ while (!lineStream.atEnd()) {
+ lineStream >> temp;
+
+ if (temp.startsWith("c[")) {
+ location = toInt(temp.right(temp.size() - 2));
+ break;
+ }
+
+ if (temp == "texunit") {
+ lineStream >> temp;
+ location = toInt(temp);
+ break;
+ }
+ }
+
+ locations[name] = location;
+ }
+ }
+
+ QList<int> result;
+
+ foreach (QString variable, variables)
+ result << locations[variable];
+
+ return result;
+}
+
+// remove #var statements
+QString trimmed(QString source)
+{
+ QTextStream in(&source);
+
+ QString result;
+
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ if (!line.trimmed().startsWith("\"#"))
+ result += line + '\n';
+ }
+
+ return result;
+}
+
+void writeVariablesEnum(QTextStream &out, const char *name, const QSet<QString> &s)
+{
+ out << "enum " << name << " {";
+ QSet<QString>::const_iterator it = s.begin();
+ if (it != s.end()) {
+ out << "\n" TAB "VAR_" << it->toUpper();
+ for (++it; it != s.end(); ++it)
+ out << ",\n" TAB "VAR_" << it->toUpper();
+ }
+ out << "\n};\n\n";
+}
+
+void writeTypesEnum(QTextStream &out, const char *name, const QList<QStringPair> &s)
+{
+ out << "enum " << name << " {";
+ QList<QStringPair>::const_iterator it = s.begin();
+ if (it != s.end()) {
+ out << "\n" TAB << it->first;
+ for (++it; it != s.end(); ++it)
+ out << ",\n" TAB << it->first;
+ }
+ out << "\n};\n\n";
+}
+
+void writeIncludeFile(const QSet<QString> &variables,
+ const QList<QStringPair> &brushes,
+ const QList<QStringPair> &compositionModes,
+ const QList<QStringPair> &masks,
+ const QMap<QString, QMap<QString, QString> > &compiled)
+{
+ QFile includeFile("fragmentprograms_p.h");
+ if (!includeFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qDebug() << "Failed opening fragmentprograms_p.h";
+ exit(0);
+ }
+
+ QTextStream out(&includeFile);
+
+ QLatin1String tab(TAB);
+
+ out << "/****************************************************************************\n"
+ "**\n"
+ "** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).\n"
+ "** All rights reserved.\n"
+ "** Contact: Nokia Corporation (qt-info@nokia.com)\n"
+ "**\n"
+ "** This file is part of the QtOpenGL module of the Qt Toolkit.\n"
+ "**\n"
+ "** $QT_BEGIN_LICENSE:LGPL$\n"
+ "** No Commercial Usage\n"
+ "** This file contains pre-release code and may not be distributed.\n"
+ "** You may use this file in accordance with the terms and conditions\n"
+ "** contained in the Technology Preview License Agreement accompanying\n"
+ "** this package.\n"
+ "**\n"
+ "** GNU Lesser General Public License Usage\n"
+ "** Alternatively, this file may be used under the terms of the GNU Lesser\n"
+ "** General Public License version 2.1 as published by the Free Software\n"
+ "** Foundation and appearing in the file LICENSE.LGPL included in the\n"
+ "** packaging of this file. Please review the following information to\n"
+ "** ensure the GNU Lesser General Public License version 2.1 requirements\n"
+ "** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.\n"
+ "**\n"
+ "** In addition, as a special exception, Nokia gives you certain additional\n"
+ "** rights. These rights are described in the Nokia Qt LGPL Exception\n"
+ "** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.\n"
+ "**\n"
+ "** If you have questions regarding the use of this file, please contact\n"
+ "** Nokia at qt-info@nokia.com.\n"
+ "**\n"
+ "**\n"
+ "**\n"
+ "**\n"
+ "**\n"
+ "**\n"
+ "**\n"
+ "**\n"
+ "** $QT_END_LICENSE$\n"
+ "**\n"
+ "****************************************************************************/\n"
+ "\n"
+ "#ifndef FRAGMENTPROGRAMS_P_H\n"
+ "#define FRAGMENTPROGRAMS_P_H\n"
+ "\n"
+ "//\n"
+ "// W A R N I N G\n"
+ "// -------------\n"
+ "//\n"
+ "// This file is not part of the Qt API. It exists purely as an\n"
+ "// implementation detail. This header file may change from version to\n"
+ "// version without notice, or even be removed.\n"
+ "//\n"
+ "// We mean it.\n"
+ "//\n"
+ "\n";
+
+ writeVariablesEnum(out, "FragmentVariable", variables);
+ writeTypesEnum(out, "FragmentBrushType", brushes);
+ writeTypesEnum(out, "FragmentCompositionModeType", compositionModes);
+ writeTypesEnum(out, "FragmentMaskType", masks);
+
+ out << "static const unsigned int num_fragment_variables = " << variables.size() << ";\n\n";
+ out << "static const unsigned int num_fragment_brushes = " << brushes.size() << ";\n";
+ out << "static const unsigned int num_fragment_composition_modes = " << compositionModes.size() << ";\n";
+ out << "static const unsigned int num_fragment_masks = " << masks.size() << ";\n\n";
+
+ foreach (QStringPair mask, masks) {
+ const QString compiledSource = compiled[mask.first]["MASK__"];
+
+ out << "static const char *FragmentProgram_" << mask.first << " =\n"
+ << trimmed(compiledSource)
+ << '\n';
+ }
+
+ foreach (QStringPair brush, brushes) {
+ foreach (QStringPair mode, compositionModes) {
+ const QString compiledSource = compiled[brush.first][mode.first];
+
+ out << "static const char *FragmentProgram_" << brush.first << '_' << mode.first << " =\n"
+ << trimmed(compiledSource)
+ << '\n';
+ }
+ }
+
+ out << "static const char *mask_fragment_program_sources[num_fragment_masks] = {\n";
+ foreach (QStringPair mask, masks)
+ out << tab << "FragmentProgram_" << mask.first << ",\n";
+ out << "};\n\n";
+
+ out << "static const char *painter_fragment_program_sources[num_fragment_brushes][num_fragment_composition_modes] = {\n";
+ foreach (QStringPair brush, brushes) {
+ out << tab << "{\n";
+
+ foreach (QStringPair mode, compositionModes)
+ out << tab << tab << "FragmentProgram_" << brush.first << '_' << mode.first << ",\n";
+
+ out << tab << "},\n";
+ }
+ out << "};\n\n";
+
+ out << "static int painter_variable_locations[num_fragment_brushes][num_fragment_composition_modes][num_fragment_variables] = {\n";
+ foreach (QStringPair brush, brushes) {
+ out << tab << "{\n";
+
+ foreach (QStringPair mode, compositionModes) {
+ out << tab << tab << "{ ";
+
+ QList<int> locations = getLocations(variables, compiled[brush.first][mode.first]);
+
+ foreach (int location, locations)
+ out << location << ", ";
+
+ out << "},\n";
+ }
+
+ out << tab << "},\n";
+ }
+ out << "};\n\n";
+
+ out << "static int mask_variable_locations[num_fragment_masks][num_fragment_variables] = {\n";
+ foreach (QStringPair mask, masks) {
+ out << tab << "{ ";
+
+ QList<int> locations = getLocations(variables, compiled[mask.first]["MASK__"]);
+
+ foreach (int location, locations)
+ out << location << ", ";
+
+ out << "},\n";
+ }
+ out << "};\n\n";
+ out << "#endif\n";
+}
+
+QList<QString> getVariables(QString program)
+{
+ QList<QString> result;
+
+ QTextStream in(&program);
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+
+ if (line.startsWith("uniform")) {
+ QString word = getWord(line, 3);
+ result << word.left(word.size() - 1);
+ } else if (line.startsWith("#include")) {
+ QString file = getWord(line, 2);
+ result << getVariables(readSourceFile(file.mid(1, file.size() - 2)));
+ }
+ }
+
+ return result;
+}
+
+int main()
+{
+ QList<QStringPair> brushes = readConf(QLatin1String("brushes.conf"));
+ QList<QStringPair> compositionModes = readConf(QLatin1String("composition_modes.conf"));
+ QList<QStringPair> masks = readConf(QLatin1String("masks.conf"));
+
+ QString painterSource = readSourceFile("painter.glsl");
+ QString painterNoMaskSource = readSourceFile("painter_nomask.glsl");
+ QString fastPainterSource = readSourceFile("fast_painter.glsl");
+ QString brushPainterSource = readSourceFile("brush_painter.glsl");
+
+ QSet<QString> variables;
+
+ QList<QStringPair> programs[3] = { brushes, compositionModes, masks };
+
+ for (int i = 0; i < 3; ++i)
+ foreach (QStringPair value, programs[i])
+ variables += QSet<QString>::fromList(getVariables(value.second));
+
+ variables += QSet<QString>::fromList(getVariables(painterSource));
+ variables += QSet<QString>::fromList(getVariables(fastPainterSource));
+
+ QMap<QString, QMap<QString, QString> > compiled;
+
+ foreach (QStringPair brush, brushes) {
+ foreach (QStringPair mode, compositionModes) {
+ QString combinedSource = brush.second + mode.second + painterSource;
+ compiled[brush.first][mode.first] = compileSource(combinedSource);
+
+ combinedSource = brush.second + mode.second + painterNoMaskSource;
+ compiled[brush.first][mode.first + "_NOMASK"] = compileSource(combinedSource);
+ }
+
+ QString fastSource = brush.second + fastPainterSource;
+ QString brushSource = brush.second + brushPainterSource;
+
+ compiled[brush.first]["COMPOSITION_MODE_BLEND_MODE_MASK"] = compileSource(fastSource);
+ compiled[brush.first]["COMPOSITION_MODE_BLEND_MODE_NOMASK"] = compileSource(brushSource);
+ }
+
+ QList<QStringPair> temp;
+
+ foreach (QStringPair mode, compositionModes)
+ temp << QStringPair(mode.first + "_NOMASK", mode.second);
+
+ compositionModes += temp;
+
+ compositionModes << QStringPair("COMPOSITION_MODE_BLEND_MODE_MASK", "")
+ << QStringPair("COMPOSITION_MODE_BLEND_MODE_NOMASK", "");
+
+ foreach (QStringPair mask, masks)
+ compiled[mask.first]["MASK__"] = compileSource(mask.second);
+
+ writeIncludeFile(variables, brushes, compositionModes, masks, compiled);
+
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/util/generator.pro b/src/opengl/util/generator.pro
new file mode 100644
index 0000000000..ac71934ecf
--- /dev/null
+++ b/src/opengl/util/generator.pro
@@ -0,0 +1,13 @@
+######################################################################
+# Automatically generated by qmake (2.01a) Thu Oct 19 11:03:24 2006
+######################################################################
+
+TEMPLATE = app
+TARGET = generator
+DEPENDPATH += .
+INCLUDEPATH += .
+
+# Input
+SOURCES += generator.cpp
+
+CONFIG += console
diff --git a/src/opengl/util/glsl_to_include.sh b/src/opengl/util/glsl_to_include.sh
new file mode 100755
index 0000000000..06ba7ffe30
--- /dev/null
+++ b/src/opengl/util/glsl_to_include.sh
@@ -0,0 +1,73 @@
+#! /bin/sh
+#############################################################################
+##
+## Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+## All rights reserved.
+## Contact: Nokia Corporation (qt-info@nokia.com)
+##
+## This file is the build configuration utility of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## No Commercial Usage
+## This file contains pre-release code and may not be distributed.
+## You may use this file in accordance with the terms and conditions
+## contained in the Technology Preview License Agreement accompanying
+## this package.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, 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.
+##
+## If you have questions regarding the use of this file, please contact
+## Nokia at qt-info@nokia.com.
+##
+##
+##
+##
+##
+##
+##
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+# Compile a .glsl file to a file that can be included in a C++ program
+USAGE="Usage: $0 <file.glsl>"
+CGC=cgc
+CGC_PROFILE=arbfp1
+
+if test $# -ne 1
+then
+ echo $USAGE
+ exit 1
+fi
+
+GLSL_FILE=$1
+FRAG_FILE=`basename $1 .glsl`.frag
+#GLSL_INC_FILE=`basename $1 .glsl`.glsl_quoted
+
+echo "// Generated by src/opengl/util/$0 from $1" > $FRAG_FILE
+$CGC -quiet -oglsl -profile $CGC_PROFILE $GLSL_FILE | while read line
+do
+ if test `echo $line | cut -c1` != "#"
+ then
+ echo -e \"$line\" >> $FRAG_FILE
+ fi
+done
+echo "; // Generated by src/opengl/util/$0 from $1" >> $FRAG_FILE
+
+#echo "// Generated by src/opengl/util/$0 from $1" > $GLSL_INC_FILE
+#cat $GLSL_FILE | while read line
+#do
+# printf \"%s\\\\n\"\\n "$line" >> $GLSL_INC_FILE
+#done
+#echo "; // Generated by src/opengl/util/$0 from $1" >> $GLSL_INC_FILE
diff --git a/src/opengl/util/linear_brush.glsl b/src/opengl/util/linear_brush.glsl
new file mode 100644
index 0000000000..90a4440a99
--- /dev/null
+++ b/src/opengl/util/linear_brush.glsl
@@ -0,0 +1,22 @@
+uniform sampler1D palette;
+uniform vec3 linear;
+uniform vec3 inv_matrix_m0;
+uniform vec3 inv_matrix_m1;
+uniform vec3 inv_matrix_m2;
+
+vec4 brush()
+{
+ mat3 mat;
+
+ mat[0] = inv_matrix_m0;
+ mat[1] = inv_matrix_m1;
+ mat[2] = inv_matrix_m2;
+
+ vec3 hcoords = mat * vec3(gl_FragCoord.xy, 1);
+ vec2 A = hcoords.xy / hcoords.z;
+
+ float val = dot(linear.xy, A) * linear.z;
+
+ return texture1D(palette, val);
+}
+
diff --git a/src/opengl/util/masks.conf b/src/opengl/util/masks.conf
new file mode 100644
index 0000000000..d853d0b6e9
--- /dev/null
+++ b/src/opengl/util/masks.conf
@@ -0,0 +1,2 @@
+FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA trap_exact_aa.glsl
+FRAGMENT_PROGRAM_MASK_ELLIPSE_AA ellipse_aa.glsl
diff --git a/src/opengl/util/meego/main.cpp b/src/opengl/util/meego/main.cpp
new file mode 100644
index 0000000000..65d6e57c67
--- /dev/null
+++ b/src/opengl/util/meego/main.cpp
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** 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 QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qdebug.h>
+
+#define QT_DEBUG_SHADER_CACHE
+#define QT_MEEGO_EXPERIMENTAL_SHADERCACHE
+#define QT_OPENGL_ES_2
+#define QT_BOOTSTRAPPED
+
+typedef int GLsizei;
+typedef unsigned int GLenum;
+
+#include "../../gl2paintengineex/qglshadercache_meego_p.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int main()
+{
+ ShaderCacheSharedMemory shm;
+
+ if (!shm.isAttached()) {
+ fprintf(stderr, "Unable to attach to shared memory\n");
+ return EXIT_FAILURE;
+ }
+
+ ShaderCacheLocker locker(&shm);
+ if (!locker.isLocked()) {
+ fprintf(stderr, "Unable to lock shared memory\n");
+ return EXIT_FAILURE;
+ }
+
+ void *data = shm.data();
+ Q_ASSERT(data);
+
+ CachedShaders *cache = reinterpret_cast<CachedShaders *>(data);
+
+ for (int i = 0; i < cache->shaderCount; ++i) {
+ printf("Shader %d: %d bytes\n", i, cache->headers[i].size);
+ }
+
+ printf("\nSummary:\n\n"
+ " Amount of cached shaders: %d\n"
+ " Bytes used: %d\n"
+ " Bytes available: %d\n",
+ cache->shaderCount, cache->dataSize, cache->availableSize());
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/src/opengl/util/meego/shader-cache-introspector.pro b/src/opengl/util/meego/shader-cache-introspector.pro
new file mode 100644
index 0000000000..520e9a5108
--- /dev/null
+++ b/src/opengl/util/meego/shader-cache-introspector.pro
@@ -0,0 +1,7 @@
+TEMPLATE = app
+
+SOURCES += main.cpp
+
+TARGET = shader-cache-introspector
+
+QT = core
diff --git a/src/opengl/util/painter.glsl b/src/opengl/util/painter.glsl
new file mode 100644
index 0000000000..b990234778
--- /dev/null
+++ b/src/opengl/util/painter.glsl
@@ -0,0 +1,21 @@
+uniform sampler2D dst_texture;
+uniform sampler2D mask_texture;
+uniform vec2 inv_mask_size;
+uniform vec2 inv_dst_size;
+uniform vec2 mask_offset;
+uniform vec4 mask_channel;
+
+float mask()
+{
+ return dot(mask_channel, texture2D(mask_texture, (gl_FragCoord.xy + mask_offset) * inv_mask_size));
+}
+
+void main()
+{
+ vec4 dst = texture2D(dst_texture, gl_FragCoord.xy * inv_dst_size);
+
+ // combine clip and coverage channels
+ float mask_alpha = mask();
+
+ gl_FragColor = mix(dst, composite(brush(), dst), mask_alpha);
+}
diff --git a/src/opengl/util/painter_nomask.glsl b/src/opengl/util/painter_nomask.glsl
new file mode 100644
index 0000000000..af5ad6505f
--- /dev/null
+++ b/src/opengl/util/painter_nomask.glsl
@@ -0,0 +1,9 @@
+uniform sampler2D dst_texture;
+uniform vec2 inv_dst_size;
+
+void main()
+{
+ vec4 dst = texture2D(dst_texture, gl_FragCoord.xy * inv_dst_size);
+
+ gl_FragColor = composite(brush(), dst);
+}
diff --git a/src/opengl/util/pattern_brush.glsl b/src/opengl/util/pattern_brush.glsl
new file mode 100644
index 0000000000..31702b887c
--- /dev/null
+++ b/src/opengl/util/pattern_brush.glsl
@@ -0,0 +1,23 @@
+uniform sampler2D brush_texture;
+uniform vec2 inv_brush_texture_size;
+uniform vec3 inv_matrix_m0;
+uniform vec3 inv_matrix_m1;
+uniform vec3 inv_matrix_m2;
+
+vec4 brush()
+{
+ mat3 mat;
+
+ mat[0] = inv_matrix_m0;
+ mat[1] = inv_matrix_m1;
+ mat[2] = inv_matrix_m2;
+
+ vec3 hcoords = mat * vec3(gl_FragCoord.xy, 1);
+ vec2 coords = hcoords.xy / hcoords.z;
+
+ coords *= inv_brush_texture_size;
+
+ float alpha = 1.0 - texture2D(brush_texture, coords).r;
+
+ return gl_Color * alpha;
+}
diff --git a/src/opengl/util/radial_brush.glsl b/src/opengl/util/radial_brush.glsl
new file mode 100644
index 0000000000..84bec62e65
--- /dev/null
+++ b/src/opengl/util/radial_brush.glsl
@@ -0,0 +1,28 @@
+uniform sampler1D palette;
+uniform vec2 fmp;
+uniform float fmp2_m_radius2;
+uniform vec3 inv_matrix_m0;
+uniform vec3 inv_matrix_m1;
+uniform vec3 inv_matrix_m2;
+
+vec4 brush()
+{
+ mat3 mat;
+
+ mat[0] = inv_matrix_m0;
+ mat[1] = inv_matrix_m1;
+ mat[2] = inv_matrix_m2;
+
+ vec3 hcoords = mat * vec3(gl_FragCoord.xy, 1);
+ vec2 A = hcoords.xy / hcoords.z;
+ vec2 B = fmp;
+
+ float a = fmp2_m_radius2;
+ float b = 2.0*dot(A, B);
+ float c = -dot(A, A);
+
+ float val = (-b + sqrt(b*b - 4.0*a*c)) / (2.0*a);
+
+ return texture1D(palette, val);
+}
+
diff --git a/src/opengl/util/simple_porter_duff.glsl b/src/opengl/util/simple_porter_duff.glsl
new file mode 100644
index 0000000000..4cb0599ac5
--- /dev/null
+++ b/src/opengl/util/simple_porter_duff.glsl
@@ -0,0 +1,16 @@
+uniform vec2 porterduff_ab;
+uniform vec3 porterduff_xyz;
+
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+
+ result.xyz = porterduff_ab.x * src.xyz * dst.a
+ + porterduff_ab.y * dst.xyz * src.a
+ + porterduff_xyz.y * src.xyz * (1.0 - dst.a)
+ + porterduff_xyz.z * dst.xyz * (1.0 - src.a);
+
+ result.a = dot(porterduff_xyz, vec3(src.a * dst.a, src.a * (1.0 - dst.a), dst.a * (1.0 - src.a)));
+
+ return result;
+}
diff --git a/src/opengl/util/solid_brush.glsl b/src/opengl/util/solid_brush.glsl
new file mode 100644
index 0000000000..760afd1a72
--- /dev/null
+++ b/src/opengl/util/solid_brush.glsl
@@ -0,0 +1,4 @@
+vec4 brush()
+{
+ return gl_Color;
+}
diff --git a/src/opengl/util/texture_brush.glsl b/src/opengl/util/texture_brush.glsl
new file mode 100644
index 0000000000..949825583f
--- /dev/null
+++ b/src/opengl/util/texture_brush.glsl
@@ -0,0 +1,21 @@
+uniform sampler2D brush_texture;
+uniform vec2 inv_brush_texture_size;
+uniform vec3 inv_matrix_m0;
+uniform vec3 inv_matrix_m1;
+uniform vec3 inv_matrix_m2;
+
+vec4 brush()
+{
+ mat3 mat;
+
+ mat[0] = inv_matrix_m0;
+ mat[1] = inv_matrix_m1;
+ mat[2] = inv_matrix_m2;
+
+ vec3 hcoords = mat * vec3(gl_FragCoord.xy, 1);
+ vec2 coords = hcoords.xy / hcoords.z;
+
+ coords *= inv_brush_texture_size;
+
+ return texture2D(brush_texture, coords);
+}
diff --git a/src/opengl/util/trap_exact_aa.glsl b/src/opengl/util/trap_exact_aa.glsl
new file mode 100644
index 0000000000..1637f430b5
--- /dev/null
+++ b/src/opengl/util/trap_exact_aa.glsl
@@ -0,0 +1,58 @@
+float quad_aa()
+{
+ float top = min(gl_FragCoord.y + 0.5, gl_TexCoord[0].x);
+ float bottom = max(gl_FragCoord.y - 0.5, gl_TexCoord[0].y);
+
+ float area = top - bottom;
+
+ float left = gl_FragCoord.x - 0.5;
+ float right = gl_FragCoord.x + 0.5;
+
+ // use line equations to compute intersections of left/right edges with top/bottom of truncated pixel
+ vec4 vecX = gl_TexCoord[1].xxzz * vec2(top, bottom).xyxy + gl_TexCoord[1].yyww;
+
+ vec2 invA = gl_TexCoord[0].zw;
+
+ // transform right line to left to be able to use same calculations for both
+ vecX.zw = 2.0 * gl_FragCoord.x - vecX.zw;
+
+ vec2 topX = vec2(vecX.x, vecX.z);
+ vec2 bottomX = vec2(vecX.y, vecX.w);
+
+ // transform lines such that top intersection is to the right of bottom intersection
+ vec2 topXTemp = max(topX, bottomX);
+ vec2 bottomXTemp = min(topX, bottomX);
+
+ // make sure line slope reflects mirrored lines
+ invA = mix(invA, -invA, step(topX, bottomX));
+
+ vec2 vecLeftRight = vec2(left, right);
+
+ // compute the intersections of the lines with the left and right edges of the pixel
+ vec4 intersectY = bottom + (vecLeftRight.xyxy - bottomXTemp.xxyy) * invA.xxyy;
+
+ vec2 temp = mix(area - 0.5 * (right - bottomXTemp) * (intersectY.yw - bottom), // left < bottom < right < top
+ (0.5 * (topXTemp + bottomXTemp) - left) * area, // left < bottom < top < right
+ step(topXTemp, right.xx));
+
+ vec2 excluded = 0.5 * (top - intersectY.xz) * (topXTemp - left); // bottom < left < top < right
+
+ excluded = mix((top - 0.5 * (intersectY.yw + intersectY.xz)) * (right - left), // bottom < left < right < top
+ excluded, step(topXTemp, right.xx));
+
+ excluded = mix(temp, // left < bottom < right (see calculation of temp)
+ excluded, step(bottomXTemp, left.xx));
+
+ excluded = mix(vec2(area, area), // right < bottom < top
+ excluded, step(bottomXTemp, right.xx));
+
+ excluded *= step(left, topXTemp);
+
+ return (area - excluded.x - excluded.y) * step(bottom, top);
+}
+
+void main()
+{
+ gl_FragColor = quad_aa().xxxx;
+}
+