summaryrefslogtreecommitdiffstats
path: root/src/opengl/gl2paintengineex
diff options
context:
space:
mode:
Diffstat (limited to 'src/opengl/gl2paintengineex')
-rw-r--r--src/opengl/gl2paintengineex/glgc_shader_source.h289
-rw-r--r--src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp156
-rw-r--r--src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h121
-rw-r--r--src/opengl/gl2paintengineex/qglgradientcache.cpp185
-rw-r--r--src/opengl/gl2paintengineex/qglgradientcache_p.h108
-rw-r--r--src/opengl/gl2paintengineex/qglpexshadermanager.cpp450
-rw-r--r--src/opengl/gl2paintengineex/qglpexshadermanager_p.h156
-rw-r--r--src/opengl/gl2paintengineex/qglshader.cpp605
-rw-r--r--src/opengl/gl2paintengineex/qglshader_p.h260
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp1278
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h125
11 files changed, 3733 insertions, 0 deletions
diff --git a/src/opengl/gl2paintengineex/glgc_shader_source.h b/src/opengl/gl2paintengineex/glgc_shader_source.h
new file mode 100644
index 0000000000..5b9d28b4cd
--- /dev/null
+++ b/src/opengl/gl2paintengineex/glgc_shader_source.h
@@ -0,0 +1,289 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GLGC_SHADER_SOURCE_H
+#define GLGC_SHADER_SOURCE_H
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+static const char* qglslImageVertexShader = "\
+ attribute highp vec4 inputVertex; \
+ attribute lowp vec2 textureCoord; \
+ uniform highp mat4 pmvMatrix; \
+ varying lowp vec2 fragTextureCoord; \
+ void main(void) \
+ {\
+ gl_Position = pmvMatrix * inputVertex;\
+ fragTextureCoord = textureCoord; \
+ }";
+
+static const char* qglslImageFragmentShader = "\
+ varying lowp vec2 fragTextureCoord;\
+ uniform sampler2D textureSampler;\
+ uniform lowp float opacity; \
+ void main(void) \
+ {\
+ gl_FragColor = texture2D(textureSampler, fragTextureCoord) * opacity; \
+ }";
+
+static const char* qglslTextFragmentShader = "\
+ varying lowp vec2 fragTextureCoord;\
+ uniform mediump vec4 fragmentColor;\
+ uniform sampler2D textureSampler;\
+ void main(void) \
+ {\
+ highp vec4 tex = texture2D(textureSampler, fragTextureCoord); \
+ tex = fragmentColor * tex.r; \
+ gl_FragColor = tex; \
+ }";
+
+static const char* qglslDefaultVertexShader = "\
+ attribute highp vec4 inputVertex;\
+ uniform highp mat4 pmvMatrix;\
+ void main(void)\
+ {\
+ gl_Position = pmvMatrix * inputVertex;\
+ }";
+
+static const char* qglslSimpleFragmentShader = "\
+ void main (void)\
+ {\
+ gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\
+ }";
+
+
+/**** FRAGMENT SHADER MAIN FUNCTIONS ****/
+// NOTE: Currently, the engine assumes brushes return colors already in pre-multiplied
+// format. However, this may change if we add support for non-premultiplied
+
+static const char* qglslNoOpacityFragmentShaderMain = "\n\
+ mediump vec4 brush();\
+ void main (void)\
+ {\
+ gl_FragColor = brush();\
+ }\n";
+
+static const char* qglslFragmentShaderMain = "\n\
+ mediump vec4 brush();\
+ uniform lowp float opacity; \
+ void main (void)\
+ {\
+ gl_FragColor = brush() * opacity;\
+ }\n";
+
+
+
+/**** BRUSH SHADERS ****/
+
+// This should never actually be used
+static const char* qglslNoBrushFragmentShader = "\n\
+ mediump vec4 brush() { \
+ discard; \
+ return vec4(1.0, 0.8, 0.8, 1.0);\
+ }\n";
+
+// Solid Fill Brush
+static const char* qglslSolidBrushFragmentShader = "\n\
+ uniform mediump vec4 fragmentColor; \
+ mediump vec4 brush() { \
+ return fragmentColor;\
+ }\n";
+
+// Texture Brush
+static const char* qglslTextureBrushVertexShader = "\
+ attribute highp vec4 inputVertex; \
+ uniform highp mat4 pmvMatrix; \
+ uniform mediump vec2 halfViewportSize; \
+ uniform mediump vec2 invertedTextureSize; \
+ uniform mediump mat3 brushTransform; \
+ varying mediump vec2 texCoords; \
+ void main(void) { \
+ gl_Position = pmvMatrix * inputVertex;\
+ gl_Position.xy = gl_Position.xy / gl_Position.w; \
+ mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \
+ mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \
+ mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \
+ gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \
+ gl_Position.w = invertedHTexCoordsZ; \
+ texCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \
+ texCoords.y = -texCoords.y; \
+ }";
+
+static const char* qglslTextureBrushFragmentShader = "\n\
+ varying mediump vec2 texCoords;\
+ uniform sampler2D brushTexture;\
+ mediump vec4 brush() { \
+ return texture2D(brushTexture, texCoords); \
+ }\n";
+
+
+// Pattern Brush - This assumes the texture size is 8x8 and thus, the inverted size is 0.125
+static const char* qglslPatternBrushVertexShader = "\
+ attribute highp vec4 inputVertex; \
+ uniform highp mat4 pmvMatrix; \
+ uniform mediump vec2 halfViewportSize; \
+ uniform mediump vec2 invertedTextureSize; \
+ uniform mediump mat3 brushTransform; \
+ varying mediump vec2 texCoords; \
+ void main(void) { \
+ gl_Position = pmvMatrix * inputVertex;\
+ gl_Position.xy = gl_Position.xy / gl_Position.w; \
+ mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \
+ mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \
+ mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \
+ gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \
+ gl_Position.w = invertedHTexCoordsZ; \
+ texCoords.xy = (hTexCoords.xy * 0.125) * invertedHTexCoordsZ; \
+ texCoords.y = -texCoords.y; \
+ }";
+
+static const char* qglslPatternBrushFragmentShader = "\n\
+ uniform sampler2D brushTexture;\
+ uniform lowp vec4 patternColor; \
+ varying mediump vec2 texCoords;\
+ mediump vec4 brush() { \
+ return patternColor * texture2D(brushTexture, texCoords).r; \
+ }\n";
+
+
+// Linear Gradient Brush
+static const char* qglslLinearGradientBrushVertexShader = "\
+ attribute highp vec4 inputVertex; \
+ uniform highp mat4 pmvMatrix; \
+ uniform mediump vec2 halfViewportSize; \
+ uniform highp vec3 linearData; \
+ uniform mediump mat3 brushTransform; \
+ varying mediump float index ; \
+ void main() { \
+ gl_Position = pmvMatrix * inputVertex;\
+ gl_Position.xy = gl_Position.xy / gl_Position.w; \
+ mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \
+ mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \
+ mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \
+ gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \
+ gl_Position.w = invertedHTexCoordsZ; \
+ index = (dot(linearData.xy, hTexCoords.xy) * linearData.z) * invertedHTexCoordsZ; \
+ }";
+
+static const char* qglslLinearGradientBrushFragmentShader = "\n\
+ uniform sampler2D brushTexture; \
+ varying mediump float index; \
+ mediump vec4 brush() { \
+ mediump vec2 val = vec2(index, 0.5); \
+ return texture2D(brushTexture, val); \
+ }\n";
+
+
+static const char* qglslRadialGradientBrushVertexShader = "\
+ attribute highp vec4 inputVertex;\
+ uniform highp mat4 pmvMatrix;\
+ uniform mediump vec2 halfViewportSize; \
+ uniform highp mat3 brushTransform; \
+ uniform highp vec2 fmp; \
+ varying highp float b; \
+ varying highp vec2 A; \
+ void main(void) \
+ {\
+ gl_Position = pmvMatrix * inputVertex;\
+ gl_Position.xy = gl_Position.xy / gl_Position.w; \
+ mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \
+ mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \
+ mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \
+ gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \
+ gl_Position.w = invertedHTexCoordsZ; \
+ A = hTexCoords.xy * invertedHTexCoordsZ; \
+ b = 2.0 * fmp * (A.x + A.y); \
+\
+ }";
+
+static const char* qglslRadialGradientBrushFragmentShader = "\n\
+ uniform sampler2D brushTexture; \
+ uniform highp float fmp2_m_radius2; \
+ uniform highp float inverse_2_fmp2_m_radius2; \
+ varying highp float b; \
+ varying highp vec2 A; \
+\
+ mediump vec4 brush() { \
+ highp float c = -dot(A, A); \
+ highp vec2 val = vec2((-b + sqrt(b*b - 4.0*fmp2_m_radius2*c)) * inverse_2_fmp2_m_radius2, 0.5); \
+ return texture2D(brushTexture, val); \
+ }\n";
+
+static const char* qglslConicalGradientBrushVertexShader = "\
+ attribute highp vec4 inputVertex;\
+ uniform highp mat4 pmvMatrix;\
+ uniform mediump vec2 halfViewportSize; \
+ uniform highp mat3 brushTransform; \
+ varying highp vec2 A; \
+ void main(void)\
+ {\
+ gl_Position = pmvMatrix * inputVertex;\
+ gl_Position.xy = gl_Position.xy / gl_Position.w; \
+ mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \
+ mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \
+ mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \
+ gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \
+ gl_Position.w = invertedHTexCoordsZ; \
+ A = hTexCoords.xy * invertedHTexCoordsZ; \
+ }";
+
+static const char* qglslConicalGradientBrushFragmentShader = "\n\
+ #define INVERSE_2PI 0.1591549430918953358 \n\
+ uniform sampler2D brushTexture; \
+ uniform mediump float angle; \
+ varying highp vec2 A; \
+ mediump vec4 brush() { \
+ if (abs(A.y) == abs(A.x)) \
+ A.y += 0.002; \
+ highp float t = (atan2(-A.y, A.x) + angle) * INVERSE_2PI; \
+ return texture2D(brushTexture, vec2(t - floor(t), 0.5)); \
+ }\n";
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // GLGC_SHADER_SOURCE_H
diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp
new file mode 100644
index 0000000000..0352d398ea
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgl2pexvertexarray_p.h"
+
+#include <private/qbezier_p.h>
+
+void QGL2PEXVertexArray::clear()
+{
+ vertexArray.reset();
+ vertexArrayStops.clear();
+ 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::addPath(const QVectorPath &path, GLfloat curveInverseScale)
+{
+ 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;
+ }
+
+ 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) {
+ const QPainterPath::ElementType elementType = elements[i];
+ switch (elementType) {
+ case QPainterPath::MoveToElement:
+// qDebug("element[%d] is a MoveToElement", i);
+ vertexArrayStops.append(vertexArray.size());
+ vertexArray.add(points[i]); // 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:
+// qDebug("element[%d] is a CurveToElement", i);
+ curveToArray(points[i], points[i+1], points[i+2], curveInverseScale);
+ i+=2;
+ break;
+ default:
+ break;
+ }
+ }
+ } while (0);
+
+ vertexArrayStops.append(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;
+}
+
+void QGL2PEXVertexArray::curveToArray(const QGLPoint &cp1, const QGLPoint &cp2, const QGLPoint &ep, GLfloat inverseScale)
+{
+ qreal inverseScaleHalf = inverseScale / 2;
+
+ QBezier beziers[32];
+ beziers[0] = QBezier::fromPoints(vertexArray.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
+ lineToArray(b->x4, b->y4);
+ --b;
+ } else {
+ // split, second half of the polygon goes lower into the stack
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+}
diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
new file mode 100644
index 0000000000..c205022a91
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 <QRectF>
+
+#include <private/qdatabuffer_p.h>
+#include <private/qvectorpath_p.h>
+#include <private/qgl_p.h>
+
+class QGLPoint
+{
+public:
+ QGLPoint(GLfloat new_x, GLfloat new_y) :
+ x(new_x), y(new_y) {};
+
+ QGLPoint(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(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() {return QRectF(left, top, right-left, bottom-top);}
+};
+
+class QGL2PEXVertexArray
+{
+public:
+ QGL2PEXVertexArray() :
+ maxX(-2e10), maxY(-2e10), minX(2e10), minY(2e10),
+ boundingRectDirty(true) {}
+
+ void addPath(const QVectorPath &path, GLfloat curveInverseScale);
+ void clear();
+
+ QGLPoint* data() {return vertexArray.data();}
+ QVector<int>& stops() {return vertexArrayStops;}
+ QGLRect boundingRect() const;
+
+ void lineToArray(const GLfloat x, const GLfloat y);
+
+private:
+ QDataBuffer<QGLPoint> vertexArray;
+ QVector<int> vertexArrayStops;
+
+ GLfloat maxX;
+ GLfloat maxY;
+ GLfloat minX;
+ GLfloat minY;
+ bool boundingRectDirty;
+
+ inline void curveToArray(const QGLPoint &cp1, const QGLPoint &cp2, const QGLPoint &ep, GLfloat inverseScale);
+};
diff --git a/src/opengl/gl2paintengineex/qglgradientcache.cpp b/src/opengl/gl2paintengineex/qglgradientcache.cpp
new file mode 100644
index 0000000000..b4591b27fe
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglgradientcache.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_p.h>
+#include <private/qgl_p.h>
+
+#include "qglgradientcache_p.h"
+
+void QGLGradientCache::cleanCache() {
+ 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 QGLGradientCache::getBuffer(const QGradient &gradient, qreal opacity, const QGLContext *ctx)
+{
+ if (buffer_ctx && !qgl_share_reg()->checkSharing(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);
+ }
+}
+
+
+GLuint QGLGradientCache::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, bin-endian (ABGR on x86).
+// Qt stores in ARGB using whatever 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
+ o |= (c >> 16) & 0x000000FF; // red
+ o |= (c << 16) & 0x00FF0000; // blue
+
+#else //Q_BIG_ENDIAN
+ o = c & 0x00FF00FF; // alpha & green already in the right place
+ o |= (c << 16) & 0xFF000000; // red
+ o |= (c >> 16) & 0x0000FF00; // blue
+#error big endian not tested with QGLGraphicsContext
+#endif // Q_BYTE_ORDER
+ return o;
+}
+
+//TODO: Let GL generate the texture using an FBO
+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(); // Qt LIES! It returns ARGB
+
+ 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;
+}
diff --git a/src/opengl/gl2paintengineex/qglgradientcache_p.h b/src/opengl/gl2paintengineex/qglgradientcache_p.h
new file mode 100644
index 0000000000..346ea13f89
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglgradientcache_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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>
+
+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 *)));
+*/
+ }
+
+ GLuint getBuffer(const QGradient &gradient, qreal opacity, const QGLContext *ctx);
+ inline int paletteSize() const { return 1024; }
+
+protected:
+ 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;
+ const QGLContext *buffer_ctx;
+
+public slots:
+ void cleanupGLContextRefs(const QGLContext *context) {
+ if (context == buffer_ctx) {
+ cleanCache();
+ buffer_ctx = 0;
+ }
+ }
+};
+
+
+
diff --git a/src/opengl/gl2paintengineex/qglpexshadermanager.cpp b/src/opengl/gl2paintengineex/qglpexshadermanager.cpp
new file mode 100644
index 0000000000..e460e08662
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglpexshadermanager.cpp
@@ -0,0 +1,450 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglpexshadermanager_p.h"
+
+#include "glgc_shader_source.h"
+
+QGLPEXShaderManager::QGLPEXShaderManager(const QGLContext* context)
+{
+ ctx = const_cast<QGLContext*>(context);
+
+ defaultVertexShader= new QGLShader(QGLShader::VertexShader, context);
+ defaultVertexShader->addSource(QLatin1String(qglslDefaultVertexShader));
+ if (!defaultVertexShader->compile())
+ qWarning() << "Default vertex shader failed to compile: " << defaultVertexShader->log();
+
+ noBrushShader = new QGLShader(QGLShader::FragmentShader, context);
+ noBrushShader->addSource(QLatin1String(qglslFragmentShaderMain));
+ noBrushShader->addSource(QLatin1String(qglslNoBrushFragmentShader));
+ if (!noBrushShader->compile())
+ qWarning() << "No brush shader failed to compile:" << noBrushShader->log();
+
+
+ // Create a program for noBrush:
+ QGLShaderProgram* noBrushProg = new QGLShaderProgram(ctx);
+ noBrushProg->addShader(defaultVertexShader);
+ noBrushProg->addShader(noBrushShader);
+ if (!noBrushProg->link())
+ qWarning() << "NoBrush shader program failed to link:" << noBrushProg->log();
+
+ // Add noBrush Program to cache:
+ QGLCachedShaderProg cachedProg;
+ cachedProg.vertexShader = defaultVertexShader;
+ cachedProg.brushShader = noBrushShader;
+ cachedProg.compositionShader = 0;
+ cachedProg.shader = noBrushProg;
+ cachedPrograms.append(cachedProg);
+
+
+ // Set state
+ useGlobalOpacity = true;
+ currentBrushStyle = Qt::NoBrush;
+ currentTransformType = FullTransform;
+ shaderProgNeedsChanging = false;
+ activeProgram = noBrushProg;
+
+ solidBrushShader = 0;
+
+ conicalBrushVertexShader = 0;
+ conicalBrushFragmentShader = 0;
+
+ radialBrushVertexShader = 0;
+ radialBrushFragmentShader = 0;
+
+ linearBrushVertexShader = 0;
+ linearBrushFragmentShader = 0;
+
+ patternBrushVertexShader = 0;
+ patternBrushFragmentShader = 0;
+
+ textureBrushFragmentShader = 0;
+ textureBrushVertexShader = 0;
+
+ simpleFragmentShader = 0;
+ simpleShaderProgram = 0;
+
+ imageVertexShader = 0;
+ imageFragmentShader = 0;
+ imageShaderProgram = 0;
+
+ textVertexShader = 0;
+ textFragmentShader = 0;
+ textShaderProgram = 0;
+}
+
+QGLPEXShaderManager::~QGLPEXShaderManager()
+{
+ delete defaultVertexShader;
+ delete imageVertexShader;
+ delete imageFragmentShader;
+ delete imageShaderProgram;
+ delete textVertexShader;
+ delete textFragmentShader;
+ delete textShaderProgram;
+ delete noBrushShader;
+ delete solidBrushShader;
+
+ delete conicalBrushVertexShader;
+ delete conicalBrushFragmentShader;
+
+ delete radialBrushVertexShader;
+ delete radialBrushFragmentShader;
+
+ delete linearBrushFragmentShader;
+ delete linearBrushVertexShader;
+
+ delete patternBrushFragmentShader;
+ delete patternBrushVertexShader;
+
+ delete textureBrushFragmentShader;
+ delete textureBrushVertexShader;
+
+ delete simpleFragmentShader;
+ delete simpleShaderProgram;
+}
+
+void QGLPEXShaderManager::setUseGlobalOpacity(bool value)
+{
+ if (value != useGlobalOpacity)
+ shaderProgNeedsChanging = true;
+
+ useGlobalOpacity = value;
+}
+
+void QGLPEXShaderManager::setBrushStyle(Qt::BrushStyle style)
+{
+ if (currentBrushStyle != style)
+ shaderProgNeedsChanging = true;
+
+ currentBrushStyle = style;
+}
+
+void QGLPEXShaderManager::setAffineOnlyBrushTransform(bool value)
+{
+ Q_UNUSED(value);
+ // TODO
+}
+
+bool QGLPEXShaderManager::useCorrectShaderProg()
+{
+ if (!shaderProgNeedsChanging) {
+ activeProgram->use();
+ return false;
+ }
+
+ const char* fragmentShaderMainSrc = qglslFragmentShaderMain;
+ QGLShader* vertexShader = defaultVertexShader;
+ QGLShader* fragmentShader = noBrushShader;
+
+ // Make sure we compile up the correct brush shader
+ switch (currentBrushStyle) {
+ case Qt::NoBrush:
+ break;
+ case Qt::SolidPattern:
+ if (!solidBrushShader) {
+ qDebug("Compiling qglslSolidBrushFragmentShader");
+ solidBrushShader = new QGLShader(QGLShader::FragmentShader, ctx);
+ solidBrushShader->addSource(QLatin1String(qglslNoOpacityFragmentShaderMain));
+ solidBrushShader->addSource(QLatin1String(qglslSolidBrushFragmentShader));
+ if (!solidBrushShader->compile())
+ qWarning() << "qglslSolidBrush failed to compile:" << solidBrushShader->log();
+ }
+ fragmentShader = solidBrushShader;
+ break;
+ case Qt::TexturePattern:
+ if (!textureBrushVertexShader) {
+ qDebug("Compiling qglslTextureBrushVertexShader");
+ textureBrushVertexShader = new QGLShader(QGLShader::VertexShader, ctx);
+ textureBrushVertexShader->addSource(QLatin1String(qglslTextureBrushVertexShader));
+ if (!textureBrushVertexShader->compile()) {
+ qWarning() << "qglslTextureBrushVertexShader failed to compile: "
+ << textureBrushVertexShader->log();
+ }
+ }
+ vertexShader = textureBrushVertexShader;
+
+ if (!textureBrushFragmentShader) {
+ qDebug("Compiling qglslTextureBrushFragmentShader");
+ textureBrushFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx);
+ textureBrushFragmentShader->addSource(QLatin1String(fragmentShaderMainSrc));
+ textureBrushFragmentShader->addSource(QLatin1String(qglslTextureBrushFragmentShader));
+ if (!textureBrushFragmentShader->compile()) {
+ qWarning() << "qglslTextureBrushFragmentShader failed to compile:"
+ << textureBrushFragmentShader->log();
+ }
+ }
+ fragmentShader = textureBrushFragmentShader;
+ break;
+ case Qt::LinearGradientPattern:
+ if (!linearBrushVertexShader) {
+ qDebug("Compiling qglslLinearGradientBrushVertexShader");
+ linearBrushVertexShader = new QGLShader(QGLShader::VertexShader, ctx);
+ linearBrushVertexShader->addSource(QLatin1String(qglslLinearGradientBrushVertexShader));
+ if (!linearBrushVertexShader->compile()) {
+ qWarning() << "qglslLinearGradientBrushVertexShader failed to compile: "
+ << linearBrushVertexShader->log();
+ }
+ }
+ vertexShader = linearBrushVertexShader;
+
+ if (!linearBrushFragmentShader) {
+ qDebug("Compiling qglslLinearGradientBrushFragmentShader");
+ linearBrushFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx);
+ linearBrushFragmentShader->addSource(QLatin1String(fragmentShaderMainSrc));
+ linearBrushFragmentShader->addSource(QLatin1String(qglslLinearGradientBrushFragmentShader));
+ if (!linearBrushFragmentShader->compile()) {
+ qWarning() << "qglslLinearGradientBrushFragmentShader failed to compile:"
+ << linearBrushFragmentShader->log();
+ }
+ }
+ fragmentShader = linearBrushFragmentShader;
+ break;
+ case Qt::RadialGradientPattern:
+ if (!radialBrushVertexShader) {
+ qDebug("Compiling qglslRadialGradientBrushVertexShader");
+ radialBrushVertexShader = new QGLShader(QGLShader::VertexShader, ctx);
+ radialBrushVertexShader->addSource(QLatin1String(qglslRadialGradientBrushVertexShader));
+ if (!radialBrushVertexShader->compile()) {
+ qWarning() << "qglslRadialGradientBrushVertexShader failed to compile: "
+ << radialBrushVertexShader->log();
+ }
+ }
+ vertexShader = radialBrushVertexShader;
+
+ if (!radialBrushFragmentShader) {
+ qDebug("Compiling qglslRadialGradientBrushFragmentShader");
+ radialBrushFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx);
+ radialBrushFragmentShader->addSource(QLatin1String(fragmentShaderMainSrc));
+ radialBrushFragmentShader->addSource(QLatin1String(qglslRadialGradientBrushFragmentShader));
+ if (!radialBrushFragmentShader->compile()) {
+ qWarning() << "qglslRadialGradientBrushFragmentShader failed to compile:"
+ << radialBrushFragmentShader->log();
+ }
+ }
+ fragmentShader = radialBrushFragmentShader;
+ break;
+ case Qt::ConicalGradientPattern:
+ // FIXME: We currently use the same vertex shader as radial brush
+ if (!conicalBrushVertexShader) {
+ qDebug("Compiling qglslConicalGradientBrushVertexShader");
+ conicalBrushVertexShader = new QGLShader(QGLShader::VertexShader, ctx);
+ conicalBrushVertexShader->addSource(QLatin1String(qglslConicalGradientBrushVertexShader));
+ if (!conicalBrushVertexShader->compile()) {
+ qWarning() << "qglslConicalGradientBrushVertexShader failed to compile: "
+ << conicalBrushVertexShader->log();
+ }
+ }
+ vertexShader = conicalBrushVertexShader;
+
+ if (!conicalBrushFragmentShader) {
+ qDebug("Compiling qglslConicalGradientBrushFragmentShader");
+ conicalBrushFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx);
+ conicalBrushFragmentShader->addSource(QLatin1String(fragmentShaderMainSrc));
+ conicalBrushFragmentShader->addSource(QLatin1String(qglslConicalGradientBrushFragmentShader));
+ if (!conicalBrushFragmentShader->compile()) {
+ qWarning() << "qglslConicalGradientBrushFragmentShader failed to compile:"
+ << conicalBrushFragmentShader->log();
+ }
+ }
+ fragmentShader = conicalBrushFragmentShader;
+ break;
+ case Qt::Dense1Pattern:
+ case Qt::Dense2Pattern:
+ case Qt::Dense3Pattern:
+ case Qt::Dense4Pattern:
+ case Qt::Dense5Pattern:
+ case Qt::Dense6Pattern:
+ case Qt::Dense7Pattern:
+ case Qt::HorPattern:
+ case Qt::VerPattern:
+ case Qt::CrossPattern:
+ case Qt::BDiagPattern:
+ case Qt::FDiagPattern:
+ case Qt::DiagCrossPattern:
+ if (!patternBrushVertexShader) {
+ qDebug("Compiling qglslPatternBrushVertexShader");
+ patternBrushVertexShader = new QGLShader(QGLShader::VertexShader, ctx);
+ patternBrushVertexShader->addSource(QLatin1String(qglslPatternBrushVertexShader));
+ if (!patternBrushVertexShader->compile()) {
+ qWarning() << "qglslPatternBrushVertexShader failed to compile: "
+ << patternBrushVertexShader->log();
+ }
+ }
+ vertexShader = patternBrushVertexShader;
+
+ if (!patternBrushFragmentShader) {
+ qDebug("Compiling qglslPatternBrushFragmentShader");
+ patternBrushFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx);
+ patternBrushFragmentShader->addSource(QLatin1String(qglslNoOpacityFragmentShaderMain));
+ patternBrushFragmentShader->addSource(QLatin1String(qglslPatternBrushFragmentShader));
+ if (!patternBrushFragmentShader->compile()) {
+ qWarning() << "qglslPatternBrushFragmentShader failed to compile:"
+ << patternBrushFragmentShader->log();
+ }
+ }
+ fragmentShader = patternBrushFragmentShader;
+ break;
+ default:
+ qWarning("Unimplemented brush style (%d)", currentBrushStyle);
+ }
+
+ // Now newBrushShader is set correctly, check to see if we already have the program
+ // already linked and ready to go in the cache:
+ bool foundProgram = false;
+ foreach (QGLCachedShaderProg cachedProg, cachedPrograms) {
+ if ((cachedProg.vertexShader == vertexShader) &&
+ (cachedProg.brushShader == fragmentShader) &&
+ (cachedProg.compositionShader == 0) ) {
+
+ activeProgram = cachedProg.shader;
+ foundProgram = true;
+ break;
+ }
+ }
+
+ if (!foundProgram) {
+ qDebug() << "Linking shader program for " << currentBrushStyle;
+ // Required program not found - create it.
+ QGLShaderProgram* newProg = new QGLShaderProgram(ctx);
+
+ newProg->addShader(vertexShader);
+ newProg->addShader(fragmentShader);
+
+ if (!newProg->link())
+ qWarning() << "Shader program for " << currentBrushStyle << "failed to link:" << newProg->log();
+
+ QGLCachedShaderProg cachedProg;
+ cachedProg.vertexShader = vertexShader;
+ cachedProg.brushShader = fragmentShader;
+ cachedProg.compositionShader = 0;
+ cachedProg.shader = newProg;
+
+ cachedPrograms.append(cachedProg);
+ activeProgram = newProg;
+ }
+
+ activeProgram->use();
+ shaderProgNeedsChanging = false;
+ return true;
+}
+
+QGLShaderProgram* QGLPEXShaderManager::brushShader()
+{
+ return activeProgram;
+}
+
+// The only uniform the simple shader has is the PMV matrix
+QGLShaderProgram* QGLPEXShaderManager::simpleShader()
+{
+ if (!simpleShaderProgram) {
+ simpleShaderProgram = new QGLShaderProgram(ctx);
+
+ if (!simpleFragmentShader) {
+ simpleFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx);
+ simpleFragmentShader->addSource(QLatin1String(qglslSimpleFragmentShader));
+ if (!simpleFragmentShader->compile())
+ qWarning() << "qglslSimpleFragmentShader failed to compile:" << simpleFragmentShader->log();
+ }
+
+ simpleShaderProgram->addShader(defaultVertexShader);
+ simpleShaderProgram->addShader(simpleFragmentShader);
+ if (!simpleShaderProgram->link())
+ qWarning() << "Simple shader program failed to link:" << simpleShaderProgram->log();
+ }
+
+ return simpleShaderProgram;
+}
+
+QGLShaderProgram* QGLPEXShaderManager::imageShader()
+{
+ if (!imageShaderProgram) {
+ if (!imageVertexShader) {
+ imageVertexShader = new QGLShader(QGLShader::VertexShader, ctx);
+ imageVertexShader->addSource(QLatin1String(qglslImageVertexShader));
+ if (!imageVertexShader->compile())
+ qWarning() << "Image/Pixmap vertex shader failed to compile:" << imageVertexShader->log();
+ }
+
+ if (!imageFragmentShader) {
+ imageFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx);
+ imageFragmentShader->addSource(QLatin1String(qglslImageFragmentShader));
+ if (!imageFragmentShader->compile())
+ qWarning() << "Image/Pixmap fragment shader failed to compile:" << imageFragmentShader->log();
+ }
+
+ imageShaderProgram = new QGLShaderProgram(ctx);
+ imageShaderProgram->addShader(imageVertexShader);
+ imageShaderProgram->addShader(imageFragmentShader);
+ if (!imageShaderProgram->link())
+ qWarning() << "Image/Pixmap shader program failed to link:" << imageShaderProgram->log();
+ }
+
+ return imageShaderProgram;
+}
+
+QGLShaderProgram* QGLPEXShaderManager::textShader()
+{
+ if (!textShaderProgram) {
+ if (!textVertexShader) {
+ textVertexShader = new QGLShader(QGLShader::VertexShader, ctx);
+ textVertexShader->addSource(QLatin1String(qglslImageVertexShader));
+ if (!textVertexShader->compile())
+ qWarning() << "Text vertex shader failed to compile:" << textVertexShader->log();
+ }
+
+ if (!textFragmentShader) {
+ textFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx);
+ textFragmentShader->addSource(QLatin1String(qglslTextFragmentShader));
+ if (!textFragmentShader->compile())
+ qWarning() << "Text fragment shader failed to compile:" << textFragmentShader->log();
+ }
+
+ textShaderProgram = new QGLShaderProgram(ctx);
+ textShaderProgram->addShader(textVertexShader);
+ textShaderProgram->addShader(textFragmentShader);
+ if (!textShaderProgram->link())
+ qWarning() << "Text shader program failed to link:" << textShaderProgram->log();
+ }
+
+ return textShaderProgram;
+}
+
diff --git a/src/opengl/gl2paintengineex/qglpexshadermanager_p.h b/src/opengl/gl2paintengineex/qglpexshadermanager_p.h
new file mode 100644
index 0000000000..c8f47b24c4
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglpexshadermanager_p.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 "qglshader_p.h"
+
+
+// Worstcase combo: Brush->Mask->Composition
+
+/*
+ Vertex shader source is specified with a single string. This string
+ contains the main function and sets the gl_Position. The choice of
+ which vertex shader to use depends on:
+ - Brush style
+ - Brush transform->isAffine()
+
+ Fragment shaders are specified as multiple strings, one for the main
+ function, one for the brush calculation and optionally one for the
+ extended composition mode. Brushes are implementations of
+ "mediump vec4 brush()"
+ Composition modes are implemented as a
+ "mediump vec4 compose(mediump vec4 color)"
+ NOTE: Precision may change in future.
+
+ The choice of which main() fragment shader string to use depends on:
+ - Global opacity
+ - Brush style (some brushes apply opacity themselves)
+ - Use of mask (TODO: Need to support high quality anti-aliasing & text)
+ - Composition mode
+
+ The choice of which brush() fragment shader to use depends on:
+ - Brush style
+
+*/
+
+
+struct QGLCachedShaderProg
+{
+ QGLShader* vertexShader;
+ QGLShader* brushShader;
+ QGLShader* compositionShader;
+ QGLShaderProgram* shader;
+};
+
+class QGLPEXShaderManager
+{
+public:
+ QGLPEXShaderManager(const QGLContext* context);
+ ~QGLPEXShaderManager();
+
+ enum TransformType {IdentityTransform, ScaleTransform, TranslateTransform, FullTransform};
+
+ void optimiseForBrushTransform(const QTransform& transform);
+ void setBrushStyle(Qt::BrushStyle style);
+ void setUseGlobalOpacity(bool value);
+ void setAffineOnlyBrushTransform(bool value); // I.e. Do we need to apply perspective-correction?
+ // Not doing so saves some vertex shader calculations.
+
+ bool useCorrectShaderProg(); // returns true if the shader program has changed
+
+ QGLShaderProgram* brushShader();
+ QGLShaderProgram* simpleShader(); // Used to draw into e.g. stencil buffers
+ QGLShaderProgram* imageShader();
+ QGLShaderProgram* textShader();
+
+private:
+ QGLShader* defaultVertexShader;
+
+ QGLShader* imageVertexShader;
+ QGLShader* imageFragmentShader;
+ QGLShaderProgram* imageShaderProgram;
+
+ QGLShader* textVertexShader;
+ QGLShader* textFragmentShader;
+ QGLShaderProgram* textShaderProgram;
+
+ QGLShader* noBrushShader;
+ QGLShader* solidBrushShader;
+
+ QGLShader* conicalBrushVertexShader;
+ QGLShader* conicalBrushFragmentShader;
+
+ QGLShader* radialBrushVertexShader;
+ QGLShader* radialBrushFragmentShader;
+
+ QGLShader* linearBrushVertexShader;
+ QGLShader* linearBrushFragmentShader;
+
+ QGLShader* patternBrushVertexShader;
+ QGLShader* patternBrushFragmentShader;
+
+ QGLShader* textureBrushFragmentShader;
+ QGLShader* textureBrushVertexShader;
+
+ QGLShader* simpleFragmentShader;
+ QGLShaderProgram* simpleShaderProgram;
+
+ QGLShaderProgram* activeProgram;
+
+ Qt::BrushStyle currentBrushStyle;
+ bool useGlobalOpacity;
+ TransformType currentTransformType;
+ bool shaderProgNeedsChanging;
+
+ QList<QGLCachedShaderProg> cachedPrograms;
+
+ QGLContext* ctx;
+};
diff --git a/src/opengl/gl2paintengineex/qglshader.cpp b/src/opengl/gl2paintengineex/qglshader.cpp
new file mode 100644
index 0000000000..634be845a3
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglshader.cpp
@@ -0,0 +1,605 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglshader_p.h"
+
+
+// Windows needs to resolve OpenGL 2.0 function pointers for each context. The
+// QGL OpenGL 2.0 functions are actually macros, which take a "ctx" parameter.
+#define Q_CTX QGLContext* ctx = d->ctx; \
+ if (!ctx) \
+ return false; \
+ ctx->makeCurrent(); \
+
+
+
+
+class QGLShaderPrivate
+{
+public:
+ QGLShaderPrivate() : shaderId(0), valid(false), ctx(0) {}
+
+ GLuint shaderId;
+ QString source;
+ bool valid;
+ QGLShader::ShaderType type;
+ QGLContext* ctx;
+};
+
+
+QGLShader::QGLShader(QGLShader::ShaderType type, const QGLContext* ctx)
+ : d_ptr(new QGLShaderPrivate)
+{
+ Q_D(QGLShader);
+
+ if (!ctx)
+ ctx = QGLContext::currentContext();
+
+ if (!ctx) {
+ qWarning("QGLShader being created without a context");
+ return;
+ }
+
+ d->ctx = const_cast<QGLContext*>(ctx);
+ d->ctx->makeCurrent();
+
+ if (type == QGLShader::FragmentShader)
+ d->shaderId = glCreateShader(GL_FRAGMENT_SHADER);
+ else
+ d->shaderId = glCreateShader(GL_VERTEX_SHADER);
+
+ if (d->shaderId == 0) {
+ qWarning("Error creating shader object");
+ return;
+ }
+
+ d->type = type;
+}
+
+GLuint QGLShader::id()
+{
+ Q_D(QGLShader);
+ return d->shaderId;
+}
+
+const QGLContext* QGLShader::context()
+{
+ Q_D(QGLShader);
+ return d->ctx;
+}
+
+void QGLShader::clearSource()
+{
+ Q_D(QGLShader);
+ d->source.clear();
+ d->valid = false;
+}
+
+void QGLShader::addSource(const QLatin1String& newSource)
+{
+ Q_D(QGLShader);
+ d->source += newSource;
+ d->valid = false;
+}
+
+
+bool QGLShader::compile()
+{
+ Q_D(QGLShader);
+
+ d->valid = false;
+
+ if (d->source.size() == 0)
+ return false;
+
+ const QByteArray src_ba = d->source.toAscii();
+ const char* src = src_ba.constData();
+
+ glShaderSource(d->shaderId, 1, &src, 0);
+
+ glCompileShader(d->shaderId);
+
+ GLint shaderCompiled;
+ glGetShaderiv(d->shaderId, GL_COMPILE_STATUS, &shaderCompiled);
+ if (!shaderCompiled)
+ return false;
+
+ d->valid = true;
+ return true;
+}
+
+bool QGLShader::isValid()
+{
+ Q_D(QGLShader);
+ return d->valid;
+}
+
+QString QGLShader::log()
+{
+ Q_D(QGLShader);
+
+ char* logData;
+ GLint logSize;
+ GLint logLength;
+
+ glGetShaderiv(d->shaderId, GL_INFO_LOG_LENGTH, &logSize);
+
+ if (!logSize)
+ return QString();
+
+ logData = new char[logSize];
+ glGetShaderInfoLog(d->shaderId, logSize, &logLength, logData);
+ QString result = QString::fromAscii(logData);
+ delete [] logData;
+
+ return result;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+class QGLShaderProgramPrivate
+{
+public:
+ QGLShaderProgramPrivate() : valid(false), programId(0), ctx(0) {}
+ void populateVariableLists();
+
+ QVector<QGLShader*> shaders;
+ QGLUniformList uniforms;
+ QGLVertexAttributeList attributeArrays;
+ bool valid;
+ GLuint programId;
+ QGLContext* ctx;
+};
+
+
+
+void QGLShaderProgramPrivate::populateVariableLists()
+{
+ attributeArrays.clear();
+ uniforms.clear();
+
+ int count;
+ int sizeOfNameBuff;
+ char* name;
+ GLint nameLength;
+ GLenum type;
+ GLint size;
+ GLint location;
+
+ glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &count);
+ glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &sizeOfNameBuff);
+ name = new char[sizeOfNameBuff];
+
+ for (int i = 0; i < count; ++i) {
+ nameLength = -1;
+ glGetActiveAttrib(programId, i, sizeOfNameBuff, &nameLength, &size, &type, name);
+ if (nameLength == -1)
+ continue;
+
+ location = glGetAttribLocation(programId, name);
+ attributeArrays.insert(QString::fromAscii(name), QGLVertexAttribute(type, location, ctx));
+ }
+
+ delete [] name;
+
+
+ glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &count);
+ glGetProgramiv(programId, GL_ACTIVE_UNIFORM_MAX_LENGTH, &sizeOfNameBuff);
+
+ name = new char[sizeOfNameBuff];
+
+ for (int i = 0; i < count; ++i) {
+ nameLength = -1;
+ glGetActiveUniform(programId, i, sizeOfNameBuff, &nameLength, &size, &type, name);
+ if (nameLength == -1)
+ continue;
+
+ location = glGetUniformLocation(programId, name);
+ uniforms.insert(QString::fromAscii(name), QGLUniform(type, location, ctx));
+ }
+}
+
+
+QGLShaderProgram::QGLShaderProgram(const QGLContext* ctx)
+ : d_ptr(new QGLShaderProgramPrivate)
+{
+ Q_D(QGLShaderProgram);
+ if (!ctx)
+ ctx = QGLContext::currentContext();
+
+ if (!ctx) {
+ qWarning("QGLShaderProgram being created without a context");
+ return;
+ }
+
+ d->ctx = const_cast<QGLContext*>(ctx);
+ d->ctx->makeCurrent();
+
+ d->programId = glCreateProgram();
+
+ d->valid = false;
+}
+
+
+const QGLUniformList & QGLShaderProgram::uniforms()
+{
+ Q_D(QGLShaderProgram);
+ return const_cast<const QGLUniformList&>(d->uniforms);
+}
+
+
+const QGLVertexAttributeList& QGLShaderProgram::vertexAttributes()
+{
+ Q_D(QGLShaderProgram);
+ return const_cast<const QGLVertexAttributeList&>(d->attributeArrays);
+}
+
+
+bool QGLShaderProgram::addShader(QGLShader* newShader)
+{
+ Q_D(QGLShaderProgram);
+ if (!newShader || !d->ctx)
+ return false;
+
+ if (newShader->context() != d->ctx) {
+ qWarning("Shader object's context does not match program's context");
+ return false;
+ }
+
+ if (!newShader->isValid())
+ return false;
+
+ QGLContext* ctx = d->ctx;
+ if (!ctx)
+ return false;
+ ctx->makeCurrent();
+
+ glAttachShader(d->programId, newShader->id());
+
+ d->shaders.append(newShader);
+ return true;
+}
+
+
+bool QGLShaderProgram::removeShader(QGLShader* oldShader)
+{
+ Q_D(QGLShaderProgram);
+
+ int idx = d->shaders.indexOf(oldShader);
+
+ if (idx == -1)
+ return false;
+
+ d->shaders.remove(idx);
+
+ QGLContext* ctx = d->ctx;
+ if (!ctx)
+ return false;
+ ctx->makeCurrent();
+
+ glDetachShader(d->programId, oldShader->id());
+ return true;
+}
+
+
+bool QGLShaderProgram::removeAllShaders()
+{
+ Q_D(QGLShaderProgram);
+
+ QGLContext* ctx = d->ctx;
+ if (!ctx)
+ return false;
+ ctx->makeCurrent();
+
+ foreach (QGLShader* shader, d->shaders)
+ glDetachShader(d->programId, shader->id());
+
+ d->shaders.clear();
+ return true;
+}
+
+#include <stdio.h>
+
+bool QGLShaderProgram::link()
+{
+ Q_D(QGLShaderProgram);
+
+ QGLContext* ctx = d->ctx;
+ if (!ctx)
+ return false;
+ ctx->makeCurrent();
+
+ glLinkProgram(d->programId);
+
+
+ GLint linked;
+ glGetProgramiv(d->programId, GL_LINK_STATUS, &linked);
+
+ if (!linked)
+ return false;
+
+ d->populateVariableLists();
+
+ d->valid = true;
+ return true;
+}
+
+void QGLShaderProgram::use()
+{
+ Q_D(QGLShaderProgram);
+ if (!d->valid)
+ return;
+
+ glUseProgram(d->programId);
+}
+
+
+QString QGLShaderProgram::log()
+{
+ Q_D(QGLShaderProgram);
+
+ QGLContext* ctx = d->ctx;
+ if (!ctx)
+ return QString();
+ ctx->makeCurrent();
+
+ GLint logSize = -666;
+ glGetProgramiv(d->programId, GL_INFO_LOG_LENGTH, &logSize);
+
+ char* logData = new char[logSize];
+ GLint logLength;
+
+ glGetProgramInfoLog(d->programId, logSize, &logLength, logData);
+
+ QString result = QString::fromAscii(logData);
+ delete [] logData;
+
+ return result;
+}
+
+GLuint QGLShaderProgram::id()
+{
+ Q_D(QGLShaderProgram);
+ return d->programId;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+
+
+
+QGLUniform::QGLUniform()
+ : m_id(0), m_type(QGLInvalidType), ctx(0)
+{
+ qWarning("Unknown uniform! Either the uniform doesn't exist or it was removed at shader link");
+}
+
+const QGLUniform& QGLUniform::operator=(const GLfloat& rhs) const
+{
+ if (m_type != QGLFloatType)
+ return *this;
+
+ glUniform1f(m_id, rhs);
+
+ return *this;
+}
+
+const QGLUniform& QGLUniform::operator=(const QGLVec2& rhs) const
+{
+ if (m_type != QGLVec2Type)
+ return *this;
+
+ glUniform2fv(m_id, 1, (const GLfloat*)&rhs);
+
+ return *this;
+}
+
+const QGLUniform& QGLUniform::operator=(const QSizeF& rhs) const
+{
+ if (m_type != QGLVec2Type)
+ return *this;
+
+ glUniform2f(m_id, rhs.width(), rhs.height());
+
+ return *this;
+}
+
+const QGLUniform& QGLUniform::operator=(const QPointF& rhs) const
+{
+ if (m_type != QGLVec2Type)
+ return *this;
+
+ glUniform2f(m_id, rhs.x(), rhs.y());
+
+ return *this;
+}
+
+const QGLUniform& QGLUniform::operator=(const QGLVec3& rhs) const
+{
+ if (m_type != QGLVec3Type)
+ return *this;
+
+ glUniform3fv(m_id, 1, (const GLfloat*)&rhs);
+
+ return *this;
+}
+
+const QGLUniform& QGLUniform::operator=(const QGLVec4& rhs) const
+{
+ if (m_type != QGLVec4Type)
+ return *this;
+
+ glUniform4fv(m_id, 1, (const GLfloat*)&rhs);
+
+ return *this;
+}
+
+const QGLUniform& QGLUniform::operator=(const QColor& rhs) const
+{
+ if (m_type != QGLVec4Type)
+ return *this;
+
+ glUniform4f(m_id, rhs.redF(), rhs.greenF(), rhs.blueF(), rhs.alphaF());
+
+ return *this;
+}
+
+const QGLUniform& QGLUniform::operator=(const GLfloat rhs[2][2]) const
+{
+ if (m_type != QGLMat2Type)
+ return *this;
+
+ glUniformMatrix2fv(m_id, 1, GL_FALSE, (GLfloat*)rhs);
+
+ return *this;
+}
+
+const QGLUniform& QGLUniform::operator=(const GLfloat rhs[3][3]) const
+{
+ if (m_type != QGLMat3Type)
+ return *this;
+
+ glUniformMatrix3fv(m_id, 1, GL_FALSE, (GLfloat*)rhs);
+
+ return *this;
+}
+
+// Transposes ready for GL
+const QGLUniform& QGLUniform::operator=(const QTransform& rhs) const
+{
+ if (m_type != QGLMat3Type)
+ return *this;
+
+ GLfloat mat3[3][3] = {
+ {rhs.m11(), rhs.m12(), rhs.m13()},
+ {rhs.m21(), rhs.m22(), rhs.m23()},
+ {rhs.m31(), rhs.m32(), rhs.m33()}
+ };
+
+ glUniformMatrix3fv(m_id, 1, GL_FALSE, (GLfloat*)mat3);
+
+ return *this;
+}
+
+const QGLUniform& QGLUniform::operator=(const GLfloat rhs[4][4]) const
+{
+ if (m_type != QGLMat4Type)
+ return *this;
+
+ glUniformMatrix4fv(m_id, 1, GL_FALSE, (GLfloat*)rhs);
+
+ return *this;
+}
+
+const QGLUniform& QGLUniform::operator=(const GLuint& rhs) const
+{
+ if ((m_type != QGLSampler2DType) || (m_type != QGLSamplerCubeType))
+ return *this;
+
+ glUniform1i(m_id, rhs);
+
+ return *this;
+}
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+
+QGLVertexAttribute::QGLVertexAttribute()
+ : m_id(0), m_type(QGLInvalidType), ctx(0)
+{
+ qWarning("Unknown vertex attribute!");
+}
+
+void QGLVertexAttribute::enable() const
+{
+ glEnableVertexAttribArray(m_id);
+}
+
+void QGLVertexAttribute::disable() const
+{
+ glDisableVertexAttribArray(m_id);
+}
+
+// NOTE: Under PC emulation, QGLVec4Type is _always_ returned as the type, so this
+// method isn't very useful. I.e. The datatypes are needed to distinguish the different
+// sizes for the function signatures.
+const QGLVertexAttribute& QGLVertexAttribute::operator=(const GLfloat* rhs) const
+{
+ int size = -1;
+ if (m_type == QGLFloatType)
+ size = 1;
+ else if (m_type == QGLVec2Type)
+ size = 2;
+ else if (m_type == QGLVec3Type)
+ size = 3;
+ else if (m_type == QGLVec4Type)
+ size = 4;
+ else if (m_type == QGLMat2Type) //### Not sure if this is right for matrix attributes...
+ size = 4;
+ else if (m_type == QGLMat3Type) //### Not sure if this is right for matrix attributes...
+ size = 9;
+ else if (m_type == QGLMat4Type) //### Not sure if this is right for matrix attributes...
+ size = 16;
+ else
+ return *this;
+
+ glVertexAttribPointer(m_id, size, GL_FLOAT, GL_FALSE, 0, rhs);
+
+ return *this;
+}
+
+const QGLVertexAttribute& QGLVertexAttribute::operator=(const QGLVec3* rhs) const
+{
+ glVertexAttribPointer(m_id, 3, GL_FLOAT, GL_FALSE, 0, (GLfloat*)rhs);
+
+ return *this;
+}
diff --git a/src/opengl/gl2paintengineex/qglshader_p.h b/src/opengl/gl2paintengineex/qglshader_p.h
new file mode 100644
index 0000000000..1625b84ac7
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglshader_p.h
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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.
+//
+
+/*
+Uniform Types in OpenGL ES 2.0
+==============================
+GL_FLOAT GLfloat GLfloat
+GL_FLOAT_VEC2 QGLVec2 GLfloat[2]
+GL_FLOAT_VEC3 QGLVec3 GLfloat[3]
+GL_FLOAT_VEC4 QGLVec4 GLfloat[4]
+GL_INT GLint GLint
+GL_INT_VEC2 QGLIVec2 GLint[2]
+GL_INT_VEC3 QGLIVec3 GLint[3]
+GL_INT_VEC4 QGLIVec4 GLint[4]
+GL_BOOL GLbool GLbool
+GL_BOOL_VEC2 QGLBVec2 GLbool[2]
+GL_BOOL_VEC3 QGLBVec3 GLbool[3]
+GL_BOOL_VEC4 QGLBVec4 GLbool[4]
+GL_FLOAT_MAT2 QGLMat2 GLfloat[2][2]
+GL_FLOAT_MAT3 QGLMat3 GLfloat[3][3]
+GL_FLOAT_MAT4 QGLMat4 GLfloat[4][4]
+GL_SAMPLER_2D QGLSampler2D GLuint
+GL_SAMPLER_CUBE QGLSamplerCube GLuint
+
+Additional Types in Desktop OpenGL 2.0
+======================================
+SAMPLER_1D,
+SAMPLER_3D,
+SAMPLER_1D_SHADOW,
+SAMPLER_2D_SHADOW.
+*/
+
+#include <QtOpenGL>
+
+
+typedef struct {
+ GLfloat a;
+ GLfloat b;
+} QGLVec2;
+
+typedef struct {
+ GLfloat a;
+ GLfloat b;
+ GLfloat c;
+} QGLVec3;
+
+typedef struct {
+ GLfloat a;
+ GLfloat b;
+ GLfloat c;
+ GLfloat d;
+} QGLVec4;
+
+
+class QGLShaderProgram;
+
+class QGLShaderPrivate;
+
+class QGLShader : QObject
+{
+ Q_OBJECT
+public:
+ enum ShaderType {VertexShader, FragmentShader};
+
+ QGLShader(ShaderType type, const QGLContext* ctx = 0);
+
+ GLuint id();
+ void clearSource();
+ void addSource(const QLatin1String& newSource);
+ bool compile();
+ bool isValid();
+ QString log();
+ const QGLContext* context(); //maybe make private with prog a friend?
+
+private:
+ QGLShaderPrivate* d_ptr;
+ Q_DECLARE_PRIVATE(QGLShader);
+
+/*
+public slots:
+ void cleanupGLContextRefs(const QGLContext *context);
+*/
+};
+
+
+enum QGLType {
+ QGLInvalidType = 0,
+ QGLFloatType = GL_FLOAT,
+ QGLVec2Type = GL_FLOAT_VEC2,
+ QGLVec3Type = GL_FLOAT_VEC3,
+ QGLVec4Type = GL_FLOAT_VEC4,
+ QGLIntType = GL_INT,
+ QGLIVec2Type = GL_INT_VEC2,
+ QGLIVec3Type = GL_INT_VEC3,
+ QGLIVec4Type = GL_INT_VEC4,
+ QGLBoolType = GL_BOOL,
+ QGLBVec2Type = GL_BOOL_VEC2,
+ QGLBVec3Type = GL_BOOL_VEC3,
+ QGLBVec4Type = GL_BOOL_VEC4,
+ QGLMat2Type = GL_FLOAT_MAT2,
+ QGLMat3Type = GL_FLOAT_MAT3,
+ QGLMat4Type = GL_FLOAT_MAT4,
+ QGLSampler2DType = GL_SAMPLER_2D,
+ QGLSamplerCubeType = GL_SAMPLER_CUBE
+};
+
+class QGLUniform
+{
+public:
+
+ QGLUniform(GLenum glType, GLint location, QGLContext* context)
+ : m_id(location), m_type(QGLType(glType)), ctx(context) {}
+
+ QGLUniform(); // Called by QMap when there's no match on the name
+
+ QGLType type() const {return m_type;}
+ GLuint id() const {return m_id;}
+
+ // Seems odd to be const, but it doesn't actually modify any of the
+ // class members, only the GL state!
+ const QGLUniform& operator=(const GLfloat&) const;
+
+ const QGLUniform& operator=(const QGLVec2&) const;
+ const QGLUniform& operator=(const QSizeF&) const;
+ const QGLUniform& operator=(const QPointF&) const;
+
+ const QGLUniform& operator=(const QGLVec3&) const;
+
+ const QGLUniform& operator=(const QGLVec4&) const;
+ const QGLUniform& operator=(const QColor&) const;
+
+ const QGLUniform& operator=(const GLfloat[2][2]) const;
+
+ const QGLUniform& operator=(const GLfloat[3][3]) const;
+ const QGLUniform& operator=(const QTransform&) const;
+
+ const QGLUniform& operator=(const GLfloat[4][4]) const;
+
+ const QGLUniform& operator=(const GLuint&) const; // sampler2d, specifying a texture unit
+
+
+protected:
+ GLuint m_id;
+ QGLType m_type;
+ QGLContext* ctx;
+};
+
+typedef QMap<QString, QGLUniform> QGLUniformList;
+typedef QMapIterator<QString, QGLUniform> QGLUniformListIterator;
+
+
+class QGLVertexAttribute
+{
+public:
+ QGLVertexAttribute(GLenum glType, GLuint location, QGLContext* context)
+ : m_id(location), m_type(QGLType(glType)), ctx(context) {}
+
+ QGLVertexAttribute(); // Called by QMap when there's no match on the name
+
+ QGLType type() const {return m_type;}
+ GLuint id() const {return m_id;}
+ void enable() const;
+ void disable() const;
+
+ const QGLVertexAttribute& operator=(const GLfloat* rhs) const;
+ const QGLVertexAttribute& operator=(const QGLVec3* rhs) const;
+
+protected:
+ GLuint m_id;
+ QGLType m_type;
+ QGLContext* ctx;
+};
+
+//TODO: Convert into setter overloads on QGLShaderProgram
+typedef QMap<QString, QGLVertexAttribute> QGLVertexAttributeList;
+typedef QMapIterator<QString, QGLVertexAttribute> QGLVertexAttributeListIterator;
+
+
+
+class QGLShaderProgramPrivate;
+
+class QGLShaderProgram : QObject
+{
+ Q_OBJECT
+public:
+ QGLShaderProgram(const QGLContext* ctx = 0);
+
+ const QGLUniformList & uniforms();
+ const QGLVertexAttributeList& vertexAttributes();
+
+ bool addShader(QGLShader* newShader);
+ bool removeShader(QGLShader* oldShader);
+ bool removeAllShaders();
+
+ bool link();
+ QString log();
+ bool isValid();
+ void use();
+
+ GLuint id();
+
+private:
+ QGLShaderProgramPrivate* d_ptr;
+ Q_DECLARE_PRIVATE(QGLShaderProgram);
+
+/*
+public slots:
+ void cleanupGLContextRefs(const QGLContext *context);
+*/
+};
+
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
new file mode 100644
index 0000000000..a74f0443fc
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -0,0 +1,1278 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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.
+*/
+
+
+#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/qtextureglyphcache_p.h>
+
+#include "qglgradientcache_p.h"
+#include "qglpexshadermanager_p.h"
+#include "qgl2pexvertexarray_p.h"
+
+
+extern QImage qt_imageForBrush(int brushStyle, bool invert); //in qbrush.cpp
+
+
+#include <QDebug>
+
+
+static const GLuint QT_VERTEX_COORDS_ATTR = 0;
+static const GLuint QT_TEXTURE_COORDS_ATTR = 1;
+static const GLuint QT_BRUSH_TEXTURE_UNIT = 0;
+
+class QGL2PaintEngineExPrivate : public QPaintEngineExPrivate
+{
+ Q_DECLARE_PUBLIC(QGL2PaintEngineEx)
+public:
+ QGL2PaintEngineExPrivate(QGL2PaintEngineEx *q_ptr) :
+ q(q_ptr),
+ width(0), height(0),
+ ctx(0),
+ currentBrush( &(q->state()->brush) ),
+ inverseScale(1),
+ shaderManager(0)
+ { }
+
+ ~QGL2PaintEngineExPrivate();
+
+ void updateBrushTexture();
+ void updateBrushUniforms();
+ void updateMatrix();
+ void updateCompositionMode();
+ void updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform);
+
+ void setBrush(const QBrush* brush);
+
+ void drawTexture(const QGLRect& dest, const QGLRect& src, int txtWidth, int txtHeight);
+
+ void fill(const QVectorPath &path);
+ void drawOutline(const QVectorPath& path);
+
+ void drawVertexArrays(QGL2PEXVertexArray& vertexArray, GLenum primitive);
+ // ^ draws whatever is in the vertex array
+ void composite(const QGLRect& boundingRect);
+ // ^ Composites the bounding rect onto dest buffer
+ void fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill);
+ // ^ Calls drawVertexArrays to render into stencil buffer
+ void cleanStencilBuffer(const QGLRect& area);
+
+ void prepareForDraw();
+
+ inline void useSimpleShader();
+ inline QColor premultiplyColor(QColor c, GLfloat opacity);
+
+ QGL2PaintEngineEx* q;
+
+ //### Move into QGLDrawable
+ int width, height;
+ QGLContext* ctx;
+
+ // Dirty flags
+ bool matrixDirty; // Implies matrix uniforms are also dirty
+ bool compositionModeDirty;
+ bool brushTextureDirty;
+ bool brushUniformsDirty;
+ bool simpleShaderMatrixUniformDirty;
+ bool brushShaderMatrixUniformDirty;
+ bool imageShaderMatrixUniformDirty;
+ bool textShaderMatrixUniformDirty;
+ bool stencilBuferDirty;
+
+ const QBrush* currentBrush; // May not be the state's brush!
+
+ GLfloat inverseScale;
+
+ QGL2PEXVertexArray pathVertexArray;
+
+ GLfloat pmvMatrix[4][4];
+
+ QGLPEXShaderManager* shaderManager;
+
+ // Clipping & state stuff stolen from QOpenGLPaintEngine:
+ void updateDepthClip();
+ uint use_system_clip : 1;
+};
+
+
+////////////////////////////////// Private Methods //////////////////////////////////////////
+
+QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate()
+{
+ if (shaderManager) {
+ delete shaderManager;
+ shaderManager = 0;
+ }
+}
+
+void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform)
+{
+ glActiveTexture(QT_BRUSH_TEXTURE_UNIT);
+
+ 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);
+}
+
+
+QColor QGL2PaintEngineExPrivate::premultiplyColor(QColor c, GLfloat opacity)
+{
+ uint alpha = qRound(c.alpha() * opacity);
+ return QColor ( ((c.red() * alpha + 128) >> 8),
+ ((c.green() * alpha + 128) >> 8),
+ ((c.blue() * alpha + 128) >> 8),
+ alpha);
+}
+
+
+void QGL2PaintEngineExPrivate::setBrush(const QBrush* brush)
+{
+ currentBrush = brush;
+ brushTextureDirty = true;
+ brushUniformsDirty = true;
+ shaderManager->setBrushStyle(currentBrush->style());
+ shaderManager->setAffineOnlyBrushTransform(currentBrush->transform().isAffine());
+}
+
+
+// Unless this gets used elsewhere, it's probably best to merge it into fillStencilWithVertexArray
+void QGL2PaintEngineExPrivate::useSimpleShader()
+{
+ shaderManager->simpleShader()->use();
+
+ if (matrixDirty)
+ updateMatrix();
+
+ if (simpleShaderMatrixUniformDirty) {
+ shaderManager->simpleShader()->uniforms()[QLatin1String("pmvMatrix")] = pmvMatrix;
+ simpleShaderMatrixUniformDirty = false;
+ }
+}
+
+
+Q_GLOBAL_STATIC(QGLGradientCache, qt_opengl_gradient_cache)
+
+void QGL2PaintEngineExPrivate::updateBrushTexture()
+{
+// 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, true);
+
+ glActiveTexture(QT_BRUSH_TEXTURE_UNIT);
+ ctx->d_func()->bindTexture(texImage, GL_TEXTURE_2D, GL_RGBA, true);
+ updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, true);
+ }
+ 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 = qt_opengl_gradient_cache()->getBuffer(*g, 1.0, ctx);
+
+ if (g->spread() == QGradient::RepeatSpread || g->type() == QGradient::ConicalGradient)
+ updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, true);
+ else if (g->spread() == QGradient::ReflectSpread)
+ updateTextureFilter(GL_TEXTURE_2D, GL_MIRRORED_REPEAT_IBM, true);
+ else
+ updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, true);
+
+ glBindTexture(GL_TEXTURE_2D, texId);
+ }
+ else if (style == Qt::TexturePattern) {
+ const QPixmap& texPixmap = currentBrush->texture();
+
+ glActiveTexture(QT_BRUSH_TEXTURE_UNIT);
+ ctx->d_func()->bindTexture(texPixmap, GL_TEXTURE_2D, GL_RGBA, true);
+ updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, true);
+ }
+ brushTextureDirty = false;
+}
+
+
+void QGL2PaintEngineExPrivate::updateBrushUniforms()
+{
+// qDebug("QGL2PaintEngineExPrivate::updateBrushUniforms()");
+ Qt::BrushStyle style = currentBrush->style();
+
+ if (style == Qt::NoBrush)
+ return;
+
+ GLfloat opacity = 1.0;
+ if (q->state()->opacity < 0.99f)
+ opacity = (GLfloat)q->state()->opacity;
+ bool setOpacity = true;
+
+ QTransform brushQTransform = currentBrush->transform();
+
+ if (style == Qt::SolidPattern) {
+ QColor col = premultiplyColor(currentBrush->color(), opacity);
+ shaderManager->brushShader()->uniforms()[QLatin1String("fragmentColor")] = col;
+ setOpacity = false;
+ }
+ else {
+ // All other brushes have a transform and thus need the translation point:
+ QPointF translationPoint;
+
+ if (style <= Qt::DiagCrossPattern) {
+ translationPoint = q->state()->brushOrigin;
+
+ QColor col = premultiplyColor(currentBrush->color(), opacity);
+
+ shaderManager->brushShader()->uniforms()[QLatin1String("patternColor")] = col;
+ setOpacity = false; //So code below doesn't try to set the opacity uniform
+
+ QGLVec2 halfViewportSize = { width*0.5, height*0.5 };
+ shaderManager->brushShader()->uniforms()[QLatin1String("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;
+
+ // ###
+ QGLVec3 linearData = {
+ l.x(),
+ l.y(),
+ 1.0f / (l.x() * l.x() + l.y() * l.y())
+ };
+
+ shaderManager->brushShader()->uniforms()[QLatin1String("linearData")] = linearData;
+
+ QGLVec2 halfViewportSize = { width*0.5, height*0.5 };
+ shaderManager->brushShader()->uniforms()[QLatin1String("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->brushShader()->uniforms()[QLatin1String("angle")] = angle;
+
+ QGLVec2 halfViewportSize = { width*0.5, height*0.5 };
+ shaderManager->brushShader()->uniforms()[QLatin1String("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->brushShader()->uniforms()[QLatin1String("fmp")] = fmp;
+
+ GLfloat fmp2_m_radius2 = -fmp.x() * fmp.x() - fmp.y() * fmp.y() + realRadius*realRadius;
+ shaderManager->brushShader()->uniforms()[QLatin1String("fmp2_m_radius2")] = fmp2_m_radius2;
+
+ shaderManager->brushShader()->uniforms()[QLatin1String("inverse_2_fmp2_m_radius2")] =
+ GLfloat(1.0 / (2.0*fmp2_m_radius2));
+
+ QGLVec2 halfViewportSize = { width*0.5, height*0.5 };
+ shaderManager->brushShader()->uniforms()[QLatin1String("halfViewportSize")] = halfViewportSize;
+ }
+ else if (style == Qt::TexturePattern) {
+ translationPoint = q->state()->brushOrigin;
+
+ const QPixmap& texPixmap = currentBrush->texture();
+
+ QSizeF invertedTextureSize( 1.0 / texPixmap.width(), 1.0 / texPixmap.height() );
+ shaderManager->brushShader()->uniforms()[QLatin1String("invertedTextureSize")] = invertedTextureSize;
+
+ QGLVec2 halfViewportSize = { width*0.5, height*0.5 };
+ shaderManager->brushShader()->uniforms()[QLatin1String("halfViewportSize")] = halfViewportSize;
+ }
+ else
+ qWarning("QGL2PaintEngineEx: Unimplemented fill style");
+
+ QTransform translate(1, 0, 0, 1, -translationPoint.x(), -translationPoint.y());
+ QTransform gl_to_qt(1, 0, 0, -1, 0, height);
+ QTransform inv_matrix = gl_to_qt * (brushQTransform * q->state()->matrix).inverted() * translate;
+
+ shaderManager->brushShader()->uniforms()[QLatin1String("brushTransform")] = inv_matrix;
+ shaderManager->brushShader()->uniforms()[QLatin1String("brushTexture")] = QT_BRUSH_TEXTURE_UNIT;
+
+ if (setOpacity)
+ shaderManager->brushShader()->uniforms()[QLatin1String("opacity")] = opacity;
+ }
+ brushUniformsDirty = false;
+}
+
+
+// This assumes the shader manager has already setup the correct shader program
+void QGL2PaintEngineExPrivate::updateMatrix()
+{
+// qDebug("QGL2PaintEngineExPrivate::updateMatrix()");
+
+ // We setup the Projection matrix to be the equivilant of glOrtho(0, w, h, 0, -1, 1):
+ GLfloat P[4][4] = {
+ {2.0/width, 0.0, 0.0, -1.0},
+ {0.0, -2.0/height, 0.0, 1.0},
+ {0.0, 0.0, -1.0, 0.0},
+ {0.0, 0.0, 0.0, 1.0}
+ };
+
+ // Use the (3x3) transform for the Model~View matrix:
+ const QTransform& transform = q->state()->matrix;
+ GLfloat MV[4][4] = {
+ {transform.m11(), transform.m21(), 0.0, transform.dx() + 0.5},
+ {transform.m12(), transform.m22(), 0.0, transform.dy() + 0.5},
+ {0.0, 0.0, 1.0, 0.0},
+ {transform.m13(), transform.m23(), 0.0, transform.m33()}
+ };
+
+ // NOTE: OpenGL ES works with column-major matrices, so when we multiply the matrices,
+ // we also transpose them ready for GL.
+ for (int row = 0; row < 4; ++row) {
+ for (int col = 0; col < 4; ++col) {
+ pmvMatrix[col][row] = 0.0;
+ for (int n = 0; n < 4; ++n)
+ pmvMatrix[col][row] += P[row][n] * MV[n][col];
+ }
+ }
+
+ // 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;
+
+ // The actual data has been updated so both shader program's uniforms need updating
+ simpleShaderMatrixUniformDirty = true;
+ brushShaderMatrixUniformDirty = true;
+ imageShaderMatrixUniformDirty = true;
+ textShaderMatrixUniformDirty = true;
+}
+
+
+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;
+}
+
+
+void QGL2PaintEngineExPrivate::drawTexture(const QGLRect& dest, const QGLRect& src, int txtWidth, int txtHeight)
+{
+// qDebug("QGL2PaintEngineExPrivate::drawImage()");
+
+ // We have a shader specifically for drawPixmap/drawImage...
+ shaderManager->imageShader()->use();
+
+ updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
+
+ if (compositionModeDirty)
+ updateCompositionMode();
+
+ if (matrixDirty)
+ updateMatrix();
+
+ if (imageShaderMatrixUniformDirty) {
+ shaderManager->imageShader()->uniforms()[QLatin1String("pmvMatrix")] = pmvMatrix;
+ imageShaderMatrixUniformDirty = false;
+ }
+
+ shaderManager->imageShader()->uniforms()[QLatin1String("textureSampler")] = QT_BRUSH_TEXTURE_UNIT;
+
+// if (q->state()->opacity < 0.99f)
+ shaderManager->imageShader()->uniforms()[QLatin1String("opacity")] = (GLfloat)q->state()->opacity;
+
+ GLfloat vertexCoords[] = {
+ dest.left, dest.top,
+ dest.left, dest.bottom,
+ dest.right, dest.bottom,
+ dest.right, dest.top
+ };
+
+ glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoords);
+
+ GLfloat dx = 1.0 / txtWidth;
+ GLfloat dy = 1.0 / txtHeight;
+
+ QGLRect srcTextureRect(src.left*dx, 1.0 - src.top*dy, src.right*dx, 1.0 - src.bottom*dy);
+
+ GLfloat textureCoords[] = {
+ srcTextureRect.left, srcTextureRect.top,
+ srcTextureRect.left, srcTextureRect.bottom,
+ srcTextureRect.right, srcTextureRect.bottom,
+ srcTextureRect.right, srcTextureRect.top
+ };
+
+ glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
+ glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoords);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
+ glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+}
+
+
+void QGL2PaintEngineExPrivate::drawOutline(const QVectorPath& path)
+{
+// qDebug("QGL2PaintEngineExPrivate::drawOutline()");
+ if (matrixDirty)
+ updateMatrix();
+
+ pathVertexArray.clear();
+ pathVertexArray.addPath(path, inverseScale);
+
+ if (path.hasImplicitClose()) {
+ // Close the path's outline
+ pathVertexArray.lineToArray(path.points()[0], path.points()[1]);
+ pathVertexArray.stops().last() += 1;
+ }
+
+ prepareForDraw();
+ drawVertexArrays(pathVertexArray, GL_LINE_STRIP);
+}
+
+
+// Assumes everything is configured for the brush you want to use
+void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
+{
+ 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();
+ composite(rect);
+ }
+ else if (path.shape() == QVectorPath::EllipseHint) {
+ pathVertexArray.clear();
+ pathVertexArray.addPath(path, inverseScale);
+ prepareForDraw();
+ drawVertexArrays(pathVertexArray, GL_TRIANGLE_FAN);
+ }
+ else {
+ // The path is too complicated & needs the stencil technique
+ pathVertexArray.clear();
+ pathVertexArray.addPath(path, inverseScale);
+
+ fillStencilWithVertexArray(pathVertexArray, path.hasWindingFill());
+
+ // Stencil the brush onto the dest buffer
+ glStencilFunc(GL_NOTEQUAL, 0, 0xFFFF); // Pass if stencil buff value != 0
+ glEnable(GL_STENCIL_TEST);
+ prepareForDraw();
+ composite(pathVertexArray.boundingRect());
+ glDisable(GL_STENCIL_TEST);
+
+ cleanStencilBuffer(pathVertexArray.boundingRect());
+ }
+}
+
+
+void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill)
+{
+// qDebug("QGL2PaintEngineExPrivate::fillStencilWithVertexArray()");
+ if (stencilBuferDirty) {
+ // Clear the stencil buffer to zeros
+ glDisable(GL_STENCIL_TEST);
+ glStencilMask(0xFFFF); // Enable writing to stencil buffer, otherwise glClear wont do anything.
+ glClearStencil(0); // Clear to zero
+ glClear(GL_STENCIL_BUFFER_BIT);
+ stencilBuferDirty = false;
+ }
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes
+ glStencilMask(0xFFFF); // Enable stencil writes
+ glStencilFunc(GL_ALWAYS, 0, 0xFFFF); // Always pass the stencil test
+
+ // Setup the stencil op:
+ if (useWindingFill) {
+ glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); // Inc. for front-facing triangle
+ glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); //Dec. for back-facing "holes"
+ } else
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
+
+ // No point in using a fancy gradiant shader for writing into the stencil buffer!
+ useSimpleShader();
+
+ glEnable(GL_STENCIL_TEST); // For some reason, this has to happen _after_ the simple shader is use()'d
+ glDisable(GL_BLEND);
+
+ // Draw the vertecies into the stencil buffer:
+ drawVertexArrays(vertexArray, GL_TRIANGLE_FAN);
+
+ // Enable color writes & disable stencil writes
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glStencilMask(0);
+}
+
+void QGL2PaintEngineExPrivate::cleanStencilBuffer(const QGLRect& area)
+{
+// qDebug("QGL2PaintEngineExPrivate::cleanStencilBuffer()");
+ useSimpleShader();
+
+ GLfloat rectVerts[] = {
+ area.left, area.top,
+ area.left, area.bottom,
+ area.right, area.bottom,
+ area.right, area.top
+ };
+
+ glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, rectVerts);
+
+ glEnable(GL_STENCIL_TEST);
+ glStencilFunc(GL_ALWAYS, 0, 0xFFFF); // Always pass the stencil test
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes
+ glStencilMask(0xFFFF); // Enable writing to stencil buffer
+ glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // Write 0's to stencil buffer
+
+ glDisable(GL_BLEND);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+
+ // Enable color writes & disable stencil writes
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glStencilMask(0);
+ glDisable(GL_STENCIL_TEST);
+}
+
+void QGL2PaintEngineExPrivate::prepareForDraw()
+{
+ if (brushTextureDirty)
+ updateBrushTexture();
+
+ if (compositionModeDirty)
+ updateCompositionMode();
+
+ if (shaderManager->useCorrectShaderProg()) {
+ // The shader program has changed so mark all uniforms as dirty:
+ brushUniformsDirty = true;
+ brushShaderMatrixUniformDirty = true;
+ }
+
+ if (brushUniformsDirty)
+ updateBrushUniforms();
+
+ if (brushShaderMatrixUniformDirty) {
+ shaderManager->brushShader()->uniforms()[QLatin1String("pmvMatrix")] = pmvMatrix;
+ brushShaderMatrixUniformDirty = false;
+ }
+
+ if ((q->state()->opacity < 0.99f) || !currentBrush->isOpaque())
+ glEnable(GL_BLEND);
+ else
+ glDisable(GL_BLEND);
+}
+
+void QGL2PaintEngineExPrivate::composite(const QGLRect& boundingRect)
+{
+ // Setup a vertex array for the bounding rect:
+ GLfloat rectVerts[] = {
+ boundingRect.left, boundingRect.top,
+ boundingRect.left, boundingRect.bottom,
+ boundingRect.right, boundingRect.bottom,
+ boundingRect.right, boundingRect.top
+ };
+
+ glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, rectVerts);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+}
+
+// Draws the vertex array as a set of <vertexArrayStops.size()> triangle fans.
+void QGL2PaintEngineExPrivate::drawVertexArrays(QGL2PEXVertexArray& vertexArray, GLenum primitive)
+{
+ // Now setup the pointer to the vertex array:
+ glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexArray.data());
+
+ int previousStop = 0;
+ foreach(int stop, vertexArray.stops()) {
+/*
+ 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;
+ }
+ glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+}
+
+
+
+
+
+/////////////////////////////////// Public Methods //////////////////////////////////////////
+
+QGL2PaintEngineEx::QGL2PaintEngineEx()
+ : QPaintEngineEx(*(new QGL2PaintEngineExPrivate(this)))
+{
+ qDebug("QGL2PaintEngineEx::QGL2PaintEngineEx()");
+
+}
+
+QGL2PaintEngineEx::~QGL2PaintEngineEx()
+{
+}
+
+void QGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush)
+{
+ Q_D(QGL2PaintEngineEx);
+
+ QTime startTime = QTime::currentTime();
+
+ d->setBrush(&brush);
+ d->fill(path);
+ d->setBrush(&(state()->brush)); // reset back to the state's brush
+}
+
+void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
+{
+ Q_D(QGL2PaintEngineEx);
+
+ if (pen.style() == Qt::NoPen)
+ return;
+
+ if ( (pen.isCosmetic() && (pen.style() == Qt::SolidLine)) && (pen.widthF() < 2.5f) )
+ {
+ // We only handle solid, cosmetic pens with a width of 1 pixel
+ const QBrush& brush = pen.brush();
+ d->setBrush(&brush);
+
+ if (pen.widthF() < 0.01f)
+ glLineWidth(1.0);
+ else
+ glLineWidth(pen.widthF());
+
+ d->drawOutline(path);
+ d->setBrush(&(state()->brush));
+ } else
+ return QPaintEngineEx::stroke(path, pen);
+
+}
+
+void QGL2PaintEngineEx::penChanged()
+{
+// qDebug("QGL2PaintEngineEx::penChanged() not implemented!");
+}
+
+
+void QGL2PaintEngineEx::brushChanged()
+{
+// qDebug("QGL2PaintEngineEx::brushChanged()");
+ Q_D(QGL2PaintEngineEx);
+ d->setBrush(&(state()->brush));
+}
+
+void QGL2PaintEngineEx::brushOriginChanged()
+{
+// qDebug("QGL2PaintEngineEx::brushOriginChanged()");
+ Q_D(QGL2PaintEngineEx);
+ d->brushUniformsDirty = true;
+}
+
+void QGL2PaintEngineEx::opacityChanged()
+{
+// qDebug("QGL2PaintEngineEx::opacityChanged()");
+ Q_D(QGL2PaintEngineEx);
+
+ Q_ASSERT(d->shaderManager);
+ d->shaderManager->setUseGlobalOpacity(state()->opacity > 0.999);
+ d->brushUniformsDirty = true;
+}
+
+void QGL2PaintEngineEx::compositionModeChanged()
+{
+// qDebug("QGL2PaintEngineEx::compositionModeChanged()");
+ Q_D(QGL2PaintEngineEx);
+ d->compositionModeDirty = true;
+}
+
+void QGL2PaintEngineEx::renderHintsChanged()
+{
+// qDebug("QGL2PaintEngineEx::renderHintsChanged() not implemented!");
+}
+
+void QGL2PaintEngineEx::transformChanged()
+{
+ Q_D(QGL2PaintEngineEx);
+ d->matrixDirty = true;
+}
+
+
+void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, const QRectF & src)
+{
+ Q_D(QGL2PaintEngineEx);
+ glActiveTexture(QT_BRUSH_TEXTURE_UNIT);
+
+ d->ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, true);
+
+ //FIXME: we should use hasAlpha() instead, but that's SLOW at the moment
+ if ((state()->opacity < 0.99f) || pixmap.hasAlphaChannel())
+ glEnable(GL_BLEND);
+ else
+ glDisable(GL_BLEND);
+
+ d->drawTexture(dest, src, pixmap.width(), pixmap.height());
+}
+
+void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src,
+ Qt::ImageConversionFlags)
+{
+ Q_D(QGL2PaintEngineEx);
+ glActiveTexture(QT_BRUSH_TEXTURE_UNIT);
+ d->ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true);
+
+ if ((state()->opacity < 0.99f) || image.hasAlphaChannel())
+ glEnable(GL_BLEND);
+ else
+ glDisable(GL_BLEND);
+
+ d->drawTexture(dest, src, image.width(), image.height());
+}
+
+void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ QOpenGLPaintEngineState *s = state();
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+
+ bool drawCached = true;
+
+ if (state()->pen.brush().style() != Qt::SolidPattern)
+ drawCached = false;
+
+ if (s->matrix.type() > QTransform::TxTranslate)
+ drawCached = false;
+
+ // don't try to cache huge fonts
+ if (ti.fontEngine->fontDef.pixelSize * qSqrt(s->matrix.determinant()) >= 64)
+ drawCached = false;
+
+ if (drawCached) {
+ drawCachedGlyphs(p, ti);
+ return;
+ }
+
+ QPaintEngineEx::drawTextItem(p, ti);
+}
+
+void QGL2PaintEngineEx::drawCachedGlyphs(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_D(QGL2PaintEngineEx);
+ QOpenGLPaintEngineState *s = state();
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+ matrix.translate(p.x(), p.y());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+
+ QFontEngineGlyphCache::Type glyphType = ti.fontEngine->glyphFormat >= 0
+ ? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat)
+ : QFontEngineGlyphCache::Raster_A8;
+
+ QImageTextureGlyphCache *cache =
+ (QImageTextureGlyphCache *) ti.fontEngine->glyphCache(glyphType, s->matrix);
+ if (!cache) {
+ cache = new QImageTextureGlyphCache(glyphType, s->matrix);
+ ti.fontEngine->setGlyphCache(glyphType, cache);
+ }
+
+ cache->populate(ti, glyphs, positions);
+
+ const QImage &image = cache->image();
+ int margin = cache->glyphMargin();
+
+ glActiveTexture(QT_BRUSH_TEXTURE_UNIT);
+ d->ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true);
+
+ glEnable(GL_BLEND);
+
+ d->shaderManager->textShader()->use();
+ d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
+
+ if (d->compositionModeDirty)
+ d->updateCompositionMode();
+
+ if (d->matrixDirty)
+ d->updateMatrix();
+
+ if (d->textShaderMatrixUniformDirty) {
+ d->shaderManager->textShader()->uniforms()[QLatin1String("pmvMatrix")] = d->pmvMatrix;
+ d->textShaderMatrixUniformDirty = false;
+ }
+
+ d->shaderManager->textShader()->uniforms()[QLatin1String("textureSampler")] = QT_BRUSH_TEXTURE_UNIT;
+ QColor col = d->premultiplyColor(state()->pen.color(), (GLfloat)state()->opacity);
+ d->shaderManager->textShader()->uniforms()[QLatin1String("fragmentColor")] = col;
+
+ GLfloat dx = 1.0 / image.width();
+ GLfloat dy = 1.0 / image.height();
+
+ glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+ glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
+ for (int i=0; i<glyphs.size(); ++i) {
+ const QTextureGlyphCache::Coord &c = cache->coords.value(glyphs[i]);
+ int x = positions[i].x.toInt() + c.baseLineX - margin;
+ int y = positions[i].y.toInt() - c.baseLineY - margin;
+
+ QGLRect dest = QRectF(x, y, c.w, c.h);
+ QGLRect src = QRectF(c.x, c.y, c.w, c.h);
+
+ GLfloat vertexCoords[] = {
+ dest.left, dest.top,
+ dest.left, dest.bottom,
+ dest.right, dest.bottom,
+ dest.right, dest.top
+ };
+
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoords);
+
+ QGLRect srcTextureRect(src.left*dx, 1.0 - src.top*dy, src.right*dx, 1.0 - src.bottom*dy);
+
+ GLfloat textureCoords[] = {
+ srcTextureRect.left, srcTextureRect.top,
+ srcTextureRect.left, srcTextureRect.bottom,
+ srcTextureRect.right, srcTextureRect.bottom,
+ srcTextureRect.right, srcTextureRect.top
+ };
+
+ glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoords);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+ glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
+ glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+}
+
+bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
+{
+ Q_D(QGL2PaintEngineEx);
+
+// qDebug("QGL2PaintEngineEx::begin()");
+
+ QGLWidget* widget = static_cast<QGLWidget*>(pdev);
+ d->ctx = const_cast<QGLContext*>(widget->context());
+ d->ctx->makeCurrent();
+ d->width = widget->width();
+ d->height = widget->height();
+
+ if (!d->shaderManager)
+ d->shaderManager = new QGLPEXShaderManager(d->ctx);
+
+ glViewport(0, 0, d->width, d->height);
+
+// glClearColor(0.0, 1.0, 0.0, 1.0);
+// glClear(GL_COLOR_BUFFER_BIT);
+// d->ctx->swapBuffers();
+// qDebug("You should see green now");
+// sleep(5);
+
+ d->brushTextureDirty = true;
+ d->brushUniformsDirty = true;
+ d->matrixDirty = true;
+ d->compositionModeDirty = true;
+ d->stencilBuferDirty = true;
+
+ d->use_system_clip = !systemClip().isEmpty();
+
+ glDisable(GL_DEPTH_TEST);
+
+ return true;
+}
+
+bool QGL2PaintEngineEx::end()
+{
+ Q_D(QGL2PaintEngineEx);
+ d->ctx->swapBuffers();
+ return false;
+}
+
+
+/////////////////////////////////// State/Clipping stolen from QOpenGLPaintEngine //////////////////////////////////////////
+
+void QGL2PaintEngineEx::clipEnabledChanged()
+{
+ Q_D(QGL2PaintEngineEx);
+
+ d->updateDepthClip();
+}
+
+void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+// qDebug("QGL2PaintEngineEx::clip()");
+ 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 QGL2PaintEngineEx::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
+{
+// qDebug("QGL2PaintEngineEx::updateClipRegion()");
+ Q_D(QGL2PaintEngineEx);
+
+ QRegion sysClip = systemClip();
+ 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);
+ path = state()->matrix.map(path);
+
+// if (path.contains(QRectF(QPointF(), d->drawable.size())))
+// isScreenClip = true;
+ if (path.contains(QRectF(0.0, 0.0, d->width, d->height)))
+ isScreenClip = true;
+ }
+ }
+
+// QRegion region = isScreenClip ? QRegion() : clipRegion * d->matrix;
+ QRegion region = isScreenClip ? QRegion() : clipRegion * state()->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 && !sysClip.isEmpty())
+ state()->clipRegion = region & sysClip;
+ else
+ state()->clipRegion = region;
+ break;
+ case Qt::UniteClip:
+ state()->clipRegion |= region;
+ if (d->use_system_clip && !sysClip.isEmpty())
+ 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 QGL2PaintEngineExPrivate::updateDepthClip()
+{
+// qDebug("QGL2PaintEngineExPrivate::updateDepthClip()");
+
+ Q_Q(QGL2PaintEngineEx);
+
+ 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 = height - (fastClip.bottom() + 1);
+ const int height = fastClip.height();
+
+ glScissor(left, bottom, width, height);
+ return;
+ }
+
+ glClearDepthf(0x0);
+ glDepthMask(true);
+ glClear(GL_DEPTH_BUFFER_BIT);
+ glClearDepthf(0x1);
+
+ const QVector<QRect> rects = q->state()->clipEnabled ? q->state()->clipRegion.rects() : q->systemClip().rects();
+ glEnable(GL_SCISSOR_TEST);
+ for (int i = 0; i < rects.size(); ++i) {
+ QRect rect = rects.at(i);
+
+ const int left = rect.left();
+ const int width = rect.width();
+ const int bottom = height - (rect.bottom() + 1);
+ const int height = rect.height();
+
+ glScissor(left, bottom, width, height);
+
+ glClear(GL_DEPTH_BUFFER_BIT);
+ }
+ glDisable(GL_SCISSOR_TEST);
+
+ glDepthMask(false);
+ glDepthFunc(GL_LEQUAL);
+ glEnable(GL_DEPTH_TEST);
+}
+
+
+
+void QGL2PaintEngineEx::setState(QPainterState *s)
+{
+// qDebug("QGL2PaintEngineEx::setState()");
+
+ Q_D(QGL2PaintEngineEx);
+ QPaintEngineEx::setState(s);
+
+ d->updateDepthClip();
+
+ d->matrixDirty = true;
+ d->compositionModeDirty = true;
+ d->brushTextureDirty = true;
+ d->brushUniformsDirty = true;
+ d->simpleShaderMatrixUniformDirty = true;
+ d->brushShaderMatrixUniformDirty = true;
+ d->imageShaderMatrixUniformDirty = true;
+ d->textShaderMatrixUniformDirty = true;
+}
+
+QPainterState *QGL2PaintEngineEx::createState(QPainterState *orig) const
+{
+ QOpenGLPaintEngineState *s;
+ if (!orig)
+ s = new QOpenGLPaintEngineState();
+ else
+ s = new QOpenGLPaintEngineState(*static_cast<QOpenGLPaintEngineState *>(orig));
+
+ return s;
+}
+
+QOpenGLPaintEngineState::QOpenGLPaintEngineState(QOpenGLPaintEngineState &other)
+ : QPainterState(other)
+{
+ clipRegion = other.clipRegion;
+ hasClipping = other.hasClipping;
+ fastClip = other.fastClip;
+}
+
+QOpenGLPaintEngineState::QOpenGLPaintEngineState()
+{
+ hasClipping = false;
+}
+
+QOpenGLPaintEngineState::~QOpenGLPaintEngineState()
+{
+}
+
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
new file mode 100644
index 0000000000..ce66e4bb12
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 <private/qpaintengineex_p.h>
+
+class QGL2PaintEngineExPrivate;
+
+
+class QOpenGLPaintEngineState : public QPainterState
+{
+public:
+ QOpenGLPaintEngineState(QOpenGLPaintEngineState &other);
+ QOpenGLPaintEngineState();
+ ~QOpenGLPaintEngineState();
+
+ QRegion clipRegion;
+ bool hasClipping;
+ QRect fastClip;
+};
+
+
+class QGL2PaintEngineEx : public QPaintEngineEx
+{
+ Q_DECLARE_PRIVATE(QGL2PaintEngineEx)
+public:
+ QGL2PaintEngineEx();
+ ~QGL2PaintEngineEx();
+
+ bool begin(QPaintDevice *device);
+ bool end();
+
+ 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 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 drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ virtual void drawTextItem(const QPointF &p, const QTextItem &textItem);
+ void drawCachedGlyphs(const QPointF &p, const QTextItemInt &ti);
+
+ Type type() const { return OpenGL; }
+
+
+ // State stuff is just for clipping and ripped off from QGLPaintEngine
+ 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());
+ }
+ void updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op);
+
+private:
+ Q_DISABLE_COPY(QGL2PaintEngineEx)
+};
+
+
+
+#endif