summaryrefslogtreecommitdiffstats
path: root/src/opengl
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 10:18:55 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 10:18:55 +0100
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/opengl
Long live Qt 4.5!
Diffstat (limited to 'src/opengl')
-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
-rw-r--r--src/opengl/opengl.pro135
-rw-r--r--src/opengl/qegl.cpp787
-rw-r--r--src/opengl/qegl_p.h188
-rw-r--r--src/opengl/qegl_qws.cpp125
-rw-r--r--src/opengl/qegl_wince.cpp105
-rw-r--r--src/opengl/qegl_x11egl.cpp130
-rw-r--r--src/opengl/qgl.cpp4204
-rw-r--r--src/opengl/qgl.h566
-rw-r--r--src/opengl/qgl_cl_p.h141
-rw-r--r--src/opengl/qgl_egl.cpp131
-rw-r--r--src/opengl/qgl_egl_p.h68
-rw-r--r--src/opengl/qgl_mac.mm982
-rw-r--r--src/opengl/qgl_p.h394
-rw-r--r--src/opengl/qgl_qws.cpp364
-rw-r--r--src/opengl/qgl_win.cpp1499
-rw-r--r--src/opengl/qgl_wince.cpp739
-rw-r--r--src/opengl/qgl_x11.cpp1425
-rw-r--r--src/opengl/qgl_x11egl.cpp378
-rw-r--r--src/opengl/qglcolormap.cpp287
-rw-r--r--src/opengl/qglcolormap.h105
-rw-r--r--src/opengl/qglextensions.cpp185
-rw-r--r--src/opengl/qglextensions_p.h516
-rw-r--r--src/opengl/qglframebufferobject.cpp719
-rw-r--r--src/opengl/qglframebufferobject.h126
-rw-r--r--src/opengl/qglpaintdevice_qws.cpp98
-rw-r--r--src/opengl/qglpaintdevice_qws_p.h85
-rw-r--r--src/opengl/qglpixelbuffer.cpp582
-rw-r--r--src/opengl/qglpixelbuffer.h119
-rw-r--r--src/opengl/qglpixelbuffer_egl.cpp211
-rw-r--r--src/opengl/qglpixelbuffer_mac.mm338
-rw-r--r--src/opengl/qglpixelbuffer_p.h193
-rw-r--r--src/opengl/qglpixelbuffer_win.cpp390
-rw-r--r--src/opengl/qglpixelbuffer_x11.cpp301
-rw-r--r--src/opengl/qglpixmapfilter.cpp409
-rw-r--r--src/opengl/qglpixmapfilter_p.h123
-rw-r--r--src/opengl/qglscreen_qws.cpp242
-rw-r--r--src/opengl/qglscreen_qws.h127
-rw-r--r--src/opengl/qglwindowsurface_qws.cpp134
-rw-r--r--src/opengl/qglwindowsurface_qws_p.h90
-rw-r--r--src/opengl/qgraphicssystem_gl.cpp72
-rw-r--r--src/opengl/qgraphicssystem_gl_p.h74
-rw-r--r--src/opengl/qpaintengine_opengl.cpp5787
-rw-r--r--src/opengl/qpaintengine_opengl_p.h155
-rw-r--r--src/opengl/qpixmapdata_gl.cpp313
-rw-r--r--src/opengl/qpixmapdata_gl_p.h107
-rw-r--r--src/opengl/qwindowsurface_gl.cpp661
-rw-r--r--src/opengl/qwindowsurface_gl_p.h102
-rw-r--r--src/opengl/util/README-GLSL18
-rw-r--r--src/opengl/util/brush_painter.glsl7
-rw-r--r--src/opengl/util/brushes.conf6
-rw-r--r--src/opengl/util/composition_mode_colorburn.glsl13
-rw-r--r--src/opengl/util/composition_mode_colordodge.glsl15
-rw-r--r--src/opengl/util/composition_mode_darken.glsl9
-rw-r--r--src/opengl/util/composition_mode_difference.glsl9
-rw-r--r--src/opengl/util/composition_mode_exclusion.glsl9
-rw-r--r--src/opengl/util/composition_mode_hardlight.glsl14
-rw-r--r--src/opengl/util/composition_mode_lighten.glsl9
-rw-r--r--src/opengl/util/composition_mode_multiply.glsl9
-rw-r--r--src/opengl/util/composition_mode_overlay.glsl13
-rw-r--r--src/opengl/util/composition_mode_screen.glsl6
-rw-r--r--src/opengl/util/composition_mode_softlight.glsl18
-rw-r--r--src/opengl/util/composition_modes.conf12
-rw-r--r--src/opengl/util/conical_brush.glsl27
-rw-r--r--src/opengl/util/ellipse.glsl6
-rw-r--r--src/opengl/util/ellipse_aa.glsl6
-rw-r--r--src/opengl/util/ellipse_aa_copy.glsl11
-rw-r--r--src/opengl/util/ellipse_aa_radial.glsl24
-rw-r--r--src/opengl/util/ellipse_functions.glsl63
-rw-r--r--src/opengl/util/fast_painter.glsl19
-rw-r--r--src/opengl/util/fragmentprograms_p.h7372
-rw-r--r--src/opengl/util/generator.cpp435
-rw-r--r--src/opengl/util/generator.pro11
-rwxr-xr-xsrc/opengl/util/glsl_to_include.sh33
-rw-r--r--src/opengl/util/linear_brush.glsl22
-rw-r--r--src/opengl/util/masks.conf3
-rw-r--r--src/opengl/util/painter.glsl21
-rw-r--r--src/opengl/util/painter_nomask.glsl9
-rw-r--r--src/opengl/util/pattern_brush.glsl25
-rw-r--r--src/opengl/util/radial_brush.glsl28
-rw-r--r--src/opengl/util/simple_porter_duff.glsl16
-rw-r--r--src/opengl/util/solid_brush.glsl4
-rw-r--r--src/opengl/util/texture_brush.glsl23
-rw-r--r--src/opengl/util/trap_exact_aa.glsl58
94 files changed, 37128 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
diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro
new file mode 100644
index 0000000000..48d7caf350
--- /dev/null
+++ b/src/opengl/opengl.pro
@@ -0,0 +1,135 @@
+TARGET = QtOpenGL
+QPRO_PWD = $$PWD
+QT = core gui
+DEFINES += QT_BUILD_OPENGL_LIB
+DEFINES += QT_NO_USING_NAMESPACE
+win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x63000000
+solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2
+
+unix:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui
+
+include(../qbase.pri)
+
+!win32:!embedded:!mac:CONFIG += x11
+contains(QT_CONFIG, opengl):CONFIG += opengl
+contains(QT_CONFIG, opengles1):CONFIG += opengles1
+contains(QT_CONFIG, opengles2):CONFIG += opengles2
+
+!contains(QT_CONFIG, opengles2) {
+ HEADERS += qgraphicssystem_gl_p.h qwindowsurface_gl_p.h qpixmapdata_gl_p.h
+ SOURCES += qgraphicssystem_gl.cpp qwindowsurface_gl.cpp qpixmapdata_gl.cpp
+}
+
+HEADERS += qgl.h \
+ qgl_p.h \
+ qglcolormap.h \
+ qglpixelbuffer.h \
+ qglframebufferobject.h \
+ qglpixmapfilter_p.h
+
+SOURCES += qgl.cpp \
+ qglcolormap.cpp \
+ qglpixelbuffer.cpp \
+ qglframebufferobject.cpp \
+ qglextensions.cpp \
+ qglpixmapfilter.cpp
+
+!contains(QT_CONFIG, opengles2) {
+ HEADERS += qpaintengine_opengl_p.h
+ SOURCES += qpaintengine_opengl.cpp
+}
+
+contains(QT_CONFIG, opengles2) {
+ SOURCES += gl2paintengineex/qglgradientcache.cpp \
+ gl2paintengineex/qglpexshadermanager.cpp \
+ gl2paintengineex/qglshader.cpp \
+ gl2paintengineex/qgl2pexvertexarray.cpp \
+ gl2paintengineex/qpaintengineex_opengl2.cpp
+
+ HEADERS += gl2paintengineex/qglgradientcache_p.h \
+ gl2paintengineex/qglpexshadermanager_p.h \
+ gl2paintengineex/qglshader_p.h \
+ gl2paintengineex/qgl2pexvertexarray_p.h \
+ gl2paintengineex/qpaintengineex_opengl2_p.h
+}
+
+
+x11 {
+ contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, opengles1cl)|contains(QT_CONFIG, opengles2) {
+ SOURCES += qgl_x11egl.cpp \
+ qglpixelbuffer_egl.cpp \
+ qgl_egl.cpp \
+ qegl.cpp \
+ qegl_x11egl.cpp
+
+ HEADERS += qegl_p.h \
+ qgl_egl_p.h
+
+ } else {
+ SOURCES += qgl_x11.cpp \
+ qglpixelbuffer_x11.cpp
+ }
+
+ contains(QT_CONFIG, fontconfig) {
+ include($$QT_SOURCE_TREE/config.tests/unix/freetype/freetype.pri)
+ } else {
+ DEFINES *= QT_NO_FREETYPE
+ }
+}
+
+mac {
+ OBJECTIVE_SOURCES += qgl_mac.mm \
+ qglpixelbuffer_mac.mm
+ LIBS += -framework AppKit
+}
+win32:!wince*: {
+ SOURCES += qgl_win.cpp \
+ qglpixelbuffer_win.cpp
+}
+wince*: {
+ SOURCES += qgl_wince.cpp \
+ qglpixelbuffer_egl.cpp \
+ qgl_egl.cpp \
+ qegl.cpp \
+ qegl_wince.cpp
+
+ HEADERS += qgl_cl_p.h \
+ qgl_egl_p.h \
+ qegl_p.h
+}
+
+embedded {
+ SOURCES += qgl_qws.cpp \
+ qglpaintdevice_qws.cpp \
+ qglpixelbuffer_egl.cpp \
+ qglscreen_qws.cpp \
+ qglwindowsurface_qws.cpp \
+ qegl.cpp \
+ qegl_qws.cpp \
+ qgl_egl.cpp
+
+ HEADERS += qglpaintdevice_qws_p.h \
+ qglscreen_qws.h \
+ qglwindowsurface_qws_p.h \
+ qgl_egl_p.h \
+ qegl_p.h
+
+ contains(QT_CONFIG, fontconfig) {
+ include($$QT_SOURCE_TREE/config.tests/unix/freetype/freetype.pri)
+ } else {
+ DEFINES *= QT_NO_FREETYPE
+ }
+}
+
+INCLUDEPATH += ../3rdparty/harfbuzz/src
+
+wince*: {
+ contains(QT_CONFIG,opengles1) {
+ QMAKE_LIBS += "libGLES_CM.lib"
+ }
+ contains(QT_CONFIG,opengles1cl) {
+ QMAKE_LIBS += "libGLES_CL.lib"
+ }
+} else {
+ QMAKE_LIBS += $$QMAKE_LIBS_OPENGL
+}
diff --git a/src/opengl/qegl.cpp b/src/opengl/qegl.cpp
new file mode 100644
index 0000000000..165a0f3710
--- /dev/null
+++ b/src/opengl/qegl.cpp
@@ -0,0 +1,787 @@
+/****************************************************************************
+**
+** 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 <QtGui/qpaintdevice.h>
+#include <QtGui/qpixmap.h>
+#include <QtGui/qwidget.h>
+#include <QtCore/qdebug.h>
+#include "qegl_p.h"
+
+#if defined(QT_OPENGL_ES) || defined(QT_OPENVG)
+
+QT_BEGIN_NAMESPACE
+
+QEglContext::QEglContext()
+{
+ apiType = OpenGL;
+ dpy = EGL_NO_DISPLAY;
+ ctx = EGL_NO_CONTEXT;
+ surf = EGL_NO_SURFACE;
+ cfg = 0;
+ share = false;
+ reserved = 0;
+}
+
+QEglContext::~QEglContext()
+{
+ destroy();
+}
+
+bool QEglContext::isValid() const
+{
+ return (ctx != EGL_NO_CONTEXT);
+}
+
+bool QEglContext::isSharing() const
+{
+ return share;
+}
+
+// Open the EGL display associated with "device".
+bool QEglContext::openDisplay(QPaintDevice *device)
+{
+ if (dpy == EGL_NO_DISPLAY)
+ dpy = defaultDisplay(device);
+ return (dpy != EGL_NO_DISPLAY);
+}
+
+// Choose a configuration that matches "properties".
+bool QEglContext::chooseConfig
+ (const QEglProperties& properties, PixelFormatMatch match)
+{
+ QEglProperties props(properties);
+ EGLConfig *configs;
+ EGLint matching, size;
+ do {
+ // Get the number of matching configurations for this set of properties.
+ matching = 0;
+ if (!eglChooseConfig(dpy, props.properties(), 0, 256, &matching) || !matching)
+ continue;
+
+ // If we want the best pixel format, then return the first
+ // matching configuration.
+ if (match == BestPixelFormat) {
+ eglChooseConfig(dpy, props.properties(), &cfg, 1, &matching);
+ if (matching < 1)
+ continue;
+ return true;
+ }
+
+ // Fetch all of the matching configurations and find the
+ // first that matches the pixel format we wanted.
+ size = matching;
+ configs = new EGLConfig [size];
+ eglChooseConfig(dpy, props.properties(), configs, size, &matching);
+ for (EGLint index = 0; index < size; ++index) {
+ EGLint red, green, blue, alpha;
+ eglGetConfigAttrib(dpy, configs[index], EGL_RED_SIZE, &red);
+ eglGetConfigAttrib(dpy, configs[index], EGL_GREEN_SIZE, &green);
+ eglGetConfigAttrib(dpy, configs[index], EGL_BLUE_SIZE, &blue);
+ eglGetConfigAttrib(dpy, configs[index], EGL_ALPHA_SIZE, &alpha);
+ if (red == props.value(EGL_RED_SIZE) &&
+ green == props.value(EGL_GREEN_SIZE) &&
+ blue == props.value(EGL_BLUE_SIZE) &&
+ (props.value(EGL_ALPHA_SIZE) == EGL_DONT_CARE ||
+ alpha == props.value(EGL_ALPHA_SIZE))) {
+ cfg = configs[index];
+ delete [] configs;
+ return true;
+ }
+ }
+ delete [] configs;
+ } while (props.reduceConfiguration());
+
+#ifdef EGL_BIND_TO_TEXTURE_RGBA
+ // Don't report an error just yet if we failed to get a pbuffer
+ // configuration with texture rendering. Only report failure if
+ // we cannot get any pbuffer configurations at all.
+ if (props.value(EGL_BIND_TO_TEXTURE_RGBA) == EGL_DONT_CARE &&
+ props.value(EGL_BIND_TO_TEXTURE_RGB) == EGL_DONT_CARE)
+#endif
+ {
+ qWarning() << "QEglContext::chooseConfig(): Could not find a suitable EGL configuration";
+ qWarning() << "Requested:" << props.toString();
+ qWarning() << "Available:";
+ dumpAllConfigs();
+ }
+ return false;
+}
+
+// Create the EGLContext.
+bool QEglContext::createContext(QEglContext *shareContext)
+{
+ // We need to select the correct API before calling eglCreateContext().
+#ifdef EGL_OPENGL_ES_API
+ if (apiType == OpenGL)
+ eglBindAPI(EGL_OPENGL_ES_API);
+#endif
+#ifdef EGL_OPENVG_API
+ if (apiType == OpenVG)
+ eglBindAPI(EGL_OPENVG_API);
+#endif
+
+ // Create a new context for the configuration.
+ QEglProperties contextProps;
+#if defined(QT_OPENGL_ES_2)
+ if (apiType == OpenGL)
+ contextProps.setValue(EGL_CONTEXT_CLIENT_VERSION, 2);
+#endif
+ if (shareContext && shareContext->ctx == EGL_NO_CONTEXT)
+ shareContext = 0;
+ if (shareContext) {
+ ctx = eglCreateContext(dpy, cfg, shareContext->ctx, contextProps.properties());
+ if (ctx == EGL_NO_CONTEXT) {
+ qWarning() << "QEglContext::createContext(): Could not share context:" << errorString(eglGetError());
+ shareContext = 0;
+ }
+ }
+ if (ctx == EGL_NO_CONTEXT) {
+ ctx = eglCreateContext(dpy, cfg, 0, contextProps.properties());
+ if (ctx == EGL_NO_CONTEXT) {
+ qWarning() << "QEglContext::createContext(): Unable to create EGL context:" << errorString(eglGetError());
+ return false;
+ }
+ }
+ share = (shareContext != 0);
+ return true;
+}
+
+// Recreate the surface for a paint device because the native id has changed.
+bool QEglContext::recreateSurface(QPaintDevice *device)
+{
+ // Bail out if the surface has not been created for the first time yet.
+ if (surf == EGL_NO_SURFACE)
+ return true;
+
+ // Destroy the old surface.
+ eglDestroySurface(dpy, surf);
+
+ // Create a new one.
+ return createSurface(device);
+}
+
+void QEglContext::destroy()
+{
+ if (ctx != EGL_NO_CONTEXT)
+ eglDestroyContext(dpy, ctx);
+ dpy = EGL_NO_DISPLAY;
+ ctx = EGL_NO_CONTEXT;
+ surf = EGL_NO_SURFACE;
+ cfg = 0;
+ share = false;
+}
+
+bool QEglContext::makeCurrent()
+{
+ if(ctx == EGL_NO_CONTEXT) {
+ qWarning() << "QEglContext::makeCurrent(): Cannot make invalid context current";
+ return false;
+ }
+
+ bool ok = eglMakeCurrent(dpy, surf, surf, ctx);
+ if (!ok) {
+ EGLint err = eglGetError();
+ qWarning() << "QEglContext::makeCurrent():" << errorString(err);
+ }
+ return ok;
+}
+
+bool QEglContext::doneCurrent()
+{
+ // If the context is invalid, we assume that an error was reported
+ // when makeCurrent() was called.
+ if (ctx == EGL_NO_CONTEXT)
+ return false;
+
+ // We need to select the correct API before calling eglMakeCurrent()
+ // with EGL_NO_CONTEXT because threads can have both OpenGL and OpenVG
+ // contexts active at the same time.
+#ifdef EGL_OPENGL_ES_API
+ if (apiType == OpenGL)
+ eglBindAPI(EGL_OPENGL_ES_API);
+#endif
+#ifdef EGL_OPENVG_API
+ if (apiType == OpenVG)
+ eglBindAPI(EGL_OPENVG_API);
+#endif
+
+ bool ok = eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ if (!ok) {
+ EGLint err = eglGetError();
+ qWarning() << "QEglContext::doneCurrent():" << errorString(err);
+ }
+ return ok;
+}
+
+bool QEglContext::swapBuffers()
+{
+ if(ctx == EGL_NO_CONTEXT)
+ return false;
+
+ bool ok = eglSwapBuffers(dpy, surf);
+ if (!ok) {
+ EGLint err = eglGetError();
+ qWarning() << "QEglContext::swapBuffers():" << errorString(err);
+ }
+ return ok;
+}
+
+// Wait for native rendering operations to complete before starting
+// to use OpenGL/OpenVG operations.
+void QEglContext::waitNative()
+{
+#ifdef EGL_CORE_NATIVE_ENGINE
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+#endif
+}
+
+// Wait for client OpenGL/OpenVG operations to complete before
+// using native rendering operations.
+void QEglContext::waitClient()
+{
+#ifdef EGL_OPENGL_ES_API
+ if (apiType == OpenGL) {
+ eglBindAPI(EGL_OPENGL_ES_API);
+ eglWaitClient();
+ }
+#else
+ if (apiType == OpenGL)
+ eglWaitGL();
+#endif
+#ifdef EGL_OPENVG_API
+ if (apiType == OpenVG) {
+ eglBindAPI(EGL_OPENVG_API);
+ eglWaitClient();
+ }
+#endif
+}
+
+// Query the actual size of the EGL surface.
+QSize QEglContext::surfaceSize() const
+{
+ int w, h;
+ eglQuerySurface(dpy, surf, EGL_WIDTH, &w);
+ eglQuerySurface(dpy, surf, EGL_HEIGHT, &h);
+ return QSize(w, h);
+}
+
+// Query the value of a configuration attribute.
+bool QEglContext::configAttrib(int name, EGLint *value) const
+{
+ return eglGetConfigAttrib(dpy, cfg, name, value);
+}
+
+// Retrieve all of the properties on "cfg". If zero, return
+// the context's configuration.
+QEglProperties QEglContext::configProperties(EGLConfig cfg) const
+{
+ if (!cfg)
+ cfg = config();
+ QEglProperties props;
+ for (int name = 0x3020; name <= 0x304F; ++name) {
+ EGLint value;
+ if (name != EGL_NONE && eglGetConfigAttrib(dpy, cfg, name, &value))
+ props.setValue(name, value);
+ }
+ eglGetError(); // Clear the error state.
+ return props;
+}
+
+// Initialize and return the default display.
+EGLDisplay QEglContext::defaultDisplay(QPaintDevice *device)
+{
+ static EGLDisplay dpy = EGL_NO_DISPLAY;
+ if (dpy == EGL_NO_DISPLAY) {
+ dpy = getDisplay(device);
+ if (dpy == EGL_NO_DISPLAY) {
+ qWarning() << "QEglContext::defaultDisplay(): Cannot open EGL display";
+ return EGL_NO_DISPLAY;
+ }
+ if (!eglInitialize(dpy, NULL, NULL)) {
+ EGLint err = eglGetError();
+ qWarning() << "QEglContext::defaultDisplay(): Cannot initialize EGL display:" << errorString(err);
+ return EGL_NO_DISPLAY;
+ }
+#ifdef EGL_OPENGL_ES_API
+ eglBindAPI(EGL_OPENGL_ES_API);
+#endif
+ }
+ return dpy;
+}
+
+// Return the error string associated with a specific code.
+QString QEglContext::errorString(int code)
+{
+ static const char * const errors[] = {
+ "Success (0x3000)", // No tr
+ "Not initialized (0x3001)", // No tr
+ "Bad access (0x3002)", // No tr
+ "Bad alloc (0x3003)", // No tr
+ "Bad attribute (0x3004)", // No tr
+ "Bad config (0x3005)", // No tr
+ "Bad context (0x3006)", // No tr
+ "Bad current surface (0x3007)", // No tr
+ "Bad display (0x3008)", // No tr
+ "Bad match (0x3009)", // No tr
+ "Bad native pixmap (0x300A)", // No tr
+ "Bad native window (0x300B)", // No tr
+ "Bad parameter (0x300C)", // No tr
+ "Bad surface (0x300D)", // No tr
+ "Context lost (0x300E)" // No tr
+ };
+ if (code >= 0x3000 && code <= 0x300E) {
+ return QString::fromLatin1(errors[code - 0x3000]);
+ } else {
+ return QLatin1String("0x") + QString::number(code, 16);
+ }
+}
+
+// Dump all of the EGL configurations supported by the system.
+void QEglContext::dumpAllConfigs()
+{
+ QEglProperties props;
+ EGLint count = 0;
+ if (!eglGetConfigs(dpy, 0, 0, &count))
+ return;
+ if (count < 1)
+ return;
+ EGLConfig *configs = new EGLConfig [count];
+ eglGetConfigs(dpy, configs, count, &count);
+ for (EGLint index = 0; index < count; ++index) {
+ props = configProperties(configs[index]);
+ qWarning() << props.toString();
+ }
+ delete [] configs;
+}
+
+// Initialize a property block.
+QEglProperties::QEglProperties()
+{
+ props.append(EGL_NONE);
+}
+
+// Fetch the current value associated with a property.
+int QEglProperties::value(int name) const
+{
+ for (int index = 0; index < (props.size() - 1); index += 2) {
+ if (props[index] == name)
+ return props[index + 1];
+ }
+ return EGL_DONT_CARE;
+}
+
+// Set the value associated with a property, replacing an existing
+// value if there is one.
+void QEglProperties::setValue(int name, int value)
+{
+ for (int index = 0; index < (props.size() - 1); index += 2) {
+ if (props[index] == name) {
+ props[index + 1] = value;
+ return;
+ }
+ }
+ props[props.size() - 1] = name;
+ props.append(value);
+ props.append(EGL_NONE);
+}
+
+// Remove a property value. Returns false if the property is not present.
+bool QEglProperties::removeValue(int name)
+{
+ for (int index = 0; index < (props.size() - 1); index += 2) {
+ if (props[index] == name) {
+ while ((index + 2) < props.size()) {
+ props[index] = props[index + 2];
+ ++index;
+ }
+ props.resize(props.size() - 2);
+ return true;
+ }
+ }
+ return false;
+}
+
+// Sets the red, green, blue, and alpha sizes based on a pixel format.
+// Normally used to match a configuration request to the screen format.
+void QEglProperties::setPixelFormat(QImage::Format pixelFormat)
+{
+ int red, green, blue, alpha;
+ switch (pixelFormat) {
+ case QImage::Format_RGB32:
+ case QImage::Format_RGB888:
+ red = green = blue = 8; alpha = 0; break;
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ red = green = blue = alpha = 8; break;
+ case QImage::Format_RGB16:
+ red = 5; green = 6; blue = 5; alpha = 0; break;
+ case QImage::Format_ARGB8565_Premultiplied:
+ red = 5; green = 6; blue = 5; alpha = 8; break;
+ case QImage::Format_RGB666:
+ red = green = blue = 6; alpha = 0; break;
+ case QImage::Format_ARGB6666_Premultiplied:
+ red = green = blue = alpha = 6; break;
+ case QImage::Format_RGB555:
+ red = green = blue = 5; alpha = 0; break;
+ case QImage::Format_ARGB8555_Premultiplied:
+ red = green = blue = 5; alpha = 8; break;
+ case QImage::Format_RGB444:
+ red = green = blue = 4; alpha = 0; break;
+ case QImage::Format_ARGB4444_Premultiplied:
+ red = green = blue = alpha = 4; break;
+ default:
+ qWarning() << "QEglProperties::setPixelFormat(): Unsupported pixel format";
+ red = green = blue = alpha = 1; break;
+ }
+ setValue(EGL_RED_SIZE, red);
+ setValue(EGL_GREEN_SIZE, green);
+ setValue(EGL_BLUE_SIZE, blue);
+ setValue(EGL_ALPHA_SIZE, alpha);
+}
+
+void QEglProperties::setRenderableType(int api)
+{
+#if defined(EGL_RENDERABLE_TYPE)
+#if defined(QT_OPENGL_ES_2)
+ if (api == QEglContext::OpenGL)
+ setValue(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT);
+#elif defined(QT_OPENGL_ES)
+ if (api == QEglContext::OpenGL)
+ setValue(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT);
+#endif
+#if defined(EGL_OPENVG_BIT)
+ if (api == QEglContext::OpenVG)
+ setValue(EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT);
+#endif
+#else
+ Q_UNUSED(api);
+#endif
+}
+
+// Reduce the complexity of a configuration request to ask for less
+// because the previous request did not result in success. Returns
+// true if the complexity was reduced, or false if no further
+// reductions in complexity are possible.
+bool QEglProperties::reduceConfiguration()
+{
+ if (removeValue(EGL_SAMPLE_BUFFERS)) {
+ removeValue(EGL_SAMPLES);
+ return true;
+ }
+ if (removeValue(EGL_ALPHA_SIZE))
+ return true;
+ if (removeValue(EGL_STENCIL_SIZE))
+ return true;
+ if (removeValue(EGL_DEPTH_SIZE))
+ return true;
+ return false;
+}
+
+static void addTag(QString& str, const QString& tag)
+{
+ int lastnl = str.lastIndexOf(QLatin1String("\n"));
+ if (lastnl == -1)
+ lastnl = 0;
+ if ((str.length() - lastnl) >= 50)
+ str += QLatin1String("\n ");
+ str += tag;
+}
+
+// Convert a property list to a string suitable for debug output.
+QString QEglProperties::toString() const
+{
+ QString str;
+ int val;
+
+ val = value(EGL_CONFIG_ID);
+ if (val != EGL_DONT_CARE) {
+ str += QLatin1String("id=");
+ str += QString::number(val);
+ str += QLatin1String(" ");
+ }
+
+#ifdef EGL_RENDERABLE_TYPE
+ val = value(EGL_RENDERABLE_TYPE);
+ if (val != EGL_DONT_CARE) {
+ str += QLatin1String("type=");
+ QStringList types;
+ if ((val & EGL_OPENGL_ES_BIT) != 0)
+ types += QLatin1String("es1");
+#ifdef EGL_OPENGL_ES2_BIT
+ if ((val & EGL_OPENGL_ES2_BIT) != 0)
+ types += QLatin1String("es2");
+#endif
+ if ((val & EGL_OPENVG_BIT) != 0)
+ types += QLatin1String("vg");
+ if ((val & ~7) != 0)
+ types += QString::number(val);
+ str += types.join(QLatin1String(","));
+ } else {
+ str += QLatin1String("type=any");
+ }
+#else
+ str += QLatin1String("type=es1");
+#endif
+
+ int red = value(EGL_RED_SIZE);
+ int green = value(EGL_GREEN_SIZE);
+ int blue = value(EGL_BLUE_SIZE);
+ int alpha = value(EGL_ALPHA_SIZE);
+ int bufferSize = value(EGL_BUFFER_SIZE);
+ if (bufferSize == (red + green + blue + alpha))
+ bufferSize = EGL_DONT_CARE;
+ str += QLatin1String(" rgba=");
+ str += QString::number(red);
+ str += QLatin1String(",");
+ str += QString::number(green);
+ str += QLatin1String(",");
+ str += QString::number(blue);
+ str += QLatin1String(",");
+ str += QString::number(alpha);
+ if (bufferSize != EGL_DONT_CARE) {
+ // Only report buffer size if different than r+g+b+a.
+ str += QLatin1String(" buffer-size=");
+ str += QString::number(bufferSize);
+ }
+
+#ifdef EGL_COLOR_BUFFER_TYPE
+ val = value(EGL_COLOR_BUFFER_TYPE);
+ if (val == EGL_LUMINANCE_BUFFER) {
+ addTag(str, QLatin1String(" color-buffer-type=luminance"));
+ } else if (val != EGL_DONT_CARE && val != EGL_RGB_BUFFER) {
+ addTag(str, QLatin1String(" color-buffer-type="));
+ str += QString::number(val, 16);
+ }
+#endif
+
+ val = value(EGL_DEPTH_SIZE);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" depth="));
+ str += QString::number(val);
+ }
+
+ val = value(EGL_STENCIL_SIZE);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" stencil="));
+ str += QString::number(val);
+ }
+
+ val = value(EGL_SURFACE_TYPE);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" surface-type="));
+ QStringList types;
+ if ((val & EGL_WINDOW_BIT) != 0)
+ types += QLatin1String("window");
+ if ((val & EGL_PIXMAP_BIT) != 0)
+ types += QLatin1String("pixmap");
+ if ((val & EGL_PBUFFER_BIT) != 0)
+ types += QLatin1String("pbuffer");
+#ifdef EGL_VG_COLORSPACE_LINEAR_BIT
+ if ((val & EGL_VG_COLORSPACE_LINEAR_BIT) != 0)
+ types += QLatin1String("vg-colorspace-linear");
+#endif
+#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT
+ if ((val & EGL_VG_ALPHA_FORMAT_PRE_BIT) != 0)
+ types += QLatin1String("vg-alpha-format-pre");
+#endif
+ if ((val & ~(EGL_WINDOW_BIT | EGL_PIXMAP_BIT | EGL_PBUFFER_BIT
+#ifdef EGL_VG_COLORSPACE_LINEAR_BIT
+ | EGL_VG_COLORSPACE_LINEAR_BIT
+#endif
+#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT
+ | EGL_VG_ALPHA_FORMAT_PRE_BIT
+#endif
+ )) != 0) {
+ types += QString::number(val);
+ }
+ str += types.join(QLatin1String(","));
+ }
+
+ val = value(EGL_CONFIG_CAVEAT);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" caveat="));
+ if (val == EGL_NONE)
+ str += QLatin1String("none");
+ else if (val == EGL_SLOW_CONFIG)
+ str += QLatin1String("slow");
+ else if (val == EGL_NON_CONFORMANT_CONFIG)
+ str += QLatin1String("non-conformant");
+ else
+ str += QString::number(val, 16);
+ }
+
+ val = value(EGL_LEVEL);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" level="));
+ str += QString::number(val);
+ }
+
+ int width, height, pixels;
+ width = value(EGL_MAX_PBUFFER_WIDTH);
+ height = value(EGL_MAX_PBUFFER_HEIGHT);
+ pixels = value(EGL_MAX_PBUFFER_PIXELS);
+ if (height != EGL_DONT_CARE || width != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" max-pbuffer-size="));
+ str += QString::number(width);
+ str += QLatin1String("x");
+ str += QString::number(height);
+ if (pixels != (width * height)) {
+ addTag(str, QLatin1String(" max-pbuffer-pixels="));
+ str += QString::number(pixels);
+ }
+ }
+
+ val = value(EGL_NATIVE_RENDERABLE);
+ if (val != EGL_DONT_CARE) {
+ if (val)
+ addTag(str, QLatin1String(" native-renderable=true"));
+ else
+ addTag(str, QLatin1String(" native-renderable=false"));
+ }
+
+ val = value(EGL_NATIVE_VISUAL_ID);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" visual-id="));
+ str += QString::number(val);
+ }
+
+ val = value(EGL_NATIVE_VISUAL_TYPE);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" visual-type="));
+ str += QString::number(val);
+ }
+
+#ifdef EGL_PRESERVED_RESOURCES
+ val = value(EGL_PRESERVED_RESOURCES);
+ if (val != EGL_DONT_CARE) {
+ if (val)
+ addTag(str, QLatin1String(" preserved-resources=true"));
+ else
+ addTag(str, QLatin1String(" preserved-resources=false"));
+ }
+#endif
+
+ val = value(EGL_SAMPLES);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" samples="));
+ str += QString::number(val);
+ }
+
+ val = value(EGL_SAMPLE_BUFFERS);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" sample-buffers="));
+ str += QString::number(val);
+ }
+
+ val = value(EGL_TRANSPARENT_TYPE);
+ if (val == EGL_TRANSPARENT_RGB) {
+ addTag(str, QLatin1String(" transparent-rgb="));
+ str += QString::number(value(EGL_TRANSPARENT_RED_VALUE));
+ str += QLatin1String(",");
+ str += QString::number(value(EGL_TRANSPARENT_GREEN_VALUE));
+ str += QLatin1String(",");
+ str += QString::number(value(EGL_TRANSPARENT_BLUE_VALUE));
+ }
+
+#if defined(EGL_BIND_TO_TEXTURE_RGB) && defined(EGL_BIND_TO_TEXTURE_RGBA)
+ val = value(EGL_BIND_TO_TEXTURE_RGB);
+ int val2 = value(EGL_BIND_TO_TEXTURE_RGBA);
+ if (val != EGL_DONT_CARE || val2 != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" bind-texture="));
+ if (val == EGL_TRUE)
+ str += QLatin1String("rgb");
+ else
+ str += QLatin1String("no-rgb");
+ if (val2 == EGL_TRUE)
+ str += QLatin1String(",rgba");
+ else
+ str += QLatin1String(",no-rgba");
+ }
+#endif
+
+#ifdef EGL_MIN_SWAP_INTERVAL
+ val = value(EGL_MIN_SWAP_INTERVAL);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" min-swap-interval="));
+ str += QString::number(val);
+ }
+#endif
+
+#ifdef EGL_MIN_SWAP_INTERVAL
+ val = value(EGL_MAX_SWAP_INTERVAL);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" max-swap-interval="));
+ str += QString::number(val);
+ }
+#endif
+
+#ifdef EGL_LUMINANCE_SIZE
+ val = value(EGL_LUMINANCE_SIZE);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" luminance="));
+ str += QString::number(val);
+ }
+#endif
+
+#ifdef EGL_ALPHA_MASK_SIZE
+ val = value(EGL_ALPHA_MASK_SIZE);
+ if (val != EGL_DONT_CARE) {
+ addTag(str, QLatin1String(" alpha-mask="));
+ str += QString::number(val);
+ }
+#endif
+
+#ifdef EGL_CONFORMANT
+ val = value(EGL_CONFORMANT);
+ if (val != EGL_DONT_CARE) {
+ if (val)
+ addTag(str, QLatin1String(" conformant=true"));
+ else
+ addTag(str, QLatin1String(" conformant=false"));
+ }
+#endif
+
+ return str;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_OPENGL_ES || QT_OPENVG
diff --git a/src/opengl/qegl_p.h b/src/opengl/qegl_p.h
new file mode 100644
index 0000000000..76cb573c2c
--- /dev/null
+++ b/src/opengl/qegl_p.h
@@ -0,0 +1,188 @@
+/****************************************************************************
+**
+** 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 QEGL_P_H
+#define QEGL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QGLWidget class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qvarlengtharray.h"
+#include "QtCore/qsize.h"
+#include "QtGui/qimage.h"
+
+#if defined(QT_OPENGL_ES) || defined(QT_OPENVG)
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#if defined(QT_OPENGL_ES_2) || defined(QT_OPENVG)
+#include <EGL/egl.h>
+#else
+#include <GLES/egl.h>
+#endif
+#if !defined(EGL_VERSION_1_3) && !defined(QEGL_NATIVE_TYPES_DEFINED)
+#undef EGLNativeWindowType
+#undef EGLNativePixmapType
+#undef EGLNativeDisplayType
+typedef NativeWindowType EGLNativeWindowType;
+typedef NativePixmapType EGLNativePixmapType;
+typedef NativeDisplayType EGLNativeDisplayType;
+#define QEGL_NATIVE_TYPES_DEFINED 1
+#endif
+QT_END_INCLUDE_NAMESPACE
+
+class QX11Info;
+class QPaintDevice;
+class QImage;
+class QPixmap;
+class QWidget;
+
+QT_BEGIN_NAMESPACE
+
+class Q_OPENGL_EXPORT QEglProperties
+{
+public:
+ QEglProperties();
+ QEglProperties(const QEglProperties& other) : props(other.props) {}
+ ~QEglProperties() {}
+
+ int value(int name) const;
+ void setValue(int name, int value);
+ bool removeValue(int name);
+
+ const int *properties() const { return props.constData(); }
+
+ void setPixelFormat(QImage::Format pixelFormat);
+#ifdef Q_WS_X11
+ void setVisualFormat(const QX11Info *xinfo);
+#endif
+ void setRenderableType(int api);
+
+ bool reduceConfiguration();
+
+ QString toString() const;
+
+private:
+ QVarLengthArray<int> props;
+};
+
+class Q_OPENGL_EXPORT QEglContext
+{
+public:
+ QEglContext();
+ ~QEglContext();
+
+ enum API
+ {
+ OpenGL,
+ OpenVG
+ };
+
+ enum PixelFormatMatch
+ {
+ ExactPixelFormat,
+ BestPixelFormat
+ };
+
+ bool isValid() const;
+ bool isSharing() const;
+
+ void setApi(QEglContext::API api) { apiType = api; }
+ bool openDisplay(QPaintDevice *device);
+ bool chooseConfig(const QEglProperties& properties, PixelFormatMatch match = ExactPixelFormat);
+ bool createContext(QEglContext *shareContext = 0);
+ bool createSurface(QPaintDevice *device);
+ bool recreateSurface(QPaintDevice *device);
+ void setSurface(EGLSurface surface) { surf = surface; }
+
+ void destroy();
+
+ bool makeCurrent();
+ bool doneCurrent();
+ bool swapBuffers();
+
+ void waitNative();
+ void waitClient();
+
+ QSize surfaceSize() const;
+
+ bool configAttrib(int name, EGLint *value) const;
+
+ void clearError() const { eglGetError(); }
+
+ QEglContext::API api() const { return apiType; }
+
+ EGLDisplay display() const { return dpy; }
+ EGLContext context() const { return ctx; }
+ EGLSurface surface() const { return surf; }
+ EGLConfig config() const { return cfg; }
+
+ QEglProperties configProperties(EGLConfig cfg = 0) const;
+
+ static EGLDisplay defaultDisplay(QPaintDevice *device);
+ static QString errorString(int code);
+
+ void dumpAllConfigs();
+
+private:
+ QEglContext::API apiType;
+ EGLDisplay dpy;
+ EGLContext ctx;
+ EGLSurface surf;
+ EGLConfig cfg;
+ bool share;
+ void *reserved; // For extension data in future versions.
+
+ static EGLDisplay getDisplay(QPaintDevice *device);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_OPENGL_ES || QT_OPENVG
+
+#endif // QEGL_P_H
diff --git a/src/opengl/qegl_qws.cpp b/src/opengl/qegl_qws.cpp
new file mode 100644
index 0000000000..9135c534eb
--- /dev/null
+++ b/src/opengl/qegl_qws.cpp
@@ -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$
+**
+****************************************************************************/
+
+#include <QtGui/qpaintdevice.h>
+#include <QtGui/qpixmap.h>
+#include <QtGui/qwidget.h>
+#include "qegl_p.h"
+
+#if defined(QT_OPENGL_ES) || defined(QT_OPENVG)
+
+#include <qglscreen_qws.h>
+#include <qscreenproxy_qws.h>
+#include <private/qglwindowsurface_qws_p.h>
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+
+QT_BEGIN_NAMESPACE
+
+static QGLScreen *glScreenForDevice(QPaintDevice *device)
+{
+ QScreen *screen = qt_screen;
+ if (screen->classId() == QScreen::MultiClass) {
+ int screenNumber;
+ if (device && device->devType() == QInternal::Widget)
+ screenNumber = qApp->desktop()->screenNumber(static_cast<QWidget *>(device));
+ else
+ screenNumber = 0;
+ screen = screen->subScreens()[screenNumber];
+ }
+ while (screen->classId() == QScreen::ProxyClass) {
+ screen = static_cast<QProxyScreen *>(screen)->screen();
+ }
+ if (screen->classId() == QScreen::GLClass)
+ return static_cast<QGLScreen *>(screen);
+ else
+ return 0;
+}
+
+// Create the surface for a QPixmap, QImage, or QWidget.
+bool QEglContext::createSurface(QPaintDevice *device)
+{
+ // Get the screen surface functions, which are used to create native ids.
+ QGLScreen *glScreen = glScreenForDevice(device);
+ if (!glScreen)
+ return false;
+ QGLScreenSurfaceFunctions *funcs = glScreen->surfaceFunctions();
+ if (!funcs)
+ return false;
+
+ // Create the native drawable for the paint device.
+ int devType = device->devType();
+ EGLNativePixmapType pixmapDrawable = 0;
+ EGLNativeWindowType windowDrawable = 0;
+ bool ok;
+ if (devType == QInternal::Pixmap) {
+ ok = funcs->createNativePixmap(static_cast<QPixmap *>(device), &pixmapDrawable);
+ } else if (devType == QInternal::Image) {
+ ok = funcs->createNativeImage(static_cast<QImage *>(device), &pixmapDrawable);
+ } else {
+ ok = funcs->createNativeWindow(static_cast<QWidget *>(device), &windowDrawable);
+ }
+ if (!ok) {
+ qWarning("QEglContext::createSurface(): Cannot create the native EGL drawable");
+ return false;
+ }
+
+ // Create the EGL surface to draw into, based on the native drawable.
+ if (devType == QInternal::Widget)
+ surf = eglCreateWindowSurface(dpy, cfg, windowDrawable, 0);
+ else
+ surf = eglCreatePixmapSurface(dpy, cfg, pixmapDrawable, 0);
+ if (surf == EGL_NO_SURFACE) {
+ qWarning("QEglContext::createSurface(): Unable to create EGL surface, error = 0x%x", eglGetError());
+ return false;
+ }
+ return true;
+}
+
+EGLDisplay QEglContext::getDisplay(QPaintDevice *device)
+{
+ Q_UNUSED(device);
+ return eglGetDisplay(EGLNativeDisplayType(EGL_DEFAULT_DISPLAY));
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_OPENGL_ES || QT_OPENVG
diff --git a/src/opengl/qegl_wince.cpp b/src/opengl/qegl_wince.cpp
new file mode 100644
index 0000000000..dbec385863
--- /dev/null
+++ b/src/opengl/qegl_wince.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** 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 <QtGui/qpaintdevice.h>
+#include <QtGui/qpixmap.h>
+#include <QtGui/qwidget.h>
+#include "qegl_p.h"
+
+#if defined(QT_OPENGL_ES) || defined(QT_OPENVG)
+
+#include <windows.h>
+
+
+QT_BEGIN_NAMESPACE
+
+bool QEglContext::createSurface(QPaintDevice *device)
+{
+ // Create the native drawable for the paint device.
+ int devType = device->devType();
+ EGLNativePixmapType pixmapDrawable = 0;
+ EGLNativeWindowType windowDrawable = 0;
+ bool ok;
+ if (devType == QInternal::Pixmap) {
+ pixmapDrawable = 0;
+ ok = (pixmapDrawable != 0);
+ } else if (devType == QInternal::Widget) {
+ windowDrawable = (EGLNativeWindowType)(static_cast<QWidget *>(device))->winId();
+ ok = (windowDrawable != 0);
+ } else {
+ ok = false;
+ }
+ if (!ok) {
+ qWarning("QEglContext::createSurface(): Cannot create the native EGL drawable");
+ return false;
+ }
+
+ // Create the EGL surface to draw into, based on the native drawable.
+ if (devType == QInternal::Widget)
+ surf = eglCreateWindowSurface(dpy, cfg, windowDrawable, 0);
+ else
+ surf = eglCreatePixmapSurface(dpy, cfg, pixmapDrawable, 0);
+ if (surf == EGL_NO_SURFACE) {
+ qWarning("QEglContext::createSurface(): Unable to create EGL surface, error = 0x%x", eglGetError());
+ return false;
+ }
+ return true;
+}
+
+EGLDisplay QEglContext::getDisplay(QPaintDevice *device)
+{
+ EGLDisplay dpy = 0;
+ HWND win = ((QWidget*)device)->winId();
+ HDC myDc = GetDC(win);
+ if (!myDc) {
+ qWarning("QEglContext::defaultDisplay(): WinCE display is not open");
+ }
+ dpy = eglGetDisplay(EGLNativeDisplayType(myDc));
+ if (dpy == EGL_NO_DISPLAY) {
+ qWarning("QEglContext::defaultDisplay(): Falling back to EGL_DEFAULT_DISPLAY");
+ dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ }
+ return dpy;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_OPENGL_ES || QT_OPENVG
diff --git a/src/opengl/qegl_x11egl.cpp b/src/opengl/qegl_x11egl.cpp
new file mode 100644
index 0000000000..8efe7e730b
--- /dev/null
+++ b/src/opengl/qegl_x11egl.cpp
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** 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 <QtGui/qpaintdevice.h>
+#include <QtGui/qpixmap.h>
+#include <QtGui/qwidget.h>
+#include "qegl_p.h"
+
+#if defined(QT_OPENGL_ES) || defined(QT_OPENVG)
+
+#if defined(Q_WS_X11)
+#include <QtGui/qx11info_x11.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+bool QEglContext::createSurface(QPaintDevice *device)
+{
+ // Create the native drawable for the paint device.
+ int devType = device->devType();
+ EGLNativePixmapType pixmapDrawable = 0;
+ EGLNativeWindowType windowDrawable = 0;
+ bool ok;
+ if (devType == QInternal::Pixmap) {
+ pixmapDrawable = (EGLNativePixmapType)(static_cast<QPixmap *>(device))->handle();
+ ok = (pixmapDrawable != 0);
+ } else if (devType == QInternal::Widget) {
+ windowDrawable = (EGLNativeWindowType)(static_cast<QWidget *>(device))->winId();
+ ok = (windowDrawable != 0);
+ } else {
+ ok = false;
+ }
+ if (!ok) {
+ qWarning("QEglContext::createSurface(): Cannot create the native EGL drawable");
+ return false;
+ }
+
+ // Create the EGL surface to draw into, based on the native drawable.
+ if (devType == QInternal::Widget)
+ surf = eglCreateWindowSurface(dpy, cfg, windowDrawable, 0);
+ else
+ surf = eglCreatePixmapSurface(dpy, cfg, pixmapDrawable, 0);
+ if (surf == EGL_NO_SURFACE) {
+ qWarning("QEglContext::createSurface(): Unable to create EGL surface, error = 0x%x", eglGetError());
+ return false;
+ }
+ return true;
+}
+
+EGLDisplay QEglContext::getDisplay(QPaintDevice *device)
+{
+ Q_UNUSED(device);
+ Display *xdpy = QX11Info::display();
+ if (!xdpy) {
+ qWarning("QEglContext::getDisplay(): X11 display is not open");
+ return EGL_NO_DISPLAY;
+ }
+ return eglGetDisplay(EGLNativeDisplayType(xdpy));
+}
+
+static int countBits(unsigned long mask)
+{
+ int count = 0;
+ while (mask != 0) {
+ if (mask & 1)
+ ++count;
+ mask >>= 1;
+ }
+ return count;
+}
+
+// Set the pixel format parameters from the visual in "xinfo".
+void QEglProperties::setVisualFormat(const QX11Info *xinfo)
+{
+ if (!xinfo)
+ return;
+ Visual *visual = (Visual*)xinfo->visual();
+ if (!visual)
+ return;
+ if (visual->c_class != TrueColor && visual->c_class != DirectColor)
+ return;
+ setValue(EGL_RED_SIZE, countBits(visual->red_mask));
+ setValue(EGL_GREEN_SIZE, countBits(visual->green_mask));
+ setValue(EGL_BLUE_SIZE, countBits(visual->blue_mask));
+ setValue(EGL_ALPHA_SIZE, 0); // XXX
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_OPENGL_ES || QT_OPENVG
diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp
new file mode 100644
index 0000000000..2d903424bb
--- /dev/null
+++ b/src/opengl/qgl.cpp
@@ -0,0 +1,4204 @@
+/****************************************************************************
+**
+** 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 "qapplication.h"
+#include "qplatformdefs.h"
+#include "qgl.h"
+#include <qdebug.h>
+
+#if defined(Q_WS_X11)
+#include "private/qt_x11_p.h"
+#include "private/qpixmap_x11_p.h"
+#define INT32 dummy_INT32
+#define INT8 dummy_INT8
+#if !defined(QT_OPENGL_ES)
+# include <GL/glx.h>
+#endif
+#undef INT32
+#undef INT8
+#include "qx11info_x11.h"
+#elif defined(Q_WS_MAC)
+# include <private/qt_mac_p.h>
+#endif
+
+#include <stdlib.h> // malloc
+
+#include "qpixmap.h"
+#include "qimage.h"
+#include "qgl_p.h"
+
+#if defined(QT_OPENGL_ES_2)
+#include "gl2paintengineex/qpaintengineex_opengl2_p.h"
+#else
+#include <private/qpaintengine_opengl_p.h>
+#endif
+
+#include <private/qimage_p.h>
+#include <private/qpixmapdata_p.h>
+#include <private/qpixmapdata_gl_p.h>
+#include "qcolormap.h"
+#include "qcache.h"
+#include "qfile.h"
+#include "qlibrary.h"
+
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_OPENGL_ES_1_CL
+#include "qgl_cl_p.h"
+#endif
+
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS)
+QGLExtensionFuncs QGLContextPrivate::qt_extensionFuncs;
+#endif
+
+QThreadStorage<QGLThreadContext *> qgl_context_storage;
+
+Q_GLOBAL_STATIC(QGLFormat, qgl_default_format)
+
+class QGLDefaultOverlayFormat: public QGLFormat
+{
+public:
+ inline QGLDefaultOverlayFormat()
+ {
+ setOption(QGL::FormatOption(0xffff << 16)); // turn off all options
+ setOption(QGL::DirectRendering);
+ setPlane(1);
+ }
+};
+Q_GLOBAL_STATIC(QGLDefaultOverlayFormat, defaultOverlayFormatInstance)
+
+QGLExtensions::Extensions QGLExtensions::glExtensions = 0;
+bool QGLExtensions::nvidiaFboNeedsFinish = false;
+
+#ifndef APIENTRY
+# define APIENTRY
+#endif
+typedef void (APIENTRY *pfn_glCompressedTexImage2DARB) (GLenum, GLint, GLenum, GLsizei,
+ GLsizei, GLint, GLsizei, const GLvoid *);
+static pfn_glCompressedTexImage2DARB qt_glCompressedTexImage2DARB = 0;
+
+
+#ifndef APIENTRY
+#define APIENTRY
+#endif
+
+Q_GLOBAL_STATIC(QGLSignalProxy, theSignalProxy)
+QGLSignalProxy *QGLSignalProxy::instance()
+{
+ return theSignalProxy();
+}
+
+/*!
+ \namespace QGL
+
+ \brief The QGL namespace specifies miscellaneous identifiers used
+ in the Qt OpenGL module.
+
+ \ingroup multimedia
+*/
+
+/*!
+ \enum QGL::FormatOption
+
+ This enum specifies the format options that can be used to configure an OpenGL
+ context. These are set using QGLFormat::setOption().
+
+ \value DoubleBuffer Specifies the use of double buffering.
+ \value DepthBuffer Enables the use of a depth buffer.
+ \value Rgba Specifies that the context should use RGBA as its pixel format.
+ \value AlphaChannel Enables the use of an alpha channel.
+ \value AccumBuffer Enables the use of an accumulation buffer.
+ \value StencilBuffer Enables the use of a stencil buffer.
+ \value StereoBuffers Enables the use of a stereo buffers for use with visualization hardware.
+ \value DirectRendering Specifies that the context is used for direct rendering to a display.
+ \value HasOverlay Enables the use of an overlay.
+ \value SampleBuffers Enables the use of sample buffers.
+ \value SingleBuffer Specifies the use of a single buffer, as opposed to double buffers.
+ \value NoDepthBuffer Disables the use of a depth buffer.
+ \value ColorIndex Specifies that the context should use a color index as its pixel format.
+ \value NoAlphaChannel Disables the use of an alpha channel.
+ \value NoAccumBuffer Disables the use of an accumulation buffer.
+ \value NoStencilBuffer Disables the use of a stencil buffer.
+ \value NoStereoBuffers Disables the use of stereo buffers.
+ \value IndirectRendering Specifies that the context is used for indirect rendering to a buffer.
+ \value NoOverlay Disables the use of an overlay.
+ \value NoSampleBuffers Disables the use of sample buffers.
+
+ \sa {Sample Buffers Example}
+*/
+
+/*****************************************************************************
+ QGLFormat implementation
+ *****************************************************************************/
+
+
+/*!
+ \class QGLFormat
+ \brief The QGLFormat class specifies the display format of an OpenGL
+ rendering context.
+
+ \ingroup multimedia
+
+ A display format has several characteristics:
+ \list
+ \i \link setDoubleBuffer() Double or single buffering.\endlink
+ \i \link setDepth() Depth buffer.\endlink
+ \i \link setRgba() RGBA or color index mode.\endlink
+ \i \link setAlpha() Alpha channel.\endlink
+ \i \link setAccum() Accumulation buffer.\endlink
+ \i \link setStencil() Stencil buffer.\endlink
+ \i \link setStereo() Stereo buffers.\endlink
+ \i \link setDirectRendering() Direct rendering.\endlink
+ \i \link setOverlay() Presence of an overlay.\endlink
+ \i \link setPlane() The plane of an overlay format.\endlink
+ \i \link setSampleBuffers() Multisample buffers.\endlink
+ \endlist
+
+ You can also specify preferred bit depths for the depth buffer,
+ alpha buffer, accumulation buffer and the stencil buffer with the
+ functions: setDepthBufferSize(), setAlphaBufferSize(),
+ setAccumBufferSize() and setStencilBufferSize().
+
+ Note that even if you specify that you prefer a 32 bit depth
+ buffer (e.g. with setDepthBufferSize(32)), the format that is
+ chosen may not have a 32 bit depth buffer, even if there is a
+ format available with a 32 bit depth buffer. The main reason for
+ this is how the system dependant picking algorithms work on the
+ different platforms, and some format options may have higher
+ precedence than others.
+
+ You create and tell a QGLFormat object what rendering options you
+ want from an OpenGL rendering context.
+
+ OpenGL drivers or accelerated hardware may or may not support
+ advanced features such as alpha channel or stereographic viewing.
+ If you request some features that the driver/hardware does not
+ provide when you create a QGLWidget, you will get a rendering
+ context with the nearest subset of features.
+
+ There are different ways to define the display characteristics of
+ a rendering context. One is to create a QGLFormat and make it the
+ default for the entire application:
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 0
+
+ Or you can specify the desired format when creating an object of
+ your QGLWidget subclass:
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 1
+
+ After the widget has been created, you can find out which of the
+ requested features the system was able to provide:
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 2
+
+ \legalese
+ OpenGL is a trademark of Silicon Graphics, Inc. in the
+ United States and other countries.
+ \endlegalese
+
+ \sa QGLContext, QGLWidget
+*/
+
+static inline void transform_point(GLdouble out[4], const GLdouble m[16], const GLdouble in[4])
+{
+#define M(row,col) m[col*4+row]
+ out[0] =
+ M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];
+ out[1] =
+ M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];
+ out[2] =
+ M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];
+ out[3] =
+ M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];
+#undef M
+}
+
+static inline GLint qgluProject(GLdouble objx, GLdouble objy, GLdouble objz,
+ const GLdouble model[16], const GLdouble proj[16],
+ const GLint viewport[4],
+ GLdouble * winx, GLdouble * winy, GLdouble * winz)
+{
+ GLdouble in[4], out[4];
+
+ in[0] = objx;
+ in[1] = objy;
+ in[2] = objz;
+ in[3] = 1.0;
+ transform_point(out, model, in);
+ transform_point(in, proj, out);
+
+ if (in[3] == 0.0)
+ return GL_FALSE;
+
+ in[0] /= in[3];
+ in[1] /= in[3];
+ in[2] /= in[3];
+
+ *winx = viewport[0] + (1 + in[0]) * viewport[2] / 2;
+ *winy = viewport[1] + (1 + in[1]) * viewport[3] / 2;
+
+ *winz = (1 + in[2]) / 2;
+ return GL_TRUE;
+}
+
+/*!
+ Constructs a QGLFormat object with the factory default settings:
+ \list
+ \i \link setDoubleBuffer() Double buffer:\endlink Enabled.
+ \i \link setDepth() Depth buffer:\endlink Enabled.
+ \i \link setRgba() RGBA:\endlink Enabled (i.e., color index disabled).
+ \i \link setAlpha() Alpha channel:\endlink Disabled.
+ \i \link setAccum() Accumulator buffer:\endlink Disabled.
+ \i \link setStencil() Stencil buffer:\endlink Disabled.
+ \i \link setStereo() Stereo:\endlink Disabled.
+ \i \link setDirectRendering() Direct rendering:\endlink Enabled.
+ \i \link setOverlay() Overlay:\endlink Disabled.
+ \i \link setPlane() Plane:\endlink 0 (i.e., normal plane).
+ \i \link setSampleBuffers() Multisample buffers:\endlink Disabled.
+ \endlist
+*/
+
+QGLFormat::QGLFormat()
+{
+ d = new QGLFormatPrivate;
+}
+
+
+/*!
+ Creates a QGLFormat object that is a copy of the current \link
+ defaultFormat() application default format\endlink.
+
+ If \a options is not 0, this copy is modified by these format
+ options. The \a options parameter should be \c FormatOption values
+ OR'ed together.
+
+ This constructor makes it easy to specify a certain desired format
+ in classes derived from QGLWidget, for example:
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 3
+
+ Note that there are \c FormatOption values to turn format settings
+ both on and off, e.g. \c DepthBuffer and \c NoDepthBuffer,
+ \c DirectRendering and \c IndirectRendering, etc.
+
+ The \a plane parameter defaults to 0 and is the plane which this
+ format should be associated with. Not all OpenGL implementations
+ supports overlay/underlay rendering planes.
+
+ \sa defaultFormat(), setOption()
+*/
+
+QGLFormat::QGLFormat(QGL::FormatOptions options, int plane)
+{
+ d = new QGLFormatPrivate;
+ QGL::FormatOptions newOpts = options;
+ d->opts = defaultFormat().d->opts;
+ d->opts |= (newOpts & 0xffff);
+ d->opts &= ~(newOpts >> 16);
+ d->pln = plane;
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QGLFormat::QGLFormat(const QGLFormat &other)
+{
+ d = new QGLFormatPrivate;
+ *d = *other.d;
+}
+
+/*!
+ Assigns \a other to this object.
+*/
+
+QGLFormat &QGLFormat::operator=(const QGLFormat &other)
+{
+ *d = *other.d;
+ return *this;
+}
+
+/*!
+ Destroys the QGLFormat.
+*/
+QGLFormat::~QGLFormat()
+{
+ delete d;
+}
+
+/*!
+ \fn bool QGLFormat::doubleBuffer() const
+
+ Returns true if double buffering is enabled; otherwise returns
+ false. Double buffering is enabled by default.
+
+ \sa setDoubleBuffer()
+*/
+
+/*!
+ If \a enable is true sets double buffering; otherwise sets single
+ buffering.
+
+ Double buffering is enabled by default.
+
+ Double buffering is a technique where graphics are rendered on an
+ off-screen buffer and not directly to the screen. When the drawing
+ has been completed, the program calls a swapBuffers() function to
+ exchange the screen contents with the buffer. The result is
+ flicker-free drawing and often better performance.
+
+ \sa doubleBuffer(), QGLContext::swapBuffers(),
+ QGLWidget::swapBuffers()
+*/
+
+void QGLFormat::setDoubleBuffer(bool enable)
+{
+ setOption(enable ? QGL::DoubleBuffer : QGL::SingleBuffer);
+}
+
+
+/*!
+ \fn bool QGLFormat::depth() const
+
+ Returns true if the depth buffer is enabled; otherwise returns
+ false. The depth buffer is enabled by default.
+
+ \sa setDepth(), setDepthBufferSize()
+*/
+
+/*!
+ If \a enable is true enables the depth buffer; otherwise disables
+ the depth buffer.
+
+ The depth buffer is enabled by default.
+
+ The purpose of a depth buffer (or Z-buffering) is to remove hidden
+ surfaces. Pixels are assigned Z values based on the distance to
+ the viewer. A pixel with a high Z value is closer to the viewer
+ than a pixel with a low Z value. This information is used to
+ decide whether to draw a pixel or not.
+
+ \sa depth(), setDepthBufferSize()
+*/
+
+void QGLFormat::setDepth(bool enable)
+{
+ setOption(enable ? QGL::DepthBuffer : QGL::NoDepthBuffer);
+}
+
+
+/*!
+ \fn bool QGLFormat::rgba() const
+
+ Returns true if RGBA color mode is set. Returns false if color
+ index mode is set. The default color mode is RGBA.
+
+ \sa setRgba()
+*/
+
+/*!
+ If \a enable is true sets RGBA mode. If \a enable is false sets
+ color index mode.
+
+ The default color mode is RGBA.
+
+ RGBA is the preferred mode for most OpenGL applications. In RGBA
+ color mode you specify colors as red + green + blue + alpha
+ quadruplets.
+
+ In color index mode you specify an index into a color lookup
+ table.
+
+ \sa rgba()
+*/
+
+void QGLFormat::setRgba(bool enable)
+{
+ setOption(enable ? QGL::Rgba : QGL::ColorIndex);
+}
+
+
+/*!
+ \fn bool QGLFormat::alpha() const
+
+ Returns true if the alpha buffer in the framebuffer is enabled;
+ otherwise returns false. The alpha buffer is disabled by default.
+
+ \sa setAlpha(), setAlphaBufferSize()
+*/
+
+/*!
+ If \a enable is true enables the alpha buffer; otherwise disables
+ the alpha buffer.
+
+ The alpha buffer is disabled by default.
+
+ The alpha buffer is typically used for implementing transparency
+ or translucency. The A in RGBA specifies the transparency of a
+ pixel.
+
+ \sa alpha(), setAlphaBufferSize()
+*/
+
+void QGLFormat::setAlpha(bool enable)
+{
+ setOption(enable ? QGL::AlphaChannel : QGL::NoAlphaChannel);
+}
+
+
+/*!
+ \fn bool QGLFormat::accum() const
+
+ Returns true if the accumulation buffer is enabled; otherwise
+ returns false. The accumulation buffer is disabled by default.
+
+ \sa setAccum(), setAccumBufferSize()
+*/
+
+/*!
+ If \a enable is true enables the accumulation buffer; otherwise
+ disables the accumulation buffer.
+
+ The accumulation buffer is disabled by default.
+
+ The accumulation buffer is used to create blur effects and
+ multiple exposures.
+
+ \sa accum(), setAccumBufferSize()
+*/
+
+void QGLFormat::setAccum(bool enable)
+{
+ setOption(enable ? QGL::AccumBuffer : QGL::NoAccumBuffer);
+}
+
+
+/*!
+ \fn bool QGLFormat::stencil() const
+
+ Returns true if the stencil buffer is enabled; otherwise returns
+ false. The stencil buffer is disabled by default.
+
+ \sa setStencil(), setStencilBufferSize()
+*/
+
+/*!
+ If \a enable is true enables the stencil buffer; otherwise
+ disables the stencil buffer.
+
+ The stencil buffer is disabled by default.
+
+ The stencil buffer masks certain parts of the drawing area so that
+ masked parts are not drawn on.
+
+ \sa stencil(), setStencilBufferSize()
+*/
+
+void QGLFormat::setStencil(bool enable)
+{
+ setOption(enable ? QGL::StencilBuffer: QGL::NoStencilBuffer);
+}
+
+
+/*!
+ \fn bool QGLFormat::stereo() const
+
+ Returns true if stereo buffering is enabled; otherwise returns
+ false. Stereo buffering is disabled by default.
+
+ \sa setStereo()
+*/
+
+/*!
+ If \a enable is true enables stereo buffering; otherwise disables
+ stereo buffering.
+
+ Stereo buffering is disabled by default.
+
+ Stereo buffering provides extra color buffers to generate left-eye
+ and right-eye images.
+
+ \sa stereo()
+*/
+
+void QGLFormat::setStereo(bool enable)
+{
+ setOption(enable ? QGL::StereoBuffers : QGL::NoStereoBuffers);
+}
+
+
+/*!
+ \fn bool QGLFormat::directRendering() const
+
+ Returns true if direct rendering is enabled; otherwise returns
+ false.
+
+ Direct rendering is enabled by default.
+
+ \sa setDirectRendering()
+*/
+
+/*!
+ If \a enable is true enables direct rendering; otherwise disables
+ direct rendering.
+
+ Direct rendering is enabled by default.
+
+ Enabling this option will make OpenGL bypass the underlying window
+ system and render directly from hardware to the screen, if this is
+ supported by the system.
+
+ \sa directRendering()
+*/
+
+void QGLFormat::setDirectRendering(bool enable)
+{
+ setOption(enable ? QGL::DirectRendering : QGL::IndirectRendering);
+}
+
+/*!
+ \fn bool QGLFormat::sampleBuffers() const
+
+ Returns true if multisample buffer support is enabled; otherwise
+ returns false.
+
+ The multisample buffer is disabled by default.
+
+ \sa setSampleBuffers()
+*/
+
+/*!
+ If \a enable is true, a GL context with multisample buffer support
+ is picked; otherwise ignored.
+
+ \sa sampleBuffers(), setSamples(), samples()
+*/
+void QGLFormat::setSampleBuffers(bool enable)
+{
+ setOption(enable ? QGL::SampleBuffers : QGL::NoSampleBuffers);
+}
+
+/*!
+ Returns the number of samples per pixel when multisampling is
+ enabled. By default, the highest number of samples that is
+ available is used.
+
+ \sa setSampleBuffers(), sampleBuffers(), setSamples()
+*/
+int QGLFormat::samples() const
+{
+ return d->numSamples;
+}
+
+/*!
+ Set the preferred number of samples per pixel when multisampling
+ is enabled to \a numSamples. By default, the highest number of
+ samples available is used.
+
+ \sa setSampleBuffers(), sampleBuffers(), samples()
+*/
+void QGLFormat::setSamples(int numSamples)
+{
+ if (numSamples < 0) {
+ qWarning("QGLFormat::setSamples: Cannot have negative number of samples per pixel %d", numSamples);
+ return;
+ }
+ d->numSamples = numSamples;
+}
+
+/*!
+ \since 4.2
+
+ Set the preferred swap interval. This can be used to sync the GL
+ drawing into a system window to the vertical refresh of the screen.
+ Setting an \a interval value of 0 will turn the vertical refresh syncing
+ off, any value higher than 0 will turn the vertical syncing on.
+
+ Under Windows and under X11, where the \c{WGL_EXT_swap_control}
+ and \c{GLX_SGI_video_sync} extensions are used, the \a interval
+ parameter can be used to set the minimum number of video frames
+ that are displayed before a buffer swap will occur. In effect,
+ setting the \a interval to 10, means there will be 10 vertical
+ retraces between every buffer swap.
+
+ Under Windows the \c{WGL_EXT_swap_control} extension has to be present,
+ and under X11 the \c{GLX_SGI_video_sync} extension has to be present.
+*/
+void QGLFormat::setSwapInterval(int interval)
+{
+ d->swapInterval = interval;
+}
+
+/*!
+ \since 4.2
+
+ Returns the currently set swap interval. -1 is returned if setting
+ the swap interval isn't supported in the system GL implementation.
+*/
+int QGLFormat::swapInterval() const
+{
+ return d->swapInterval;
+}
+
+/*!
+ \fn bool QGLFormat::hasOverlay() const
+
+ Returns true if overlay plane is enabled; otherwise returns false.
+
+ Overlay is disabled by default.
+
+ \sa setOverlay()
+*/
+
+/*!
+ If \a enable is true enables an overlay plane; otherwise disables
+ the overlay plane.
+
+ Enabling the overlay plane will cause QGLWidget to create an
+ additional context in an overlay plane. See the QGLWidget
+ documentation for further information.
+
+ \sa hasOverlay()
+*/
+
+void QGLFormat::setOverlay(bool enable)
+{
+ setOption(enable ? QGL::HasOverlay : QGL::NoOverlay);
+}
+
+/*!
+ Returns the plane of this format. The default for normal formats
+ is 0, which means the normal plane. The default for overlay
+ formats is 1, which is the first overlay plane.
+
+ \sa setPlane()
+*/
+int QGLFormat::plane() const
+{
+ return d->pln;
+}
+
+/*!
+ Sets the requested plane to \a plane. 0 is the normal plane, 1 is
+ the first overlay plane, 2 is the second overlay plane, etc.; -1,
+ -2, etc. are underlay planes.
+
+ Note that in contrast to other format specifications, the plane
+ specifications will be matched exactly. This means that if you
+ specify a plane that the underlying OpenGL system cannot provide,
+ an \link QGLWidget::isValid() invalid\endlink QGLWidget will be
+ created.
+
+ \sa plane()
+*/
+void QGLFormat::setPlane(int plane)
+{
+ d->pln = plane;
+}
+
+/*!
+ Sets the format option to \a opt.
+
+ \sa testOption()
+*/
+
+void QGLFormat::setOption(QGL::FormatOptions opt)
+{
+ if (opt & 0xffff)
+ d->opts |= opt;
+ else
+ d->opts &= ~(opt >> 16);
+}
+
+
+
+/*!
+ Returns true if format option \a opt is set; otherwise returns false.
+
+ \sa setOption()
+*/
+
+bool QGLFormat::testOption(QGL::FormatOptions opt) const
+{
+ if (opt & 0xffff)
+ return (d->opts & opt) != 0;
+ else
+ return (d->opts & (opt >> 16)) == 0;
+}
+
+/*!
+ Set the minimum depth buffer size to \a size.
+
+ \sa depthBufferSize(), setDepth(), depth()
+*/
+void QGLFormat::setDepthBufferSize(int size)
+{
+ if (size < 0) {
+ qWarning("QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size %d", size);
+ return;
+ }
+ d->depthSize = size;
+}
+
+/*!
+ Returns the depth buffer size.
+
+ \sa depth(), setDepth(), setDepthBufferSize()
+*/
+int QGLFormat::depthBufferSize() const
+{
+ return d->depthSize;
+}
+
+/*!
+ \since 4.2
+
+ Set the preferred red buffer size to \a size.
+
+ \sa setGreenBufferSize(), setBlueBufferSize(), setAlphaBufferSize()
+*/
+void QGLFormat::setRedBufferSize(int size)
+{
+ if (size < 0) {
+ qWarning("QGLFormat::setRedBufferSize: Cannot set negative red buffer size %d", size);
+ return;
+ }
+ d->redSize = size;
+}
+
+/*!
+ \since 4.2
+
+ Returns the red buffer size.
+
+ \sa setRedBufferSize()
+*/
+int QGLFormat::redBufferSize() const
+{
+ return d->redSize;
+}
+
+/*!
+ \since 4.2
+
+ Set the preferred green buffer size to \a size.
+
+ \sa setRedBufferSize(), setBlueBufferSize(), setAlphaBufferSize()
+*/
+void QGLFormat::setGreenBufferSize(int size)
+{
+ if (size < 0) {
+ qWarning("QGLFormat::setGreenBufferSize: Cannot set negative green buffer size %d", size);
+ return;
+ }
+ d->greenSize = size;
+}
+
+/*!
+ \since 4.2
+
+ Returns the green buffer size.
+
+ \sa setGreenBufferSize()
+*/
+int QGLFormat::greenBufferSize() const
+{
+ return d->greenSize;
+}
+
+/*!
+ \since 4.2
+
+ Set the preferred blue buffer size to \a size.
+
+ \sa setRedBufferSize(), setGreenBufferSize(), setAlphaBufferSize()
+*/
+void QGLFormat::setBlueBufferSize(int size)
+{
+ if (size < 0) {
+ qWarning("QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size %d", size);
+ return;
+ }
+ d->blueSize = size;
+}
+
+/*!
+ \since 4.2
+
+ Returns the blue buffer size.
+
+ \sa setBlueBufferSize()
+*/
+int QGLFormat::blueBufferSize() const
+{
+ return d->blueSize;
+}
+
+/*!
+ Set the preferred alpha buffer size to \a size.
+ This function implicitly enables the alpha channel.
+
+ \sa setRedBufferSize(), setGreenBufferSize(), alphaBufferSize()
+*/
+void QGLFormat::setAlphaBufferSize(int size)
+{
+ if (size < 0) {
+ qWarning("QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size %d", size);
+ return;
+ }
+ d->alphaSize = size;
+ setOption(QGL::AlphaChannel);
+}
+
+/*!
+ Returns the alpha buffer size.
+
+ \sa alpha(), setAlpha(), setAlphaBufferSize()
+*/
+int QGLFormat::alphaBufferSize() const
+{
+ return d->alphaSize;
+}
+
+/*!
+ Set the preferred accumulation buffer size, where \a size is the
+ bit depth for each RGBA component.
+
+ \sa accum(), setAccum(), accumBufferSize()
+*/
+void QGLFormat::setAccumBufferSize(int size)
+{
+ if (size < 0) {
+ qWarning("QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size %d", size);
+ return;
+ }
+ d->accumSize = size;
+}
+
+/*!
+ Returns the accumulation buffer size.
+
+ \sa setAccumBufferSize(), accum(), setAccum()
+*/
+int QGLFormat::accumBufferSize() const
+{
+ return d->accumSize;
+}
+
+/*!
+ Set the preferred stencil buffer size to \a size.
+
+ \sa stencilBufferSize(), setStencil(), stencil()
+*/
+void QGLFormat::setStencilBufferSize(int size)
+{
+ if (size < 0) {
+ qWarning("QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size %d", size);
+ return;
+ }
+ d->stencilSize = size;
+}
+
+/*!
+ Returns the stencil buffer size.
+
+ \sa stencil(), setStencil(), setStencilBufferSize()
+*/
+int QGLFormat::stencilBufferSize() const
+{
+ return d->stencilSize;
+}
+
+/*!
+ \fn bool QGLFormat::hasOpenGL()
+
+ Returns true if the window system has any OpenGL support;
+ otherwise returns false.
+
+ \warning This function must not be called until the QApplication
+ object has been created.
+*/
+
+
+
+/*!
+ \fn bool QGLFormat::hasOpenGLOverlays()
+
+ Returns true if the window system supports OpenGL overlays;
+ otherwise returns false.
+
+ \warning This function must not be called until the QApplication
+ object has been created.
+*/
+
+QGLFormat::OpenGLVersionFlags Q_AUTOTEST_EXPORT qOpenGLVersionFlagsFromString(const QString &versionString)
+{
+ QGLFormat::OpenGLVersionFlags versionFlags = QGLFormat::OpenGL_Version_None;
+
+ if (versionString.startsWith(QLatin1String("OpenGL ES"))) {
+ QStringList parts = versionString.split(QLatin1Char(' '));
+ if (parts.size() >= 3) {
+ if (parts[2].startsWith(QLatin1String("1."))) {
+ if (parts[1].endsWith(QLatin1String("-CM"))) {
+ versionFlags |= QGLFormat::OpenGL_ES_Common_Version_1_0 |
+ QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
+ if (parts[2].startsWith(QLatin1String("1.1")))
+ versionFlags |= QGLFormat::OpenGL_ES_Common_Version_1_1 |
+ QGLFormat::OpenGL_ES_CommonLite_Version_1_1;
+ }
+ else {
+ // Not -CM, must be CL, CommonLite
+ versionFlags |= QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
+ if (parts[2].startsWith(QLatin1String("1.1")))
+ versionFlags |= QGLFormat::OpenGL_ES_CommonLite_Version_1_1;
+ }
+ }
+ else {
+ // OpenGL ES version 2.0 or higher
+ versionFlags |= QGLFormat::OpenGL_ES_Version_2_0;
+ }
+ }
+ else {
+ // if < 3 parts to the name, it is an unrecognised OpenGL ES
+ qWarning("Unrecognised OpenGL ES version");
+ }
+ }
+ else {
+ // not ES, regular OpenGL, the version numbers are first in the string
+ if (versionString.startsWith(QLatin1String("1."))) {
+ switch (versionString[2].toAscii()) {
+ case '5':
+ versionFlags |= QGLFormat::OpenGL_Version_1_5;
+ case '4':
+ versionFlags |= QGLFormat::OpenGL_Version_1_4;
+ case '3':
+ versionFlags |= QGLFormat::OpenGL_Version_1_3;
+ case '2':
+ versionFlags |= QGLFormat::OpenGL_Version_1_2;
+ case '1':
+ versionFlags |= QGLFormat::OpenGL_Version_1_1;
+ default:
+ break;
+ }
+ }
+ else if (versionString.startsWith(QLatin1String("2."))) {
+ versionFlags |= QGLFormat::OpenGL_Version_1_1 |
+ QGLFormat::OpenGL_Version_1_2 |
+ QGLFormat::OpenGL_Version_1_3 |
+ QGLFormat::OpenGL_Version_1_4 |
+ QGLFormat::OpenGL_Version_1_5 |
+ QGLFormat::OpenGL_Version_2_0;
+ QString minorVersion = versionString.section(QLatin1Char(' '), 0, 0).section(QLatin1Char('.'), 1, 1);
+ if (minorVersion == QChar(QLatin1Char('1')))
+ versionFlags |= QGLFormat::OpenGL_Version_2_1;
+ }
+ else if (versionString.startsWith(QLatin1String("3."))) {
+ versionFlags |= QGLFormat::OpenGL_Version_1_1 |
+ QGLFormat::OpenGL_Version_1_2 |
+ QGLFormat::OpenGL_Version_1_3 |
+ QGLFormat::OpenGL_Version_1_4 |
+ QGLFormat::OpenGL_Version_1_5 |
+ QGLFormat::OpenGL_Version_2_0 |
+ QGLFormat::OpenGL_Version_2_1 |
+ QGLFormat::OpenGL_Version_3_0;
+ }
+ else
+ qWarning("Unrecognised OpenGL version");
+ }
+ return versionFlags;
+}
+
+/*!
+ \enum QGLFormat::OpenGLVersionFlag
+ \since 4.2
+
+ This enum describes the various OpenGL versions that are
+ recognized by Qt. Use the QGLFormat::openGLVersionFlags() function
+ to identify which versions that are supported at runtime.
+
+ \value OpenGL_Version_None If no OpenGL is present or if no OpenGL context is current.
+
+ \value OpenGL_Version_1_1 OpenGL version 1.1 or higher is present.
+
+ \value OpenGL_Version_1_2 OpenGL version 1.2 or higher is present.
+
+ \value OpenGL_Version_1_3 OpenGL version 1.3 or higher is present.
+
+ \value OpenGL_Version_1_4 OpenGL version 1.4 or higher is present.
+
+ \value OpenGL_Version_1_5 OpenGL version 1.5 or higher is present.
+
+ \value OpenGL_Version_2_0 OpenGL version 2.0 or higher is present.
+ Note that version 2.0 supports all the functionality of version 1.5.
+
+ \value OpenGL_Version_2_1 OpenGL version 2.1 or higher is present.
+
+ \value OpenGL_Version_3_0 OpenGL version 3.0 or higher is present.
+
+ \value OpenGL_ES_CommonLite_Version_1_0 OpenGL ES version 1.0 Common Lite or higher is present.
+
+ \value OpenGL_ES_Common_Version_1_0 OpenGL ES version 1.0 Common or higher is present.
+ The Common profile supports all the features of Common Lite.
+
+ \value OpenGL_ES_CommonLite_Version_1_1 OpenGL ES version 1.1 Common Lite or higher is present.
+
+ \value OpenGL_ES_Common_Version_1_1 OpenGL ES version 1.1 Common or higher is present.
+ The Common profile supports all the features of Common Lite.
+
+ \value OpenGL_ES_Version_2_0 OpenGL ES version 2.0 or higher is present.
+ Note that OpenGL ES version 2.0 does not support all the features of OpenGL ES 1.x.
+ So if OpenGL_ES_Version_2_0 is returned, none of the ES 1.x flags are returned.
+
+ See also \l{http://www.opengl.org} for more information about the different
+ revisions of OpenGL.
+
+ \sa openGLVersionFlags()
+*/
+
+/*!
+ \since 4.2
+
+ Identifies, at runtime, which OpenGL versions that are supported
+ by the current platform.
+
+ Note that if OpenGL version 1.5 is supported, its predecessors
+ (i.e., version 1.4 and lower) are also supported. To identify the
+ support of a particular feature, like multi texturing, test for
+ the version in which the feature was first introduced (i.e.,
+ version 1.3 in the case of multi texturing) to adapt to the largest
+ possible group of runtime platforms.
+
+ This function needs a valid current OpenGL context to work;
+ otherwise it will return OpenGL_Version_None.
+
+ \sa hasOpenGL(), hasOpenGLOverlays()
+*/
+QGLFormat::OpenGLVersionFlags QGLFormat::openGLVersionFlags()
+{
+ static bool cachedDefault = false;
+ static OpenGLVersionFlags defaultVersionFlags = OpenGL_Version_None;
+ QGLContext *currentCtx = const_cast<QGLContext *>(QGLContext::currentContext());
+ QGLWidget *dummy = 0;
+
+ if (currentCtx && currentCtx->d_func()->version_flags_cached)
+ return currentCtx->d_func()->version_flags;
+
+ if (!currentCtx) {
+ if (cachedDefault) {
+ return defaultVersionFlags;
+ } else {
+ cachedDefault = true;
+ if (!hasOpenGL())
+ return defaultVersionFlags;
+ dummy = new QGLWidget;
+ dummy->makeCurrent(); // glGetString() needs a current context
+ }
+ }
+
+ QString versionString(QLatin1String(reinterpret_cast<const char*>(glGetString(GL_VERSION))));
+ OpenGLVersionFlags versionFlags = qOpenGLVersionFlagsFromString(versionString);
+ if (currentCtx) {
+ currentCtx->d_func()->version_flags_cached = true;
+ currentCtx->d_func()->version_flags = versionFlags;
+ }
+ if (dummy) {
+ defaultVersionFlags = versionFlags;
+ delete dummy;
+ }
+
+ return versionFlags;
+}
+
+
+/*!
+ Returns the default QGLFormat for the application. All QGLWidgets
+ that are created use this format unless another format is
+ specified, e.g. when they are constructed.
+
+ If no special default format has been set using
+ setDefaultFormat(), the default format is the same as that created
+ with QGLFormat().
+
+ \sa setDefaultFormat()
+*/
+
+QGLFormat QGLFormat::defaultFormat()
+{
+ return *qgl_default_format();
+}
+
+/*!
+ Sets a new default QGLFormat for the application to \a f. For
+ example, to set single buffering as the default instead of double
+ buffering, your main() might contain code like this:
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 4
+
+ \sa defaultFormat()
+*/
+
+void QGLFormat::setDefaultFormat(const QGLFormat &f)
+{
+ *qgl_default_format() = f;
+}
+
+
+/*!
+ Returns the default QGLFormat for overlay contexts.
+
+ The factory default overlay format is:
+ \list
+ \i \link setDoubleBuffer() Double buffer:\endlink Disabled.
+ \i \link setDepth() Depth buffer:\endlink Disabled.
+ \i \link setRgba() RGBA:\endlink Disabled (i.e., color index enabled).
+ \i \link setAlpha() Alpha channel:\endlink Disabled.
+ \i \link setAccum() Accumulator buffer:\endlink Disabled.
+ \i \link setStencil() Stencil buffer:\endlink Disabled.
+ \i \link setStereo() Stereo:\endlink Disabled.
+ \i \link setDirectRendering() Direct rendering:\endlink Enabled.
+ \i \link setOverlay() Overlay:\endlink Disabled.
+ \i \link setPlane() Plane:\endlink 1 (i.e., first overlay plane).
+ \endlist
+
+ \sa setDefaultFormat()
+*/
+
+QGLFormat QGLFormat::defaultOverlayFormat()
+{
+ return *defaultOverlayFormatInstance();
+}
+
+/*!
+ Sets a new default QGLFormat for overlay contexts to \a f. This
+ format is used whenever a QGLWidget is created with a format that
+ hasOverlay() enabled.
+
+ For example, to get a double buffered overlay context (if
+ available), use code like this:
+
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 5
+
+ As usual, you can find out after widget creation whether the
+ underlying OpenGL system was able to provide the requested
+ specification:
+
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 6
+
+ \sa defaultOverlayFormat()
+*/
+
+void QGLFormat::setDefaultOverlayFormat(const QGLFormat &f)
+{
+ QGLFormat *defaultFormat = defaultOverlayFormatInstance();
+ *defaultFormat = f;
+ // Make sure the user doesn't request that the overlays themselves
+ // have overlays, since it is unlikely that the system supports
+ // infinitely many planes...
+ defaultFormat->setOverlay(false);
+}
+
+
+/*!
+ Returns true if all the options of the two QGLFormats are equal;
+ otherwise returns false.
+*/
+
+bool operator==(const QGLFormat& a, const QGLFormat& b)
+{
+ return (int) a.d->opts == (int) b.d->opts && a.d->pln == b.d->pln && a.d->alphaSize == b.d->alphaSize
+ && a.d->accumSize == b.d->accumSize && a.d->stencilSize == b.d->stencilSize
+ && a.d->depthSize == b.d->depthSize;
+}
+
+
+/*!
+ Returns false if all the options of the two QGLFormats are equal;
+ otherwise returns true.
+*/
+
+bool operator!=(const QGLFormat& a, const QGLFormat& b)
+{
+ return !(a == b);
+}
+
+/*****************************************************************************
+ QGLContext implementation
+ *****************************************************************************/
+void QGLContextPrivate::init(QPaintDevice *dev, const QGLFormat &format)
+{
+ Q_Q(QGLContext);
+ glFormat = reqFormat = format;
+ valid = false;
+ q->setDevice(dev);
+#if defined(Q_WS_X11)
+ pbuf = 0;
+ gpm = 0;
+ vi = 0;
+ screen = QX11Info::appScreen();
+#endif
+#if defined(Q_WS_WIN)
+ dc = 0;
+ win = 0;
+ pixelFormatId = 0;
+ cmap = 0;
+ hbitmap = 0;
+ hbitmap_hdc = 0;
+#endif
+#if defined(Q_WS_MAC)
+# ifndef QT_MAC_USE_COCOA
+ update = false;
+# endif
+ vi = 0;
+#endif
+#if defined(QT_OPENGL_ES)
+ eglContext = 0;
+#endif
+ pbo = 0;
+ crWin = false;
+ initDone = false;
+ sharing = false;
+ clear_on_painter_begin = true;
+ max_texture_size = -1;
+ version_flags_cached = false;
+ version_flags = QGLFormat::OpenGL_Version_None;
+}
+
+QGLContext* QGLContext::currentCtx = 0;
+
+/*
+ Read back the contents of the currently bound framebuffer, used in
+ QGLWidget::grabFrameBuffer(), QGLPixelbuffer::toImage() and
+ QGLFramebufferObject::toImage()
+*/
+
+QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha)
+{
+ QImage img(size, alpha_format ? QImage::Format_ARGB32 : QImage::Format_RGB32);
+ int w = size.width();
+ int h = size.height();
+ glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ // OpenGL gives RGBA; Qt wants ARGB
+ uint *p = (uint*)img.bits();
+ uint *end = p + w*h;
+ if (alpha_format && include_alpha) {
+ while (p < end) {
+ uint a = *p << 24;
+ *p = (*p >> 8) | a;
+ p++;
+ }
+ } else {
+ // This is an old legacy fix for PowerPC based Macs, which
+ // we shouldn't remove
+ while (p < end) {
+ *p = 0xFF000000 | (*p>>8);
+ ++p;
+ }
+ }
+ } else {
+ // OpenGL gives ABGR (i.e. RGBA backwards); Qt wants ARGB
+ img = img.rgbSwapped();
+ }
+ return img.mirrored();
+}
+
+// returns the highest number closest to v, which is a power of 2
+// NB! assumes 32 bit ints
+int qt_next_power_of_two(int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ ++v;
+ return v;
+}
+
+class QGLTexture {
+public:
+ QGLTexture(const QGLContext *ctx, GLuint tx_id, GLenum tx_target, bool _clean = false)
+ : context(ctx), id(tx_id), target(tx_target), clean(_clean) {}
+ ~QGLTexture() {
+ if (clean) {
+ QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext());
+ QGLContext *ctx = const_cast<QGLContext *>(context);
+ bool switch_context = current && current != ctx && !qgl_share_reg()->checkSharing(current, ctx);
+ if (switch_context)
+ ctx->makeCurrent();
+ glDeleteTextures(1, &id);
+ if (switch_context)
+ current->makeCurrent();
+ }
+ }
+
+ const QGLContext *context;
+ GLuint id;
+ GLenum target;
+ bool clean;
+};
+
+typedef QCache<qint64, QGLTexture> QGLTextureCache;
+static int qt_tex_cache_limit = 64*1024; // cache ~64 MB worth of textures - this is not accurate though
+static QGLTextureCache *qt_tex_cache = 0;
+
+typedef void (*_qt_pixmap_cleanup_hook_64)(qint64);
+typedef void (*_qt_image_cleanup_hook_64)(qint64);
+
+extern Q_GUI_EXPORT _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64;
+extern Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64;
+
+// DDS format structure
+struct DDSFormat {
+ quint32 dwSize;
+ quint32 dwFlags;
+ quint32 dwHeight;
+ quint32 dwWidth;
+ quint32 dwLinearSize;
+ quint32 dummy1;
+ quint32 dwMipMapCount;
+ quint32 dummy2[11];
+ struct {
+ quint32 dummy3[2];
+ quint32 dwFourCC;
+ quint32 dummy4[5];
+ } ddsPixelFormat;
+};
+
+// compressed texture pixel formats
+#define FOURCC_DXT1 0x31545844
+#define FOURCC_DXT2 0x32545844
+#define FOURCC_DXT3 0x33545844
+#define FOURCC_DXT4 0x34545844
+#define FOURCC_DXT5 0x35545844
+
+#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT
+#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
+#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#endif
+
+#ifndef GL_GENERATE_MIPMAP_SGIS
+#define GL_GENERATE_MIPMAP_SGIS 0x8191
+#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192
+#endif
+
+Q_GLOBAL_STATIC(QGLShareRegister, _qgl_share_reg);
+Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg()
+{
+ return _qgl_share_reg();
+}
+
+/*!
+ \class QGLContext
+ \brief The QGLContext class encapsulates an OpenGL rendering context.
+
+ \ingroup multimedia
+
+ An OpenGL rendering context is a complete set of OpenGL state
+ variables. The rendering context's \l {QGL::FormatOption} {format}
+ is set in the constructor, but it can also be set later with
+ setFormat(). The format options that are actually set are returned
+ by format(); the options you asked for are returned by
+ requestedFormat(). Note that after a QGLContext object has been
+ constructed, the actual OpenGL context must be created by
+ explicitly calling the \link create() create()\endlink
+ function. The makeCurrent() function makes this context the
+ current rendering context. You can make \e no context current
+ using doneCurrent(). The reset() function will reset the context
+ and make it invalid.
+
+ You can examine properties of the context with, e.g. isValid(),
+ isSharing(), initialized(), windowCreated() and
+ overlayTransparentColor().
+
+ If you're using double buffering you can swap the screen contents
+ with the off-screen buffer using swapBuffers().
+
+ Please note that QGLContext is not thread safe.
+*/
+
+
+/*!
+ \obsolete
+
+ Constructs an OpenGL context for the given paint \a device, which
+ can be a widget or a pixmap. The \a format specifies several
+ display options for the context.
+
+ If the underlying OpenGL/Window system cannot satisfy all the
+ features requested in \a format, the nearest subset of features
+ will be used. After creation, the format() method will return the
+ actual format obtained.
+
+ Note that after a QGLContext object has been constructed, \l
+ create() must be called explicitly to create the actual OpenGL
+ context. The context will be \l {isValid()}{invalid} if it was not
+ possible to obtain a GL context at all.
+*/
+
+QGLContext::QGLContext(const QGLFormat &format, QPaintDevice *device)
+{
+ d_ptr = new QGLContextPrivate(this);
+ Q_D(QGLContext);
+ d->init(device, format);
+}
+
+/*!
+ Constructs an OpenGL context with the given \a format which
+ specifies several display options for the context.
+
+ If the underlying OpenGL/Window system cannot satisfy all the
+ features requested in \a format, the nearest subset of features
+ will be used. After creation, the format() method will return the
+ actual format obtained.
+
+ Note that after a QGLContext object has been constructed, \l
+ create() must be called explicitly to create the actual OpenGL
+ context. The context will be \l {isValid()}{invalid} if it was not
+ possible to obtain a GL context at all.
+
+ \sa format(), isValid()
+*/
+QGLContext::QGLContext(const QGLFormat &format)
+{
+ d_ptr = new QGLContextPrivate(this);
+ Q_D(QGLContext);
+ d->init(0, format);
+}
+
+/*!
+ Destroys the OpenGL context and frees its resources.
+*/
+
+QGLContext::~QGLContext()
+{
+ Q_D(QGLContext);
+ // remove any textures cached in this context
+ if (qt_tex_cache) {
+ QList<qint64> keys = qt_tex_cache->keys();
+ for (int i = 0; i < keys.size(); ++i) {
+ const qint64 &key = keys.at(i);
+ if (qt_tex_cache->object(key)->context == this)
+ qt_tex_cache->remove(key);
+ }
+ // ### thread safety
+ if (qt_tex_cache->size() == 0) {
+ qt_pixmap_cleanup_hook_64 = 0;
+ qt_image_cleanup_hook_64 = 0;
+ delete qt_tex_cache;
+ qt_tex_cache = 0;
+ }
+ }
+
+ QGLSignalProxy::instance()->emitAboutToDestroyContext(this);
+ reset();
+ delete d;
+}
+
+void QGLContextPrivate::cleanup()
+{
+ Q_Q(QGLContext);
+ if (pbo) {
+ QGLContext *ctx = q;
+ glDeleteBuffersARB(1, &pbo);
+ pbo = 0;
+ }
+}
+
+typedef QHash<QString, GLuint> QGLDDSCache;
+Q_GLOBAL_STATIC(QGLDDSCache, qgl_dds_cache)
+
+/*!
+ \overload
+
+ Reads the DirectDrawSurface (DDS) compressed file \a fileName and
+ generates a 2D GL texture from it.
+
+ Only the DXT1, DXT3 and DXT5 DDS formats are supported.
+
+ Note that this will only work if the implementation supports the
+ \c GL_ARB_texture_compression and \c GL_EXT_texture_compression_s3tc
+ extensions.
+
+ \sa deleteTexture()
+*/
+
+GLuint QGLContext::bindTexture(const QString &fileName)
+{
+ if (!qt_glCompressedTexImage2DARB) {
+ qWarning("QGLContext::bindTexture(): The GL implementation does not support texture"
+ "compression extensions.");
+ return 0;
+ }
+
+ QGLDDSCache::const_iterator it = qgl_dds_cache()->constFind(fileName);
+ if (it != qgl_dds_cache()->constEnd()) {
+ glBindTexture(GL_TEXTURE_2D, it.value());
+ return it.value();
+ }
+
+ QFile f(fileName);
+ f.open(QIODevice::ReadOnly);
+
+ char tag[4];
+ f.read(&tag[0], 4);
+ if (strncmp(tag,"DDS ", 4) != 0) {
+ qWarning("QGLContext::bindTexture(): not a DDS image file.");
+ return 0;
+ }
+
+ DDSFormat ddsHeader;
+ f.read((char *) &ddsHeader, sizeof(DDSFormat));
+
+ if (!ddsHeader.dwLinearSize) {
+ qWarning("QGLContext::bindTexture() DDS image size is not valid.");
+ return 0;
+ }
+
+ int factor = 4;
+ int bufferSize = 0;
+ int blockSize = 16;
+ GLenum format;
+
+ switch(ddsHeader.ddsPixelFormat.dwFourCC) {
+ case FOURCC_DXT1:
+ format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+ factor = 2;
+ blockSize = 8;
+ break;
+ case FOURCC_DXT3:
+ format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+ break;
+ case FOURCC_DXT5:
+ format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+ break;
+ default:
+ qWarning("QGLContext::bindTexture() DDS image format not supported.");
+ return 0;
+ }
+
+ if (ddsHeader.dwMipMapCount > 1)
+ bufferSize = ddsHeader.dwLinearSize * factor;
+ else
+ bufferSize = ddsHeader.dwLinearSize;
+
+ GLubyte *pixels = (GLubyte *) malloc(bufferSize*sizeof(GLubyte));
+ f.seek(ddsHeader.dwSize + 4);
+ f.read((char *) pixels, bufferSize);
+ f.close();
+
+ GLuint tx_id;
+ glGenTextures(1, &tx_id);
+ glBindTexture(GL_TEXTURE_2D, tx_id);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ int size;
+ int offset = 0;
+ int w = ddsHeader.dwWidth;
+ int h = ddsHeader.dwHeight;
+
+ // load mip-maps
+ for(int i = 0; i < (int) ddsHeader.dwMipMapCount; ++i) {
+ if (w == 0) w = 1;
+ if (h == 0) h = 1;
+
+ size = ((w+3)/4) * ((h+3)/4) * blockSize;
+ qt_glCompressedTexImage2DARB(GL_TEXTURE_2D, i, format, w, h, 0,
+ size, pixels + offset);
+ offset += size;
+
+ // half size for each mip-map level
+ w = w/2;
+ h = h/2;
+ }
+
+ free(pixels);
+
+ qgl_dds_cache()->insert(fileName, tx_id);
+ return tx_id;
+}
+
+/*
+ a hook that removes textures from the cache when a pixmap/image
+ is deref'ed
+*/
+static void qt_gl_clean_cache(qint64 cacheKey)
+{
+ // ### remove when the GL texture cache becomes thread-safe
+ if (qApp->thread() != QThread::currentThread())
+ return;
+ if (qt_tex_cache) {
+ QGLTexture *texture = qt_tex_cache->object(cacheKey);
+ if (texture && texture->clean)
+ qt_tex_cache->remove(cacheKey);
+ }
+}
+
+static void convertToGLFormatHelper(QImage &dst, const QImage &img, GLenum texture_format)
+{
+ Q_ASSERT(dst.size() == img.size());
+ Q_ASSERT(dst.depth() == 32);
+ Q_ASSERT(img.depth() == 32);
+
+ const int width = img.width();
+ const int height = img.height();
+ const uint *p = (const uint*) img.scanLine(img.height() - 1);
+ uint *q = (uint*) dst.scanLine(0);
+
+ if (texture_format == GL_BGRA) {
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ // mirror + swizzle
+ for (int i=0; i < height; ++i) {
+ const uint *end = p + width;
+ while (p < end) {
+ *q = ((*p << 24) & 0xff000000)
+ | ((*p >> 24) & 0x000000ff)
+ | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00);
+ p++;
+ q++;
+ }
+ p -= 2 * width;
+ }
+ } else {
+ const uint bytesPerLine = img.bytesPerLine();
+ for (int i=0; i < height; ++i) {
+ memcpy(q, p, bytesPerLine);
+ q += width;
+ p -= width;
+ }
+ }
+ } else {
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ for (int i=0; i < height; ++i) {
+ const uint *end = p + width;
+ while (p < end) {
+ *q = (*p << 8) | ((*p >> 24) & 0xFF);
+ p++;
+ q++;
+ }
+ p -= 2 * width;
+ }
+ } else {
+ for (int i=0; i < height; ++i) {
+ const uint *end = p + width;
+ while (p < end) {
+ *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00);
+ p++;
+ q++;
+ }
+ p -= 2 * width;
+ }
+ }
+ }
+}
+
+QImage QGLContextPrivate::convertToGLFormat(const QImage &image, bool force_premul,
+ GLenum texture_format)
+{
+ QImage::Format target_format = image.format();
+ if (force_premul || image.format() != QImage::Format_ARGB32)
+ target_format = QImage::Format_ARGB32_Premultiplied;
+
+ QImage result(image.width(), image.height(), target_format);
+ convertToGLFormatHelper(result, image.convertToFormat(target_format), texture_format);
+ return result;
+}
+
+GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format,
+ const qint64 key, bool clean)
+{
+ Q_Q(QGLContext);
+
+ QGLContext *ctx = q;
+
+ bool use_pbo = false;
+ if (QGLExtensions::glExtensions & QGLExtensions::PixelBufferObject) {
+
+ use_pbo = qt_resolve_buffer_extensions(ctx);
+ if (use_pbo && pbo == 0)
+ glGenBuffersARB(1, &pbo);
+ }
+
+ // the GL_BGRA format is only present in GL version >= 1.2
+ GLenum texture_format = (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2)
+ ? GL_BGRA : GL_RGBA;
+ if (!qt_tex_cache) {
+ qt_tex_cache = new QGLTextureCache(qt_tex_cache_limit);
+ qt_pixmap_cleanup_hook_64 = qt_gl_clean_cache;
+ qt_image_cleanup_hook_64 = qt_gl_clean_cache;
+ }
+
+ // Scale the pixmap if needed. GL textures needs to have the
+ // dimensions 2^n+2(border) x 2^m+2(border).
+ int tx_w = qt_next_power_of_two(image.width());
+ int tx_h = qt_next_power_of_two(image.height());
+
+ // Note: the clean param is only true when a texture is bound
+ // from the QOpenGLPaintEngine - in that case we have to force
+ // a premultiplied texture format
+ QImage img = image;
+ if (( !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) &&
+ !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0) )
+ && (target == GL_TEXTURE_2D && (tx_w != image.width() || tx_h != image.height())))
+ {
+ img = image.scaled(tx_w, tx_h);
+ }
+
+ GLuint tx_id;
+ glGenTextures(1, &tx_id);
+ glBindTexture(target, tx_id);
+ glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ if (glFormat.directRendering()
+ && QGLExtensions::glExtensions & QGLExtensions::GenerateMipmap
+ && target == GL_TEXTURE_2D && !clean)
+ {
+ glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
+#ifndef QT_OPENGL_ES
+ glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
+#else
+ glTexParameterf(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
+#endif
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+
+ // Mipmap generation causes huge slowdown with PBO's for some reason
+ use_pbo = false;
+ } else {
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ }
+
+ uchar *ptr = 0;
+ if (use_pbo) {
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbo);
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, img.width() * img.height() * 4, 0, GL_STREAM_DRAW_ARB);
+ ptr = reinterpret_cast<uchar *>(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB));
+ }
+
+ if (ptr) {
+ QImage::Format target_format = img.format();
+ if (clean || img.format() != QImage::Format_ARGB32)
+ target_format = QImage::Format_ARGB32_Premultiplied;
+
+ QImage buffer(ptr, img.width(), img.height(), target_format);
+ convertToGLFormatHelper(buffer, img.convertToFormat(target_format), texture_format);
+ glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB);
+ glTexImage2D(target, 0, format, img.width(), img.height(), 0, texture_format, GL_UNSIGNED_BYTE, 0);
+ } else {
+ QImage tx = convertToGLFormat(img, clean, texture_format);
+ glTexImage2D(target, 0, format, tx.width(), tx.height(), 0, texture_format,
+ GL_UNSIGNED_BYTE, tx.bits());
+ }
+
+ if (use_pbo)
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
+
+ // this assumes the size of a texture is always smaller than the max cache size
+ int cost = img.width()*img.height()*4/1024;
+ if (qt_tex_cache->totalCost() + cost > qt_tex_cache->maxCost()) {
+ // the cache is full - make an attempt to remove something
+ const QList<qint64> keys = qt_tex_cache->keys();
+ int i = 0;
+ while (i < qt_tex_cache->count()
+ && (qt_tex_cache->totalCost() + cost > qt_tex_cache->maxCost())) {
+ QGLTexture *tex = qt_tex_cache->object(keys.at(i));
+ if (tex->context == q)
+ qt_tex_cache->remove(keys.at(i));
+ ++i;
+ }
+ }
+ qt_tex_cache->insert(key, new QGLTexture(q, tx_id, target, clean), cost);
+ return tx_id;
+}
+
+bool QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum target, GLuint *id)
+{
+ Q_Q(QGLContext);
+ if (qt_tex_cache) {
+ QGLTexture *texture = qt_tex_cache->object(key);
+ if (texture && texture->target == target
+ && (texture->context == q || qgl_share_reg()->checkSharing(q, texture->context)))
+ {
+ *id = texture->id;
+ return true;
+ }
+ }
+ return false;
+}
+
+/*! \internal */
+GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format, bool clean)
+{
+ const qint64 key = image.cacheKey();
+ GLuint id;
+ if (textureCacheLookup(key, target, &id)) {
+ glBindTexture(target, id);
+ return id;
+ }
+ GLuint cached = bindTexture(image, target, format, key, clean);
+ const_cast<QImage &>(image).data_ptr()->is_cached = (cached > 0);
+ return cached;
+}
+
+/*! \internal */
+GLuint QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, bool clean)
+{
+#if !defined(QT_OPENGL_ES_2)
+ if (target == qt_gl_preferredTextureTarget() && pixmap.pixmapData()->classId() == QPixmapData::OpenGLClass) {
+ const QGLPixmapData *data = static_cast<const QGLPixmapData *>(pixmap.pixmapData());
+
+ if (data->isValidContext(QGLContext::currentContext()))
+ return data->bind();
+ }
+#endif
+
+ const qint64 key = pixmap.cacheKey();
+ GLuint id;
+ if (textureCacheLookup(key, target, &id)) {
+ glBindTexture(target, id);
+ return id;
+ }
+ GLuint cached = bindTexture(pixmap.toImage(), target, format, key, clean);
+ const_cast<QPixmap &>(pixmap).data_ptr()->is_cached = (cached > 0);
+ return cached;
+}
+
+/*! \internal */
+int QGLContextPrivate::maxTextureSize()
+{
+ if (max_texture_size != -1)
+ return max_texture_size;
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
+
+#if defined(QT_OPENGL_ES)
+ return max_texture_size;
+#else
+ GLenum proxy = GL_PROXY_TEXTURE_2D;
+
+ GLint size;
+ GLint next = 64;
+ glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &size);
+ if (size == 0) {
+ return max_texture_size;
+ }
+ do {
+ size = next;
+ next = size * 2;
+
+ if (next > max_texture_size)
+ break;
+ glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &next);
+ } while (next > size);
+
+ max_texture_size = size;
+ return max_texture_size;
+#endif
+}
+
+/*!
+ Generates and binds a 2D GL texture to the current context, based
+ on \a image. The generated texture id is returned and can be used
+ in later \c glBindTexture() calls.
+
+ The \a target parameter specifies the texture target. The default
+ target is \c GL_TEXTURE_2D.
+
+ The \a format parameter sets the internal format for the
+ texture. The default format is \c GL_RGBA8.
+
+ If the GL implementation supports the \c GL_SGIS_generate_mipmap
+ extension, mipmaps will be automatically generated for the
+ texture. Mipmap generation is only supported for the \c
+ GL_TEXTURE_2D target.
+
+ The texture that is generated is cached, so multiple calls to
+ bindTexture() with the same QImage will return the same texture
+ id.
+
+ Note that we assume default values for the glPixelStore() and
+ glPixelTransfer() parameters.
+
+ \sa deleteTexture()
+*/
+GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format)
+{
+ Q_D(QGLContext);
+ return d->bindTexture(image, target, format, false);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format)
+{
+ Q_D(QGLContext);
+ return d->bindTexture(image, GLenum(target), GLint(format), false);
+}
+#endif
+
+/*! \overload
+
+ Generates and binds a 2D GL texture based on \a pixmap.
+*/
+GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format)
+{
+ Q_D(QGLContext);
+ return d->bindTexture(pixmap, target, format, false);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format)
+{
+ Q_D(QGLContext);
+ return d->bindTexture(pixmap, GLenum(target), GLint(format), false);
+}
+#endif
+
+/*!
+ Removes the texture identified by \a id from the texture cache,
+ and calls glDeleteTextures() to delete the texture from the
+ context.
+
+ \sa bindTexture()
+*/
+void QGLContext::deleteTexture(GLuint id)
+{
+ if (qt_tex_cache) {
+ QList<qint64> keys = qt_tex_cache->keys();
+ for (int i = 0; i < keys.size(); ++i) {
+ QGLTexture *tex = qt_tex_cache->object(keys.at(i));
+ if (tex->id == id && tex->context == this) {
+ tex->clean = true; // forces a glDeleteTextures() call
+ qt_tex_cache->remove(keys.at(i));
+ return;
+ }
+ }
+ }
+
+ // check the DDS cache if the texture wasn't found in the pixmap/image
+ // cache
+ QList<QString> ddsKeys = qgl_dds_cache()->keys();
+ for (int i = 0; i < ddsKeys.size(); ++i) {
+ GLuint texture = qgl_dds_cache()->value(ddsKeys.at(i));
+ if (id == texture) {
+ glDeleteTextures(1, &texture);
+ qgl_dds_cache()->remove(ddsKeys.at(i));
+ return;
+ }
+ }
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLContext::deleteTexture(QMacCompatGLuint id)
+{
+ return deleteTexture(GLuint(id));
+}
+#endif
+
+// qpaintengine_opengl.cpp
+#if !defined(QT_OPENGL_ES_2)
+extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array);
+#else
+void qt_add_rect_to_array(const QRectF &r, q_vertexType *array) {};
+#endif
+
+static void qDrawTextureRect(const QRectF &target, GLint textureWidth, GLint textureHeight, GLenum textureTarget)
+{
+ q_vertexType tx = f2vt(1);
+ q_vertexType ty = f2vt(1);
+
+#ifdef QT_OPENGL_ES
+ Q_UNUSED(textureWidth);
+ Q_UNUSED(textureHeight);
+ Q_UNUSED(textureTarget);
+#else
+ if (textureTarget != GL_TEXTURE_2D) {
+ if (textureWidth == -1 || textureHeight == -1) {
+ glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_WIDTH, &textureWidth);
+ glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_HEIGHT, &textureHeight);
+ }
+
+ tx = f2vt(textureWidth);
+ ty = f2vt(textureHeight);
+ }
+#endif
+
+ q_vertexType texCoordArray[4*2] = {
+ 0, ty, tx, ty, tx, 0, 0, 0
+ };
+
+ q_vertexType vertexArray[4*2];
+ qt_add_rect_to_array(target, vertexArray);
+
+#if !defined(QT_OPENGL_ES_2)
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+#endif
+}
+
+/*!
+ \since 4.4
+
+ Draws the given texture, \a textureId, to the given target rectangle,
+ \a target, in OpenGL model space. The \a textureTarget should be a 2D
+ texture target.
+
+ Equivalent to the corresponding QGLContext::drawTexture().
+*/
+void QGLContext::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
+{
+#ifdef QT_OPENGL_ES
+ if (textureTarget != GL_TEXTURE_2D) {
+ qWarning("QGLContext::drawTexture(): texture target must be GL_TEXTURE_2D on OpenGL ES");
+ return;
+ }
+#else
+ const bool wasEnabled = glIsEnabled(GL_TEXTURE_2D);
+ GLint oldTexture;
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTexture);
+#endif
+
+ glEnable(textureTarget);
+ glBindTexture(textureTarget, textureId);
+
+ qDrawTextureRect(target, -1, -1, textureTarget);
+
+#ifdef QT_OPENGL_ES
+ glDisable(textureTarget);
+#else
+ if (!wasEnabled)
+ glDisable(textureTarget);
+ glBindTexture(textureTarget, oldTexture);
+#endif
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLContext::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ drawTexture(target, GLuint(textureId), GLenum(textureTarget));
+}
+#endif
+
+/*!
+ \since 4.4
+
+ Draws the given texture at the given \a point in OpenGL model
+ space. The \a textureTarget should be a 2D texture target.
+
+ Equivalent to the corresponding QGLContext::drawTexture().
+*/
+void QGLContext::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
+{
+ // this would be ok on OpenGL ES 2.0, but currently we don't have a define for that
+#ifdef QT_OPENGL_ES
+ Q_UNUSED(point);
+ Q_UNUSED(textureId);
+ Q_UNUSED(textureTarget);
+ qWarning("drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget) not supported with OpenGL ES, use rect version instead");
+#else
+ const bool wasEnabled = glIsEnabled(GL_TEXTURE_2D);
+ GLint oldTexture;
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTexture);
+
+ glEnable(textureTarget);
+ glBindTexture(textureTarget, textureId);
+
+ GLint textureWidth;
+ GLint textureHeight;
+
+ glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_WIDTH, &textureWidth);
+ glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_HEIGHT, &textureHeight);
+
+ qDrawTextureRect(QRectF(point, QSizeF(textureWidth, textureHeight)), textureWidth, textureHeight, textureTarget);
+
+ if (!wasEnabled)
+ glDisable(textureTarget);
+ glBindTexture(textureTarget, oldTexture);
+#endif
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLContext::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ drawTexture(point, GLuint(textureId), GLenum(textureTarget));
+}
+#endif
+
+
+/*!
+ This function sets the limit for the texture cache to \a size,
+ expressed in kilobytes.
+
+ By default, the cache limit is approximately 64 MB.
+
+ \sa textureCacheLimit()
+*/
+void QGLContext::setTextureCacheLimit(int size)
+{
+ qt_tex_cache_limit = size;
+ if (qt_tex_cache)
+ qt_tex_cache->setMaxCost(qt_tex_cache_limit);
+}
+
+/*!
+ Returns the current texture cache limit in kilobytes.
+
+ \sa setTextureCacheLimit()
+*/
+int QGLContext::textureCacheLimit()
+{
+ return qt_tex_cache_limit;
+}
+
+
+/*!
+ \fn QGLFormat QGLContext::format() const
+
+ Returns the frame buffer format that was obtained (this may be a
+ subset of what was requested).
+
+ \sa requestedFormat()
+*/
+
+/*!
+ \fn QGLFormat QGLContext::requestedFormat() const
+
+ Returns the frame buffer format that was originally requested in
+ the constructor or setFormat().
+
+ \sa format()
+*/
+
+/*!
+ Sets a \a format for this context. The context is \link reset()
+ reset\endlink.
+
+ Call create() to create a new GL context that tries to match the
+ new format.
+
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 7
+
+ \sa format(), reset(), create()
+*/
+
+void QGLContext::setFormat(const QGLFormat &format)
+{
+ Q_D(QGLContext);
+ reset();
+ d->glFormat = d->reqFormat = format;
+}
+
+/*!
+ \internal
+*/
+void QGLContext::setDevice(QPaintDevice *pDev)
+{
+ Q_D(QGLContext);
+ if (isValid())
+ reset();
+ d->paintDevice = pDev;
+ if (d->paintDevice && (d->paintDevice->devType() != QInternal::Widget
+ && d->paintDevice->devType() != QInternal::Pixmap
+ && d->paintDevice->devType() != QInternal::Pbuffer)) {
+ qWarning("QGLContext: Unsupported paint device type");
+ }
+}
+
+/*!
+ \fn bool QGLContext::isValid() const
+
+ Returns true if a GL rendering context has been successfully
+ created; otherwise returns false.
+*/
+
+/*!
+ \fn void QGLContext::setValid(bool valid)
+ \internal
+
+ Forces the GL rendering context to be valid.
+*/
+
+/*!
+ \fn bool QGLContext::isSharing() const
+
+ Returns true if this context is sharing its GL context with
+ another QGLContext, otherwise false is returned. Note that context
+ sharing might not be supported between contexts with different
+ formats.
+*/
+
+/*!
+ \fn bool QGLContext::deviceIsPixmap() const
+
+ Returns true if the paint device of this context is a pixmap;
+ otherwise returns false.
+*/
+
+/*!
+ \fn bool QGLContext::windowCreated() const
+
+ Returns true if a window has been created for this context;
+ otherwise returns false.
+
+ \sa setWindowCreated()
+*/
+
+/*!
+ \fn void QGLContext::setWindowCreated(bool on)
+
+ If \a on is true the context has had a window created for it. If
+ \a on is false no window has been created for the context.
+
+ \sa windowCreated()
+*/
+
+/*!
+ \fn uint QGLContext::colorIndex(const QColor& c) const
+
+ \internal
+
+ Returns a colormap index for the color c, in ColorIndex mode. Used
+ by qglColor() and qglClearColor().
+*/
+
+
+/*!
+ \fn bool QGLContext::initialized() const
+
+ Returns true if this context has been initialized, i.e. if
+ QGLWidget::initializeGL() has been performed on it; otherwise
+ returns false.
+
+ \sa setInitialized()
+*/
+
+/*!
+ \fn void QGLContext::setInitialized(bool on)
+
+ If \a on is true the context has been initialized, i.e.
+ QGLContext::setInitialized() has been called on it. If \a on is
+ false the context has not been initialized.
+
+ \sa initialized()
+*/
+
+/*!
+ \fn const QGLContext* QGLContext::currentContext()
+
+ Returns the current context, i.e. the context to which any OpenGL
+ commands will currently be directed. Returns 0 if no context is
+ current.
+
+ \sa makeCurrent()
+*/
+
+/*!
+ \fn QColor QGLContext::overlayTransparentColor() const
+
+ If this context is a valid context in an overlay plane, returns
+ the plane's transparent color. Otherwise returns an \link
+ QColor::isValid() invalid \endlink color.
+
+ The returned color's \link QColor::pixel() pixel \endlink value is
+ the index of the transparent color in the colormap of the overlay
+ plane. (Naturally, the color's RGB values are meaningless.)
+
+ The returned QColor object will generally work as expected only
+ when passed as the argument to QGLWidget::qglColor() or
+ QGLWidget::qglClearColor(). Under certain circumstances it can
+ also be used to draw transparent graphics with a QPainter. See the
+ examples/opengl/overlay_x11 example for details.
+*/
+
+
+/*!
+ Creates the GL context. Returns true if it was successful in
+ creating a valid GL rendering context on the paint device
+ specified in the constructor; otherwise returns false (i.e. the
+ context is invalid).
+
+ After successful creation, format() returns the set of features of
+ the created GL rendering context.
+
+ If \a shareContext points to a valid QGLContext, this method will
+ try to establish OpenGL display list and texture object sharing
+ between this context and the \a shareContext. Note that this may
+ fail if the two contexts have different \l {format()} {formats}.
+ Use isSharing() to see if sharing is in effect.
+
+ \warning Implementation note: initialization of C++ class
+ members usually takes place in the class constructor. QGLContext
+ is an exception because it must be simple to customize. The
+ virtual functions chooseContext() (and chooseVisual() for X11) can
+ be reimplemented in a subclass to select a particular context. The
+ problem is that virtual functions are not properly called during
+ construction (even though this is correct C++) because C++
+ constructs class hierarchies from the bottom up. For this reason
+ we need a create() function.
+
+ \sa chooseContext(), format(), isValid()
+*/
+
+bool QGLContext::create(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+ if (!d->paintDevice)
+ return false;
+ reset();
+ d->valid = chooseContext(shareContext);
+ if (d->sharing) // ok, we managed to share
+ qgl_share_reg()->addShare(this, shareContext);
+ return d->valid;
+}
+
+bool QGLContext::isValid() const
+{
+ Q_D(const QGLContext);
+ return d->valid;
+}
+
+void QGLContext::setValid(bool valid)
+{
+ Q_D(QGLContext);
+ d->valid = valid;
+}
+
+bool QGLContext::isSharing() const
+{
+ Q_D(const QGLContext);
+ return d->sharing;
+}
+
+QGLFormat QGLContext::format() const
+{
+ Q_D(const QGLContext);
+ return d->glFormat;
+}
+
+QGLFormat QGLContext::requestedFormat() const
+{
+ Q_D(const QGLContext);
+ return d->reqFormat;
+}
+
+ QPaintDevice* QGLContext::device() const
+{
+ Q_D(const QGLContext);
+ return d->paintDevice;
+}
+
+bool QGLContext::deviceIsPixmap() const
+{
+ Q_D(const QGLContext);
+ return d->paintDevice->devType() == QInternal::Pixmap;
+}
+
+
+bool QGLContext::windowCreated() const
+{
+ Q_D(const QGLContext);
+ return d->crWin;
+}
+
+
+void QGLContext::setWindowCreated(bool on)
+{
+ Q_D(QGLContext);
+ d->crWin = on;
+}
+
+bool QGLContext::initialized() const
+{
+ Q_D(const QGLContext);
+ return d->initDone;
+}
+
+void QGLContext::setInitialized(bool on)
+{
+ Q_D(QGLContext);
+ d->initDone = on;
+}
+
+const QGLContext* QGLContext::currentContext()
+{
+ if (qgl_context_storage.hasLocalData())
+ return qgl_context_storage.localData()->context;
+ return 0;
+}
+
+/*!
+ \fn bool QGLContext::chooseContext(const QGLContext* shareContext = 0)
+
+ This semi-internal function is called by create(). It creates a
+ system-dependent OpenGL handle that matches the format() of \a
+ shareContext as closely as possible, returning true if successful
+ or false if a suitable handle could not be found.
+
+ On Windows, it calls the virtual function choosePixelFormat(),
+ which finds a matching pixel format identifier. On X11, it calls
+ the virtual function chooseVisual() which finds an appropriate X
+ visual. On other platforms it may work differently.
+*/
+
+
+/*!
+ \fn void QGLContext::reset()
+
+ Resets the context and makes it invalid.
+
+ \sa create(), isValid()
+*/
+
+
+/*!
+ \fn void QGLContext::makeCurrent()
+
+ Makes this context the current OpenGL rendering context. All GL
+ functions you call operate on this context until another context
+ is made current.
+
+ In some very rare cases the underlying call may fail. If this
+ occurs an error message is output to stderr.
+*/
+
+
+/*!
+ \fn void QGLContext::swapBuffers() const
+
+ Swaps the screen contents with an off-screen buffer. Only works if
+ the context is in double buffer mode.
+
+ \sa QGLFormat::setDoubleBuffer()
+*/
+
+
+/*!
+ \fn void QGLContext::doneCurrent()
+
+ Makes no GL context the current context. Normally, you do not need
+ to call this function; QGLContext calls it as necessary.
+*/
+
+
+/*!
+ \fn QPaintDevice* QGLContext::device() const
+
+ Returns the paint device set for this context.
+
+ \sa QGLContext::QGLContext()
+*/
+
+/*!
+ \obsolete
+ \fn void QGLContext::generateFontDisplayLists(const QFont& font, int listBase)
+
+ Generates a set of 256 display lists for the 256 first characters
+ in the font \a font. The first list will start at index \a listBase.
+
+ \sa QGLWidget::renderText()
+*/
+
+
+/*****************************************************************************
+ QGLWidget implementation
+ *****************************************************************************/
+
+
+/*!
+ \class QGLWidget
+ \brief The QGLWidget class is a widget for rendering OpenGL graphics.
+
+ \ingroup multimedia
+ \mainclass
+
+ QGLWidget provides functionality for displaying OpenGL graphics
+ integrated into a Qt application. It is very simple to use. You
+ inherit from it and use the subclass like any other QWidget,
+ except that you have the choice between using QPainter and
+ standard OpenGL rendering commands.
+
+ QGLWidget provides three convenient virtual functions that you can
+ reimplement in your subclass to perform the typical OpenGL tasks:
+
+ \list
+ \i paintGL() - Renders the OpenGL scene. Gets called whenever the widget
+ needs to be updated.
+ \i resizeGL() - Sets up the OpenGL viewport, projection, etc. Gets
+ called whenever the the widget has been resized (and also when it
+ is shown for the first time because all newly created widgets get a
+ resize event automatically).
+ \i initializeGL() - Sets up the OpenGL rendering context, defines display
+ lists, etc. Gets called once before the first time resizeGL() or
+ paintGL() is called.
+ \endlist
+
+ Here is a rough outline of how a QGLWidget subclass might look:
+
+ \snippet doc/src/snippets/code/src_opengl_qgl.cpp 8
+
+ If you need to trigger a repaint from places other than paintGL()
+ (a typical example is when using \link QTimer timers\endlink to
+ animate scenes), you should call the widget's updateGL() function.
+
+ Your widget's OpenGL rendering context is made current when
+ paintGL(), resizeGL(), or initializeGL() is called. If you need to
+ call the standard OpenGL API functions from other places (e.g. in
+ your widget's constructor or in your own paint functions), you
+ must call makeCurrent() first.
+
+ QGLWidget provides functions for requesting a new display \link
+ QGLFormat format\endlink and you can also create widgets with
+ customized rendering \link QGLContext contexts\endlink.
+
+ You can also share OpenGL display lists between QGLWidgets (see
+ the documentation of the QGLWidget constructors for details).
+
+ Note that under Windows, the QGLContext belonging to a QGLWidget
+ has to be recreated when the QGLWidget is reparented. This is
+ necessary due to limitations on the Windows platform. This will
+ most likely cause problems for users that have subclassed and
+ installed their own QGLContext on a QGLWidget. It is possible to
+ work around this issue by putting the QGLWidget inside a dummy
+ widget and then reparenting the dummy widget, instead of the
+ QGLWidget. This will side-step the issue altogether, and is what
+ we recommend for users that need this kind of functionality.
+
+ \section1 Overlays
+
+ The QGLWidget creates a GL overlay context in addition to the
+ normal context if overlays are supported by the underlying system.
+
+ If you want to use overlays, you specify it in the \link QGLFormat
+ format\endlink. (Note: Overlay must be requested in the format
+ passed to the QGLWidget constructor.) Your GL widget should also
+ implement some or all of these virtual methods:
+
+ \list
+ \i paintOverlayGL()
+ \i resizeOverlayGL()
+ \i initializeOverlayGL()
+ \endlist
+
+ These methods work in the same way as the normal paintGL() etc.
+ functions, except that they will be called when the overlay
+ context is made current. You can explicitly make the overlay
+ context current by using makeOverlayCurrent(), and you can access
+ the overlay context directly (e.g. to ask for its transparent
+ color) by calling overlayContext().
+
+ On X servers in which the default visual is in an overlay plane,
+ non-GL Qt windows can also be used for overlays.
+
+ \section1 Painting Techniques
+
+ As described above, subclass QGLWidget to render pure 3D content in the
+ following way:
+
+ \list
+ \o Reimplement the QGLWidget::initializeGL() and QGLWidget::resizeGL() to
+ set up the OpenGL state and provide a perspective transformation.
+ \o Reimplement QGLWidget::paintGL() to paint the 3D scene, calling only
+ OpenGL functions to draw on the widget.
+ \endlist
+
+ It is also possible to draw 2D graphics onto a QGLWidget subclass, it is necessary
+ to reimplement QGLWidget::paintEvent() and do the following:
+
+ \list
+ \o Construct a QPainter object.
+ \o Initialize it for use on the widget with the QPainter::begin() function.
+ \o Draw primitives using QPainter's member functions.
+ \o Call QPainter::end() to finish painting.
+ \endlist
+
+ Overpainting 2D content on top of 3D content takes a little more effort.
+ One approach to doing this is shown in the
+ \l{Overpainting Example}{Overpainting} example.
+
+ \e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other
+ countries.}
+
+ \sa QGLPixelBuffer, {Hello GL Example}, {2D Painting Example}, {Overpainting Example},
+ {Grabber Example}
+*/
+
+/*!
+ Constructs an OpenGL widget with a \a parent widget.
+
+ The \link QGLFormat::defaultFormat() default format\endlink is
+ used. The widget will be \link isValid() invalid\endlink if the
+ system has no \link QGLFormat::hasOpenGL() OpenGL support\endlink.
+
+ The \a parent and widget flag, \a f, arguments are passed
+ to the QWidget constructor.
+
+ If \a shareWidget is a valid QGLWidget, this widget will share
+ OpenGL display lists and texture objects with \a shareWidget. But
+ if \a shareWidget and this widget have different \l {format()}
+ {formats}, sharing might not be possible. You can check whether
+ sharing is in effect by calling isSharing().
+
+ The initialization of OpenGL rendering state, etc. should be done
+ by overriding the initializeGL() function, rather than in the
+ constructor of your QGLWidget subclass.
+
+ \sa QGLFormat::defaultFormat(), {Textures Example}
+*/
+
+QGLWidget::QGLWidget(QWidget *parent, const QGLWidget* shareWidget, Qt::WindowFlags f)
+ : QWidget(*(new QGLWidgetPrivate), parent, f | Qt::MSWindowsOwnDC)
+{
+ Q_D(QGLWidget);
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAutoFillBackground(true); // for compatibility
+ d->init(new QGLContext(QGLFormat::defaultFormat(), this), shareWidget);
+}
+
+
+/*!
+ Constructs an OpenGL widget with parent \a parent.
+
+ The \a format argument specifies the desired \link QGLFormat
+ rendering options \endlink. If the underlying OpenGL/Window system
+ cannot satisfy all the features requested in \a format, the
+ nearest subset of features will be used. After creation, the
+ format() method will return the actual format obtained.
+
+ The widget will be \link isValid() invalid\endlink if the system
+ has no \link QGLFormat::hasOpenGL() OpenGL support\endlink.
+
+ The \a parent and widget flag, \a f, arguments are passed
+ to the QWidget constructor.
+
+ If \a shareWidget is a valid QGLWidget, this widget will share
+ OpenGL display lists and texture objects with \a shareWidget. But
+ if \a shareWidget and this widget have different \l {format()}
+ {formats}, sharing might not be possible. You can check whether
+ sharing is in effect by calling isSharing().
+
+ The initialization of OpenGL rendering state, etc. should be done
+ by overriding the initializeGL() function, rather than in the
+ constructor of your QGLWidget subclass.
+
+ \sa QGLFormat::defaultFormat(), isValid()
+*/
+
+QGLWidget::QGLWidget(const QGLFormat &format, QWidget *parent, const QGLWidget* shareWidget,
+ Qt::WindowFlags f)
+ : QWidget(*(new QGLWidgetPrivate), parent, f | Qt::MSWindowsOwnDC)
+{
+ Q_D(QGLWidget);
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAutoFillBackground(true); // for compatibility
+ d->init(new QGLContext(format, this), shareWidget);
+}
+
+/*!
+ Constructs an OpenGL widget with parent \a parent.
+
+ The \a context argument is a pointer to the QGLContext that
+ you wish to be bound to this widget. This allows you to pass in
+ your own QGLContext sub-classes.
+
+ The widget will be \link isValid() invalid\endlink if the system
+ has no \link QGLFormat::hasOpenGL() OpenGL support\endlink.
+
+ The \a parent and widget flag, \a f, arguments are passed
+ to the QWidget constructor.
+
+ If \a shareWidget is a valid QGLWidget, this widget will share
+ OpenGL display lists and texture objects with \a shareWidget. But
+ if \a shareWidget and this widget have different \l {format()}
+ {formats}, sharing might not be possible. You can check whether
+ sharing is in effect by calling isSharing().
+
+ The initialization of OpenGL rendering state, etc. should be done
+ by overriding the initializeGL() function, rather than in the
+ constructor of your QGLWidget subclass.
+
+ \sa QGLFormat::defaultFormat(), isValid()
+*/
+QGLWidget::QGLWidget(QGLContext *context, QWidget *parent, const QGLWidget *shareWidget,
+ Qt::WindowFlags f)
+ : QWidget(*(new QGLWidgetPrivate), parent, f | Qt::MSWindowsOwnDC)
+{
+ Q_D(QGLWidget);
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAutoFillBackground(true); // for compatibility
+ d->init(context, shareWidget);
+}
+
+/*!
+ Destroys the widget.
+*/
+
+QGLWidget::~QGLWidget()
+{
+ Q_D(QGLWidget);
+#if defined(GLX_MESA_release_buffers) && defined(QGL_USE_MESA_EXT)
+ bool doRelease = (glcx && glcx->windowCreated());
+#endif
+ delete d->glcx;
+#if defined(Q_WGL)
+ delete d->olcx;
+#endif
+#if defined(GLX_MESA_release_buffers) && defined(QGL_USE_MESA_EXT)
+ if (doRelease)
+ glXReleaseBuffersMESA(x11Display(), winId());
+#endif
+ d->cleanupColormaps();
+
+#ifdef Q_WS_MAC
+ QWidget *current = parentWidget();
+ while (current) {
+ qt_widget_private(current)->glWidgets.removeAll(QWidgetPrivate::GlWidgetInfo(this));
+ if (current->isWindow())
+ break;
+ current = current->parentWidget();
+ };
+#endif
+}
+
+/*!
+ \fn QGLFormat QGLWidget::format() const
+
+ Returns the format of the contained GL rendering context.
+*/
+
+/*!
+ \fn bool QGLWidget::doubleBuffer() const
+
+ Returns true if the contained GL rendering context has double
+ buffering; otherwise returns false.
+
+ \sa QGLFormat::doubleBuffer()
+*/
+
+/*!
+ \fn void QGLWidget::setAutoBufferSwap(bool on)
+
+ If \a on is true automatic GL buffer swapping is switched on;
+ otherwise it is switched off.
+
+ If \a on is true and the widget is using a double-buffered format,
+ the background and foreground GL buffers will automatically be
+ swapped after each paintGL() call.
+
+ The buffer auto-swapping is on by default.
+
+ \sa autoBufferSwap(), doubleBuffer(), swapBuffers()
+*/
+
+/*!
+ \fn bool QGLWidget::autoBufferSwap() const
+
+ Returns true if the widget is doing automatic GL buffer swapping;
+ otherwise returns false.
+
+ \sa setAutoBufferSwap()
+*/
+
+/*!
+ \fn void *QGLContext::getProcAddress(const QString &proc) const
+
+ Returns a function pointer to the GL extension function passed in
+ \a proc. 0 is returned if a pointer to the function could not be
+ obtained.
+*/
+
+/*!
+ \fn bool QGLWidget::isValid() const
+
+ Returns true if the widget has a valid GL rendering context;
+ otherwise returns false. A widget will be invalid if the system
+ has no \link QGLFormat::hasOpenGL() OpenGL support\endlink.
+*/
+
+bool QGLWidget::isValid() const
+{
+ Q_D(const QGLWidget);
+ return d->glcx && d->glcx->isValid();
+}
+
+/*!
+ \fn bool QGLWidget::isSharing() const
+
+ Returns true if this widget's GL context is shared with another GL
+ context, otherwise false is returned. Context sharing might not be
+ possible if the QGLWidgets use different formats.
+
+ \sa format()
+*/
+
+bool QGLWidget::isSharing() const
+{
+ Q_D(const QGLWidget);
+ return d->glcx->isSharing();
+}
+
+/*!
+ \fn void QGLWidget::makeCurrent()
+
+ Makes this widget the current widget for OpenGL operations, i.e.
+ makes the widget's rendering context the current OpenGL rendering
+ context.
+*/
+
+void QGLWidget::makeCurrent()
+{
+ Q_D(QGLWidget);
+ d->glcx->makeCurrent();
+}
+
+/*!
+ \fn void QGLWidget::doneCurrent()
+
+ Makes no GL context the current context. Normally, you do not need
+ to call this function; QGLContext calls it as necessary. However,
+ it may be useful in multithreaded environments.
+*/
+
+void QGLWidget::doneCurrent()
+{
+ Q_D(QGLWidget);
+ d->glcx->doneCurrent();
+}
+
+/*!
+ \fn void QGLWidget::swapBuffers()
+
+ Swaps the screen contents with an off-screen buffer. This only
+ works if the widget's format specifies double buffer mode.
+
+ Normally, there is no need to explicitly call this function
+ because it is done automatically after each widget repaint, i.e.
+ each time after paintGL() has been executed.
+
+ \sa doubleBuffer(), setAutoBufferSwap(), QGLFormat::setDoubleBuffer()
+*/
+
+void QGLWidget::swapBuffers()
+{
+ Q_D(QGLWidget);
+ d->glcx->swapBuffers();
+}
+
+
+/*!
+ \fn const QGLContext* QGLWidget::overlayContext() const
+
+ Returns the overlay context of this widget, or 0 if this widget
+ has no overlay.
+
+ \sa context()
+*/
+
+
+
+/*!
+ \fn void QGLWidget::makeOverlayCurrent()
+
+ Makes the overlay context of this widget current. Use this if you
+ need to issue OpenGL commands to the overlay context outside of
+ initializeOverlayGL(), resizeOverlayGL(), and paintOverlayGL().
+
+ Does nothing if this widget has no overlay.
+
+ \sa makeCurrent()
+*/
+
+
+/*!
+ \obsolete
+
+ Sets a new format for this widget.
+
+ If the underlying OpenGL/Window system cannot satisfy all the
+ features requested in \a format, the nearest subset of features will
+ be used. After creation, the format() method will return the actual
+ rendering context format obtained.
+
+ The widget will be assigned a new QGLContext, and the initializeGL()
+ function will be executed for this new context before the first
+ resizeGL() or paintGL().
+
+ This method will try to keep display list and texture object sharing
+ in effect with other QGLWidgets, but changing the format might make
+ sharing impossible. Use isSharing() to see if sharing is still in
+ effect.
+
+ \sa format(), isSharing(), isValid()
+*/
+
+void QGLWidget::setFormat(const QGLFormat &format)
+{
+ setContext(new QGLContext(format,this));
+}
+
+
+
+
+/*!
+ \fn const QGLContext *QGLWidget::context() const
+
+ Returns the context of this widget.
+
+ It is possible that the context is not valid (see isValid()), for
+ example, if the underlying hardware does not support the format
+ attributes that were requested.
+*/
+
+/*
+ \obsolete
+
+ \fn void QGLWidget::setContext(QGLContext *context,
+ const QGLContext* shareContext,
+ bool deleteOldContext)
+
+ Sets a new context for this widget. The QGLContext \a context must
+ be created using \e new. QGLWidget will delete \a context when
+ another context is set or when the widget is destroyed.
+
+ If \a context is invalid, QGLContext::create() is performed on
+ it. The initializeGL() function will then be executed for the new
+ context before the first resizeGL() or paintGL().
+
+ If \a context is invalid, this method will try to keep display list
+ and texture object sharing in effect, or (if \a shareContext points
+ to a valid context) start display list and texture object sharing
+ with that context, but sharing might be impossible if the two
+ contexts have different \l {format()} {formats}. Use isSharing() to
+ see whether sharing is in effect.
+
+ If \a deleteOldContext is true (the default), the existing context
+ will be deleted. You may use false here if you have kept a pointer
+ to the old context (as returned by context()), and want to restore
+ that context later.
+
+ \sa context(), isSharing()
+*/
+
+
+
+/*!
+ \fn void QGLWidget::updateGL()
+
+ Updates the widget by calling glDraw().
+*/
+
+void QGLWidget::updateGL()
+{
+ if (updatesEnabled())
+ glDraw();
+}
+
+
+/*!
+ \fn void QGLWidget::updateOverlayGL()
+
+ Updates the widget's overlay (if any). Will cause the virtual
+ function paintOverlayGL() to be executed.
+
+ The widget's rendering context will become the current context and
+ initializeGL() will be called if it hasn't already been called.
+*/
+
+
+/*!
+ This virtual function is called once before the first call to
+ paintGL() or resizeGL(), and then once whenever the widget has
+ been assigned a new QGLContext. Reimplement it in a subclass.
+
+ This function should set up any required OpenGL context rendering
+ flags, defining display lists, etc.
+
+ There is no need to call makeCurrent() because this has already
+ been done when this function is called.
+*/
+
+void QGLWidget::initializeGL()
+{
+}
+
+
+/*!
+ This virtual function is called whenever the widget needs to be
+ painted. Reimplement it in a subclass.
+
+ There is no need to call makeCurrent() because this has already
+ been done when this function is called.
+*/
+
+void QGLWidget::paintGL()
+{
+}
+
+
+/*!
+ \fn void QGLWidget::resizeGL(int width , int height)
+
+ This virtual function is called whenever the widget has been
+ resized. The new size is passed in \a width and \a height.
+ Reimplement it in a subclass.
+
+ There is no need to call makeCurrent() because this has already
+ been done when this function is called.
+*/
+
+void QGLWidget::resizeGL(int, int)
+{
+}
+
+
+
+/*!
+ This virtual function is used in the same manner as initializeGL()
+ except that it operates on the widget's overlay context instead of
+ the widget's main context. This means that initializeOverlayGL()
+ is called once before the first call to paintOverlayGL() or
+ resizeOverlayGL(). Reimplement it in a subclass.
+
+ This function should set up any required OpenGL context rendering
+ flags, defining display lists, etc. for the overlay context.
+
+ There is no need to call makeOverlayCurrent() because this has
+ already been done when this function is called.
+*/
+
+void QGLWidget::initializeOverlayGL()
+{
+}
+
+
+/*!
+ This virtual function is used in the same manner as paintGL()
+ except that it operates on the widget's overlay context instead of
+ the widget's main context. This means that paintOverlayGL() is
+ called whenever the widget's overlay needs to be painted.
+ Reimplement it in a subclass.
+
+ There is no need to call makeOverlayCurrent() because this has
+ already been done when this function is called.
+*/
+
+void QGLWidget::paintOverlayGL()
+{
+}
+
+
+/*!
+ \fn void QGLWidget::resizeOverlayGL(int width , int height)
+
+ This virtual function is used in the same manner as paintGL()
+ except that it operates on the widget's overlay context instead of
+ the widget's main context. This means that resizeOverlayGL() is
+ called whenever the widget has been resized. The new size is
+ passed in \a width and \a height. Reimplement it in a subclass.
+
+ There is no need to call makeOverlayCurrent() because this has
+ already been done when this function is called.
+*/
+
+void QGLWidget::resizeOverlayGL(int, int)
+{
+}
+
+
+#if !defined(Q_OS_WINCE) && !defined(Q_WS_QWS)
+/*! \reimp */
+bool QGLWidget::event(QEvent *e)
+{
+ Q_D(QGLWidget);
+
+ if (e->type() == QEvent::Paint) {
+ QPoint offset;
+ QPaintDevice *redirectedDevice = d->redirected(&offset);
+ if (redirectedDevice && redirectedDevice->devType() == QInternal::Pixmap) {
+ d->restoreRedirected();
+ QPixmap pixmap = renderPixmap();
+ d->setRedirected(redirectedDevice, offset);
+ QPainter p(redirectedDevice);
+ p.drawPixmap(-offset, pixmap);
+ return true;
+ }
+ }
+
+#if defined(Q_WS_X11)
+ // prevents X errors on some systems, where we get a flush to a
+ // hidden widget
+ if (e->type() == QEvent::Hide) {
+ makeCurrent();
+ glFinish();
+ doneCurrent();
+ } else if (e->type() == QEvent::ParentChange) {
+ if (d->glcx->d_func()->screen != d->xinfo.screen()) {
+ setContext(new QGLContext(d->glcx->requestedFormat(), this));
+ // ### recreating the overlay isn't supported atm
+ }
+#if defined(QT_OPENGL_ES)
+ // The window may have been re-created during re-parent - if so, the EGL
+ // surface will need to be re-created.
+ d->recreateEglSurface(false);
+#endif
+ }
+#elif defined(Q_WS_WIN)
+ if (e->type() == QEvent::ParentChange) {
+ QGLContext *newContext = new QGLContext(d->glcx->requestedFormat(), this);
+ qgl_share_reg()->replaceShare(d->glcx, newContext);
+ setContext(newContext);
+ // the overlay needs to be recreated as well
+ delete d->olcx;
+ if (isValid() && context()->format().hasOverlay()) {
+ d->olcx = new QGLContext(QGLFormat::defaultOverlayFormat(), this);
+ if (!d->olcx->create(isSharing() ? d->glcx : 0)) {
+ delete d->olcx;
+ d->olcx = 0;
+ d->glcx->d_func()->glFormat.setOverlay(false);
+ }
+ } else {
+ d->olcx = 0;
+ }
+ } else if (e->type() == QEvent::Show) {
+ if (!format().rgba())
+ d->updateColormap();
+ }
+#elif defined(Q_WS_MAC)
+ if (e->type() == QEvent::MacGLWindowChange
+#if 0 //(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ && ((QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5 && isWindow())
+ || QSysInfo::MacintoshVersion <= QSysInfo::MV_10_4)
+#endif
+ ) {
+ if (d->needWindowChange) {
+ d->needWindowChange = false;
+ d->glcx->updatePaintDevice();
+ update();
+ }
+ return true;
+ }
+#endif
+
+ return QWidget::event(e);
+}
+#endif
+
+/*!
+ \fn void QGLWidget::paintEvent(QPaintEvent *event)
+
+ Handles paint events passed in the \a event parameter. Will cause
+ the virtual paintGL() function to be called.
+
+ The widget's rendering context will become the current context and
+ initializeGL() will be called if it hasn't already been called.
+*/
+
+void QGLWidget::paintEvent(QPaintEvent *)
+{
+ if (updatesEnabled()) {
+ glDraw();
+ updateOverlayGL();
+ }
+}
+
+
+/*!
+ \fn void QGLWidget::resizeEvent(QResizeEvent *event)
+
+ Handles resize events that are passed in the \a event parameter.
+ Calls the virtual function resizeGL().
+*/
+
+
+/*!
+ \fn void QGLWidget::setMouseTracking(bool enable)
+
+ If \a enable is true then mouse tracking is enabled; otherwise it
+ is disabled.
+*/
+
+
+/*!
+ Renders the current scene on a pixmap and returns the pixmap.
+
+ You can use this method on both visible and invisible QGLWidgets.
+
+ This method will create a pixmap and a temporary QGLContext to
+ render on the pixmap. It will then call initializeGL(),
+ resizeGL(), and paintGL() on this context. Finally, the widget's
+ original GL context is restored.
+
+ The size of the pixmap will be \a w pixels wide and \a h pixels
+ high unless one of these parameters is 0 (the default), in which
+ case the pixmap will have the same size as the widget.
+
+ If \a useContext is true, this method will try to be more
+ efficient by using the existing GL context to render the pixmap.
+ The default is false. Only use true if you understand the risks.
+ Note that under Windows a temporary context has to be created
+ and usage of the \e useContext parameter is not supported.
+
+ Overlays are not rendered onto the pixmap.
+
+ If the GL rendering context and the desktop have different bit
+ depths, the result will most likely look surprising.
+
+ Note that the creation of display lists, modifications of the view
+ frustum etc. should be done from within initializeGL(). If this is
+ not done, the temporary QGLContext will not be initialized
+ properly, and the rendered pixmap may be incomplete/corrupted.
+*/
+
+QPixmap QGLWidget::renderPixmap(int w, int h, bool useContext)
+{
+ Q_D(QGLWidget);
+ QSize sz = size();
+ if ((w > 0) && (h > 0))
+ sz = QSize(w, h);
+
+#if defined(Q_WS_X11)
+ extern int qt_x11_preferred_pixmap_depth;
+ int old_depth = qt_x11_preferred_pixmap_depth;
+ qt_x11_preferred_pixmap_depth = x11Info().depth();
+
+ QPixmapData *data = new QX11PixmapData(QPixmapData::PixmapType);
+ data->resize(sz.width(), sz.height());
+ QPixmap pm(data);
+ qt_x11_preferred_pixmap_depth = old_depth;
+ QX11Info xinfo = x11Info();
+
+ // make sure we use a pixmap with the same depth/visual as the widget
+ if (xinfo.visual() != QX11Info::appVisual()) {
+ QX11InfoData* xd = pm.x11Info().getX11Data(true);
+ xd->depth = xinfo.depth();
+ xd->visual = static_cast<Visual *>(xinfo.visual());
+ const_cast<QX11Info &>(pm.x11Info()).setX11Data(xd);
+ }
+
+#else
+ QPixmap pm(sz);
+#endif
+
+ d->glcx->doneCurrent();
+
+ bool success = true;
+
+ if (useContext && isValid() && d->renderCxPm(&pm))
+ return pm;
+
+ QGLFormat fmt = d->glcx->requestedFormat();
+ fmt.setDirectRendering(false); // Direct is unlikely to work
+ fmt.setDoubleBuffer(false); // We don't need dbl buf
+#ifdef Q_WS_MAC // crash prevention on the Mac - it's unlikely to work anyway
+ fmt.setSampleBuffers(false);
+#endif
+
+ QGLContext* ocx = d->glcx;
+ ocx->doneCurrent();
+ d->glcx = new QGLContext(fmt, &pm);
+ d->glcx->create();
+
+ if (d->glcx->isValid())
+ updateGL();
+ else
+ success = false;
+
+ delete d->glcx;
+ d->glcx = ocx;
+
+ ocx->makeCurrent();
+
+ if (success) {
+#if defined(Q_WS_X11)
+ if (xinfo.visual() != QX11Info::appVisual()) {
+ QImage image = pm.toImage();
+ QPixmap p = QPixmap::fromImage(image);
+ return p;
+ }
+#endif
+ return pm;
+ }
+ return QPixmap();
+}
+
+/*!
+ Returns an image of the frame buffer. If \a withAlpha is true the
+ alpha channel is included.
+
+ Depending on your hardware, you can explicitly select which color
+ buffer to grab with a glReadBuffer() call before calling this
+ function.
+*/
+QImage QGLWidget::grabFrameBuffer(bool withAlpha)
+{
+ makeCurrent();
+ QImage res;
+ int w = width();
+ int h = height();
+ if (format().rgba()) {
+ res = qt_gl_read_framebuffer(QSize(w, h), format().alpha(), withAlpha);
+ } else {
+#if defined (Q_WS_WIN) && !defined(QT_OPENGL_ES)
+ res = QImage(w, h, QImage::Format_Indexed8);
+ glReadPixels(0, 0, w, h, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, res.bits());
+ const QVector<QColor> pal = QColormap::instance().colormap();
+ if (pal.size()) {
+ res.setNumColors(pal.size());
+ for (int i = 0; i < pal.size(); i++)
+ res.setColor(i, pal.at(i).rgb());
+ }
+ res = res.mirrored();
+#endif
+ }
+
+ return res;
+}
+
+
+
+/*!
+ Initializes OpenGL for this widget's context. Calls the virtual
+ function initializeGL().
+*/
+
+void QGLWidget::glInit()
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ initializeGL();
+ d->glcx->setInitialized(true);
+}
+
+
+/*!
+ Executes the virtual function paintGL().
+
+ The widget's rendering context will become the current context and
+ initializeGL() will be called if it hasn't already been called.
+*/
+
+void QGLWidget::glDraw()
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+#ifndef QT_OPENGL_ES
+ if (d->glcx->deviceIsPixmap())
+ glDrawBuffer(GL_FRONT);
+#endif
+ if (!d->glcx->initialized()) {
+ glInit();
+ resizeGL(d->glcx->device()->width(), d->glcx->device()->height()); // New context needs this "resize"
+ }
+ paintGL();
+ if (doubleBuffer()) {
+ if (d->autoSwap)
+ swapBuffers();
+ } else {
+ glFlush();
+ }
+}
+
+/*!
+ Convenience function for specifying a drawing color to OpenGL.
+ Calls glColor4 (in RGBA mode) or glIndex (in color-index mode)
+ with the color \a c. Applies to this widgets GL context.
+
+ \sa qglClearColor(), QGLContext::currentContext(), QColor
+*/
+
+void QGLWidget::qglColor(const QColor& c) const
+{
+#if !defined(QT_OPENGL_ES_2)
+#ifdef QT_OPENGL_ES
+ glColor4f(c.red()/255.0, c.green()/255.0, c.blue()/255.0, c.alpha()/255.0);
+#else
+ Q_D(const QGLWidget);
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (ctx) {
+ if (ctx->format().rgba())
+ glColor4ub(c.red(), c.green(), c.blue(), c.alpha());
+ else if (!d->cmap.isEmpty()) { // QGLColormap in use?
+ int i = d->cmap.find(c.rgb());
+ if (i < 0)
+ i = d->cmap.findNearest(c.rgb());
+ glIndexi(i);
+ } else
+ glIndexi(ctx->colorIndex(c));
+ }
+#endif //QT_OPENGL_ES
+#endif //QT_OPENGL_ES_2
+}
+
+/*!
+ Convenience function for specifying the clearing color to OpenGL.
+ Calls glClearColor (in RGBA mode) or glClearIndex (in color-index
+ mode) with the color \a c. Applies to this widgets GL context.
+
+ \sa qglColor(), QGLContext::currentContext(), QColor
+*/
+
+void QGLWidget::qglClearColor(const QColor& c) const
+{
+#ifdef QT_OPENGL_ES
+ glClearColor((GLfloat)c.red() / 255.0, (GLfloat)c.green() / 255.0,
+ (GLfloat)c.blue() / 255.0, (GLfloat) c.alpha() / 255.0);
+#else
+ Q_D(const QGLWidget);
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (ctx) {
+ if (ctx->format().rgba())
+ glClearColor((GLfloat)c.red() / 255.0, (GLfloat)c.green() / 255.0,
+ (GLfloat)c.blue() / 255.0, (GLfloat) c.alpha() / 255.0);
+ else if (!d->cmap.isEmpty()) { // QGLColormap in use?
+ int i = d->cmap.find(c.rgb());
+ if (i < 0)
+ i = d->cmap.findNearest(c.rgb());
+ glClearIndex(i);
+ } else
+ glClearIndex(ctx->colorIndex(c));
+ }
+#endif
+}
+
+
+/*!
+ Converts the image \a img into the unnamed format expected by
+ OpenGL functions such as glTexImage2D(). The returned image is not
+ usable as a QImage, but QImage::width(), QImage::height() and
+ QImage::bits() may be used with OpenGL. The GL format used is
+ \c GL_RGBA.
+
+ \omit ###
+
+ \l opengl/texture example
+ The following few lines are from the texture example. Most of the
+ code is irrelevant, so we just quote the relevant bits:
+
+ \quotefromfile opengl/texture/gltexobj.cpp
+ \skipto tex1
+ \printline tex1
+ \printline gllogo.bmp
+
+ We create \e tex1 (and another variable) for OpenGL, and load a real
+ image into \e buf.
+
+ \skipto convertToGLFormat
+ \printline convertToGLFormat
+
+ A few lines later, we convert \e buf into OpenGL format and store it
+ in \e tex1.
+
+ \skipto glTexImage2D
+ \printline glTexImage2D
+ \printline tex1.bits
+
+ Note the dimension restrictions for texture images as described in
+ the glTexImage2D() documentation. The width must be 2^m + 2*border
+ and the height 2^n + 2*border where m and n are integers and
+ border is either 0 or 1.
+
+ Another function in the same example uses \e tex1 with OpenGL.
+
+ \endomit
+*/
+
+QImage QGLWidget::convertToGLFormat(const QImage& img)
+{
+ QImage res(img.size(), QImage::Format_ARGB32);
+ convertToGLFormatHelper(res, img.convertToFormat(QImage::Format_ARGB32), GL_RGBA);
+ return res;
+}
+
+
+/*!
+ \fn QGLColormap & QGLWidget::colormap() const
+
+ Returns the colormap for this widget.
+
+ Usually it is only top-level widgets that can have different
+ colormaps installed. Asking for the colormap of a child widget
+ will return the colormap for the child's top-level widget.
+
+ If no colormap has been set for this widget, the QColormap
+ returned will be empty.
+
+ \sa setColormap()
+*/
+
+/*!
+ \fn void QGLWidget::setColormap(const QGLColormap & cmap)
+
+ Set the colormap for this widget to \a cmap. Usually it is only
+ top-level widgets that can have colormaps installed.
+
+ \sa colormap()
+*/
+
+
+/*!
+ \obsolete
+
+ Returns the value of the first display list that is generated for
+ the characters in the given \a font. \a listBase indicates the base
+ value used when generating the display lists for the font. The
+ default value is 2000.
+*/
+int QGLWidget::fontDisplayListBase(const QFont & font, int listBase)
+{
+ Q_D(QGLWidget);
+ int base;
+
+ if (!d->glcx) { // this can't happen unless we run out of mem
+ return 0;
+ }
+
+ // always regenerate font disp. lists for pixmaps - hw accelerated
+ // contexts can't handle this otherwise
+ bool regenerate = d->glcx->deviceIsPixmap();
+#ifndef QT_NO_FONTCONFIG
+ // font color needs to be part of the font cache key when using
+ // antialiased fonts since one set of glyphs needs to be generated
+ // for each font color
+ QString color_key;
+ if (font.styleStrategy() != QFont::NoAntialias) {
+ GLfloat color[4];
+#ifndef QT_OPENGL_ES
+ glGetFloatv(GL_CURRENT_COLOR, color);
+#endif
+ color_key.sprintf("%f_%f_%f",color[0], color[1], color[2]);
+ }
+ QString key = font.key() + color_key + QString::number((int) regenerate);
+#else
+ QString key = font.key() + QString::number((int) regenerate);
+#endif
+ if (!regenerate && (d->displayListCache.find(key) != d->displayListCache.end())) {
+ base = d->displayListCache[key];
+ } else {
+ int maxBase = listBase - 256;
+ QMap<QString,int>::ConstIterator it;
+ for (it = d->displayListCache.constBegin(); it != d->displayListCache.constEnd(); ++it) {
+ if (maxBase < it.value()) {
+ maxBase = it.value();
+ }
+ }
+ maxBase += 256;
+ d->glcx->generateFontDisplayLists(font, maxBase);
+ d->displayListCache[key] = maxBase;
+ base = maxBase;
+ }
+ return base;
+}
+
+static void qt_save_gl_state()
+{
+#ifndef QT_OPENGL_ES
+ glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
+ glPushAttrib(GL_ALL_ATTRIB_BITS);
+#endif
+#if !defined(QT_OPENGL_ES_2)
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ glShadeModel(GL_FLAT);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_LIGHTING);
+ glDisable(GL_STENCIL_TEST);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+#endif // !defined(QT_OPENGL_ES_2)
+}
+
+static void qt_restore_gl_state()
+{
+#if !defined(QT_OPENGL_ES_2)
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+#endif // !defined(QT_OPENGL_ES_2)
+#ifndef QT_OPENGL_ES
+ glPopAttrib();
+ glPopClientAttrib();
+#endif
+}
+
+static void qt_gl_draw_text(QPainter *p, int x, int y, const QString &str,
+ const QFont &font)
+{
+ GLfloat color[4];
+#ifndef QT_OPENGL_ES
+ glGetFloatv(GL_CURRENT_COLOR, &color[0]);
+#endif
+
+ QColor col;
+ col.setRgbF(color[0], color[1], color[2],color[3]);
+ QPen old_pen = p->pen();
+ QFont old_font = p->font();
+
+ p->setPen(col);
+ p->setFont(font);
+ p->drawText(x, y, str);
+
+ p->setPen(old_pen);
+ p->setFont(old_font);
+}
+
+/*!
+ Renders the string \a str into the GL context of this widget.
+
+ \a x and \a y are specified in window coordinates, with the origin
+ in the upper left-hand corner of the window. If \a font is not
+ specified, the currently set application font will be used to
+ render the string. To change the color of the rendered text you can
+ use the glColor() call (or the qglColor() convenience function),
+ just before the renderText() call.
+
+ The \a listBase parameter is obsolete and will be removed in a
+ future version of Qt.
+
+ \note This function clears the stencil buffer.
+*/
+
+void QGLWidget::renderText(int x, int y, const QString &str, const QFont &font, int)
+{
+ Q_D(QGLWidget);
+ if (str.isEmpty() || !isValid())
+ return;
+
+ GLint view[4];
+#ifndef QT_OPENGL_ES
+ bool use_scissor_testing = glIsEnabled(GL_SCISSOR_TEST);
+ if (!use_scissor_testing)
+ glGetIntegerv(GL_VIEWPORT, &view[0]);
+#else
+ bool use_scissor_testing = false;
+#endif
+ int width = d->glcx->device()->width();
+ int height = d->glcx->device()->height();
+ bool auto_swap = autoBufferSwap();
+
+ QPaintEngine *engine = paintEngine();
+ QPainter *p;
+ bool reuse_painter = false;
+ if (engine->isActive()) {
+ reuse_painter = true;
+ p = engine->painter();
+ qt_save_gl_state();
+
+#if !defined(QT_OPENGL_ES_2)
+ glDisable(GL_DEPTH_TEST);
+ glViewport(0, 0, width, height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+#ifndef QT_OPENGL_ES
+ glOrtho(0, width, height, 0, 0, 1);
+#else
+ glOrthof(0, width, height, 0, 0, 1);
+#endif
+ glMatrixMode(GL_MODELVIEW);
+
+ glLoadIdentity();
+#endif // !defined(QT_OPENGL_ES_2)
+ } else {
+ setAutoBufferSwap(false);
+ // disable glClear() as a result of QPainter::begin()
+ d->glcx->d_func()->clear_on_painter_begin = false;
+ p = new QPainter(this);
+ }
+
+ QRect viewport(view[0], view[1], view[2], view[3]);
+ if (!use_scissor_testing && viewport != rect()) {
+ // if the user hasn't set a scissor box, we set one that
+ // covers the current viewport
+ glScissor(view[0], view[1], view[2], view[3]);
+ glEnable(GL_SCISSOR_TEST);
+ } else if (use_scissor_testing) {
+ // use the scissor box set by the user
+ glEnable(GL_SCISSOR_TEST);
+ }
+
+ qt_gl_draw_text(p, x, y, str, font);
+
+ if (reuse_painter) {
+ qt_restore_gl_state();
+ } else {
+ p->end();
+ delete p;
+ setAutoBufferSwap(auto_swap);
+ d->glcx->d_func()->clear_on_painter_begin = true;
+ }
+}
+
+/*! \overload
+
+ \a x, \a y and \a z are specified in scene or object coordinates
+ relative to the currently set projection and model matrices. This
+ can be useful if you want to annotate models with text labels and
+ have the labels move with the model as it is rotated etc.
+*/
+void QGLWidget::renderText(double x, double y, double z, const QString &str, const QFont &font, int)
+{
+ Q_D(QGLWidget);
+ if (str.isEmpty() || !isValid())
+ return;
+
+ bool auto_swap = autoBufferSwap();
+
+ int width = d->glcx->device()->width();
+ int height = d->glcx->device()->height();
+ GLdouble model[4][4], proj[4][4];
+ GLint view[4];
+#ifndef QT_OPENGL_ES
+ glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]);
+ glGetDoublev(GL_PROJECTION_MATRIX, &proj[0][0]);
+ glGetIntegerv(GL_VIEWPORT, &view[0]);
+#endif
+ GLdouble win_x = 0, win_y = 0, win_z = 0;
+ qgluProject(x, y, z, &model[0][0], &proj[0][0], &view[0],
+ &win_x, &win_y, &win_z);
+ win_y = height - win_y; // y is inverted
+
+ QPaintEngine *engine = paintEngine();
+ QPainter *p;
+ bool reuse_painter = false;
+#ifndef QT_OPENGL_ES
+ bool use_depth_testing = glIsEnabled(GL_DEPTH_TEST);
+ bool use_scissor_testing = glIsEnabled(GL_SCISSOR_TEST);
+#else
+ bool use_depth_testing = false;
+ bool use_scissor_testing = false;
+#endif
+
+ if (engine->isActive()) {
+ reuse_painter = true;
+ p = engine->painter();
+ qt_save_gl_state();
+ } else {
+ setAutoBufferSwap(false);
+ // disable glClear() as a result of QPainter::begin()
+ d->glcx->d_func()->clear_on_painter_begin = false;
+ p = new QPainter(this);
+ }
+
+ QRect viewport(view[0], view[1], view[2], view[3]);
+ if (!use_scissor_testing && viewport != rect()) {
+ glScissor(view[0], view[1], view[2], view[3]);
+ glEnable(GL_SCISSOR_TEST);
+ } else if (use_scissor_testing) {
+ glEnable(GL_SCISSOR_TEST);
+ }
+#if !defined(QT_OPENGL_ES_2)
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glViewport(0, 0, width, height);
+#ifndef QT_OPENGL_ES
+ glOrtho(0, width, height, 0, 0, 1);
+#else
+ glOrthof(0, width, height, 0, 0, 1);
+#endif
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glAlphaFunc(GL_GREATER, 0.0);
+ glEnable(GL_ALPHA_TEST);
+ if (use_depth_testing)
+ glEnable(GL_DEPTH_TEST);
+#ifndef QT_OPENGL_ES
+ glTranslated(0, 0, -win_z);
+#else
+ glTranslatef(0, 0, -win_z);
+#endif
+#endif // !defined(QT_OPENGL_ES_2)
+ qt_gl_draw_text(p, qRound(win_x), qRound(win_y), str, font);
+
+ if (reuse_painter) {
+ qt_restore_gl_state();
+ } else {
+ p->end();
+ delete p;
+ setAutoBufferSwap(auto_swap);
+ d->glcx->d_func()->clear_on_painter_begin = true;
+ }
+}
+
+QGLFormat QGLWidget::format() const
+{
+ Q_D(const QGLWidget);
+ return d->glcx->format();
+}
+
+const QGLContext *QGLWidget::context() const
+{
+ Q_D(const QGLWidget);
+ return d->glcx;
+}
+
+bool QGLWidget::doubleBuffer() const
+{
+ Q_D(const QGLWidget);
+ return d->glcx->d_ptr->glFormat.testOption(QGL::DoubleBuffer);
+}
+
+void QGLWidget::setAutoBufferSwap(bool on)
+{
+ Q_D(QGLWidget);
+ d->autoSwap = on;
+}
+
+bool QGLWidget::autoBufferSwap() const
+{
+ Q_D(const QGLWidget);
+ return d->autoSwap;
+}
+
+/*!
+ Calls QGLContext:::bindTexture(\a image, \a target, \a format) on the currently
+ set context.
+
+ \sa deleteTexture()
+*/
+GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format)
+{
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(image, target, format);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+GLuint QGLWidget::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format)
+{
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(image, GLenum(target), GLint(format));
+}
+#endif
+
+/*!
+ Calls QGLContext:::bindTexture(\a pixmap, \a target, \a format) on the currently
+ set context.
+
+ \sa deleteTexture()
+*/
+GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format)
+{
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(pixmap, target, format);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+GLuint QGLWidget::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format)
+{
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(pixmap, target, format);
+}
+#endif
+
+
+/*! \overload
+
+ Calls QGLContext::bindTexture(\a fileName) on the currently set context.
+
+ \sa deleteTexture()
+*/
+GLuint QGLWidget::bindTexture(const QString &fileName)
+{
+ Q_D(QGLWidget);
+ return d->glcx->bindTexture(fileName);
+}
+
+/*!
+ Calls QGLContext::deleteTexture(\a id) on the currently set
+ context.
+
+ \sa bindTexture()
+*/
+void QGLWidget::deleteTexture(GLuint id)
+{
+ Q_D(QGLWidget);
+ d->glcx->deleteTexture(id);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLWidget::deleteTexture(QMacCompatGLuint id)
+{
+ Q_D(QGLWidget);
+ d->glcx->deleteTexture(GLuint(id));
+}
+#endif
+
+/*!
+ \since 4.4
+
+ Draws the given texture, \a textureId to the given target rectangle,
+ \a target, in OpenGL model space. The \a textureTarget should be a 2D
+ texture target.
+
+ Equivalent to the corresponding QGLContext::drawTexture().
+*/
+void QGLWidget::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
+{
+ Q_D(QGLWidget);
+ d->glcx->drawTexture(target, textureId, textureTarget);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLWidget::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ Q_D(QGLWidget);
+ d->glcx->drawTexture(target, GLint(textureId), GLenum(textureTarget));
+}
+#endif
+
+/*!
+ \since 4.4
+
+ Draws the given texture, \a textureId, at the given \a point in OpenGL
+ model space. The \a textureTarget should be a 2D texture target.
+
+ Equivalent to the corresponding QGLContext::drawTexture().
+*/
+void QGLWidget::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
+{
+ Q_D(QGLWidget);
+ d->glcx->drawTexture(point, textureId, textureTarget);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLWidget::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ Q_D(QGLWidget);
+ d->glcx->drawTexture(point, GLuint(textureId), GLenum(textureTarget));
+}
+#endif
+
+#if defined(QT_OPENGL_ES_2)
+Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_engine)
+#else
+Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_engine)
+#endif
+
+#ifdef Q_WS_QWS
+Q_OPENGL_EXPORT QOpenGLPaintEngine* qt_qgl_paint_engine()
+{
+#if !defined(QT_OPENGL_ES_2)
+ return qt_gl_engine();
+#else
+ return 0; // XXX
+#endif
+}
+#endif
+
+/*!
+ \internal
+
+ Returns the GL widget's paint engine. This is normally a
+ QOpenGLPaintEngine.
+*/
+QPaintEngine *QGLWidget::paintEngine() const
+{
+ return qt_gl_engine();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \overload
+ \obsolete
+ */
+QGLWidget::QGLWidget(QWidget *parent, const char *name,
+ const QGLWidget* shareWidget, Qt::WindowFlags f)
+ : QWidget(*(new QGLWidgetPrivate), parent, f | Qt::MSWindowsOwnDC)
+{
+ Q_D(QGLWidget);
+ if (name)
+ setObjectName(QString::fromAscii(name));
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAutoFillBackground(true); // for compatibility
+ d->init(new QGLContext(QGLFormat::defaultFormat(), this), shareWidget);
+}
+
+/*!
+ \overload
+ \obsolete
+ */
+QGLWidget::QGLWidget(const QGLFormat &format, QWidget *parent,
+ const char *name, const QGLWidget* shareWidget,
+ Qt::WindowFlags f)
+ : QWidget(*(new QGLWidgetPrivate), parent, f | Qt::MSWindowsOwnDC)
+{
+ Q_D(QGLWidget);
+ if (name)
+ setObjectName(QString::fromAscii(name));
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAutoFillBackground(true); // for compatibility
+ d->init(new QGLContext(format, this), shareWidget);
+}
+
+/*!
+ \overload
+ \obsolete
+ */
+QGLWidget::QGLWidget(QGLContext *context, QWidget *parent,
+ const char *name, const QGLWidget *shareWidget, Qt::WindowFlags f)
+ : QWidget(*(new QGLWidgetPrivate), parent, f | Qt::MSWindowsOwnDC)
+{
+ Q_D(QGLWidget);
+ if (name)
+ setObjectName(QString::fromAscii(name));
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAutoFillBackground(true); // for compatibility
+ d->init(context, shareWidget);
+}
+
+#endif // QT3_SUPPORT
+
+void QGLExtensions::init_extensions()
+{
+ QString extensions = QLatin1String(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
+ if (extensions.contains(QLatin1String("texture_rectangle")))
+ glExtensions |= TextureRectangle;
+ if (extensions.contains(QLatin1String("multisample")))
+ glExtensions |= SampleBuffers;
+ if (extensions.contains(QLatin1String("generate_mipmap")))
+ glExtensions |= GenerateMipmap;
+ if (extensions.contains(QLatin1String("texture_compression_s3tc")))
+ glExtensions |= TextureCompression;
+ if (extensions.contains(QLatin1String("ARB_fragment_program")))
+ glExtensions |= FragmentProgram;
+ if (extensions.contains(QLatin1String("mirrored_repeat")))
+ glExtensions |= MirroredRepeat;
+ if (extensions.contains(QLatin1String("EXT_framebuffer_object")))
+ glExtensions |= FramebufferObject;
+ if (extensions.contains(QLatin1String("EXT_stencil_two_side")))
+ glExtensions |= StencilTwoSide;
+ if (extensions.contains(QLatin1String("EXT_stencil_wrap")))
+ glExtensions |= StencilWrap;
+ if (extensions.contains(QLatin1String("EXT_packed_depth_stencil")))
+ glExtensions |= PackedDepthStencil;
+ if (extensions.contains(QLatin1String("GL_NV_float_buffer")))
+ glExtensions |= NVFloatBuffer;
+ if (extensions.contains(QLatin1String("ARB_pixel_buffer_object")))
+ glExtensions |= PixelBufferObject;
+#if defined(QT_OPENGL_ES_2)
+ glExtensions |= FramebufferObject;
+ glExtensions |= GenerateMipmap;
+#endif
+
+ QGLContext cx(QGLFormat::defaultFormat());
+ if (glExtensions & TextureCompression) {
+ qt_glCompressedTexImage2DARB = (pfn_glCompressedTexImage2DARB) cx.getProcAddress(QLatin1String("glCompressedTexImage2DARB"));
+ }
+}
+
+/*
+ This is the shared initialization for all platforms. Called from QGLWidgetPrivate::init()
+*/
+void QGLWidgetPrivate::initContext(QGLContext *context, const QGLWidget* shareWidget)
+{
+ Q_Q(QGLWidget);
+
+ QGLExtensions::init();
+ glcx = 0;
+ autoSwap = true;
+
+ if (context && !context->device())
+ context->setDevice(q);
+ q->setContext(context, shareWidget ? shareWidget->context() : 0);
+
+ if (!glcx)
+ glcx = new QGLContext(QGLFormat::defaultFormat(), q);
+
+ q->setAttribute(Qt::WA_NoSystemBackground);
+}
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS)
+Q_GLOBAL_STATIC(QString, qt_gl_lib_name);
+
+Q_OPENGL_EXPORT void qt_set_gl_library_name(const QString& name)
+{
+ qt_gl_lib_name()->operator=(name);
+}
+
+Q_OPENGL_EXPORT const QString qt_gl_library_name()
+{
+ if (qt_gl_lib_name()->isNull()) {
+#if defined(Q_WS_X11) || defined(Q_WS_QWS)
+ return QString(QLatin1String("GL"));
+#else // Q_WS_MAC
+ return QLatin1String("/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib");
+#endif
+ }
+ return *qt_gl_lib_name();
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h
new file mode 100644
index 0000000000..01b1d6fc12
--- /dev/null
+++ b/src/opengl/qgl.h
@@ -0,0 +1,566 @@
+/****************************************************************************
+**
+** 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 QGL_H
+#define QGL_H
+
+#include <QtGui/qwidget.h>
+#include <QtOpenGL/qglcolormap.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_HEADER
+
+#if defined(Q_WS_WIN)
+# include <QtCore/qt_windows.h>
+#endif
+
+#if defined(Q_WS_MAC)
+# include <OpenGL/gl.h>
+# include <OpenGL/glu.h>
+#elif defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL)
+# include <GLES/gl.h>
+#ifndef GL_DOUBLE
+# define GL_DOUBLE GL_FLOAT
+#endif
+#ifndef GLdouble
+typedef GLfloat GLdouble;
+#endif
+#elif defined(QT_OPENGL_ES_2)
+# include <GLES2/gl2.h>
+#ifndef GL_DOUBLE
+# define GL_DOUBLE GL_FLOAT
+#endif
+#ifndef GLdouble
+typedef GLfloat GLdouble;
+#endif
+#else
+# include <GL/gl.h>
+# ifndef QT_LINUXBASE
+# include <GL/glu.h>
+# endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+#if defined(Q_WS_MAC) && defined (QT_BUILD_OPENGL_LIB) && !defined(QT_MAC_USE_COCOA) && !defined(QDOC)
+#define Q_MAC_COMPAT_GL_FUNCTIONS
+
+template <typename T>
+struct QMacGLCompatTypes
+{
+ typedef long CompatGLint;
+ typedef unsigned long CompatGLuint;
+ typedef unsigned long CompatGLenum;
+};
+
+template <>
+struct QMacGLCompatTypes<long>
+{
+ typedef int CompatGLint;
+ typedef unsigned int CompatGLuint;
+ typedef unsigned int CompatGLenum;
+};
+
+typedef QMacGLCompatTypes<GLint>::CompatGLint QMacCompatGLint;
+typedef QMacGLCompatTypes<GLint>::CompatGLuint QMacCompatGLuint;
+typedef QMacGLCompatTypes<GLint>::CompatGLenum QMacCompatGLenum;
+
+#endif
+
+#ifdef QT3_SUPPORT
+#define QGL_VERSION 460
+#define QGL_VERSION_STR "4.6"
+inline QT3_SUPPORT const char *qGLVersion() {
+ return QGL_VERSION_STR;
+}
+#endif
+
+#if defined(Q_WS_WIN) || defined(Q_WS_MAC)
+class QGLCmap;
+#endif
+
+class QPixmap;
+#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
+class QGLOverlayWidget;
+#endif
+class QGLWidgetPrivate;
+class QGLContextPrivate;
+
+// Namespace class:
+namespace QGL
+{
+ enum FormatOption {
+ DoubleBuffer = 0x0001,
+ DepthBuffer = 0x0002,
+ Rgba = 0x0004,
+ AlphaChannel = 0x0008,
+ AccumBuffer = 0x0010,
+ StencilBuffer = 0x0020,
+ StereoBuffers = 0x0040,
+ DirectRendering = 0x0080,
+ HasOverlay = 0x0100,
+ SampleBuffers = 0x0200,
+ SingleBuffer = DoubleBuffer << 16,
+ NoDepthBuffer = DepthBuffer << 16,
+ ColorIndex = Rgba << 16,
+ NoAlphaChannel = AlphaChannel << 16,
+ NoAccumBuffer = AccumBuffer << 16,
+ NoStencilBuffer = StencilBuffer << 16,
+ NoStereoBuffers = StereoBuffers << 16,
+ IndirectRendering = DirectRendering << 16,
+ NoOverlay = HasOverlay << 16,
+ NoSampleBuffers = SampleBuffers << 16
+ };
+ Q_DECLARE_FLAGS(FormatOptions, FormatOption)
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGL::FormatOptions)
+
+class QGLFormatPrivate;
+
+class Q_OPENGL_EXPORT QGLFormat
+{
+public:
+ QGLFormat();
+ QGLFormat(QGL::FormatOptions options, int plane = 0);
+ QGLFormat(const QGLFormat &other);
+ QGLFormat &operator=(const QGLFormat &other);
+ ~QGLFormat();
+
+ void setDepthBufferSize(int size);
+ int depthBufferSize() const;
+
+ void setAccumBufferSize(int size);
+ int accumBufferSize() const;
+
+ void setRedBufferSize(int size);
+ int redBufferSize() const;
+
+ void setGreenBufferSize(int size);
+ int greenBufferSize() const;
+
+ void setBlueBufferSize(int size);
+ int blueBufferSize() const;
+
+ void setAlphaBufferSize(int size);
+ int alphaBufferSize() const;
+
+ void setStencilBufferSize(int size);
+ int stencilBufferSize() const;
+
+ void setSampleBuffers(bool enable);
+ bool sampleBuffers() const;
+
+ void setSamples(int numSamples);
+ int samples() const;
+
+ void setSwapInterval(int interval);
+ int swapInterval() const;
+
+ bool doubleBuffer() const;
+ void setDoubleBuffer(bool enable);
+ bool depth() const;
+ void setDepth(bool enable);
+ bool rgba() const;
+ void setRgba(bool enable);
+ bool alpha() const;
+ void setAlpha(bool enable);
+ bool accum() const;
+ void setAccum(bool enable);
+ bool stencil() const;
+ void setStencil(bool enable);
+ bool stereo() const;
+ void setStereo(bool enable);
+ bool directRendering() const;
+ void setDirectRendering(bool enable);
+ bool hasOverlay() const;
+ void setOverlay(bool enable);
+
+ int plane() const;
+ void setPlane(int plane);
+
+ void setOption(QGL::FormatOptions opt);
+ bool testOption(QGL::FormatOptions opt) const;
+
+ static QGLFormat defaultFormat();
+ static void setDefaultFormat(const QGLFormat& f);
+
+ static QGLFormat defaultOverlayFormat();
+ static void setDefaultOverlayFormat(const QGLFormat& f);
+
+ static bool hasOpenGL();
+ static bool hasOpenGLOverlays();
+
+ enum OpenGLVersionFlag {
+ OpenGL_Version_None = 0x00000000,
+ OpenGL_Version_1_1 = 0x00000001,
+ OpenGL_Version_1_2 = 0x00000002,
+ OpenGL_Version_1_3 = 0x00000004,
+ OpenGL_Version_1_4 = 0x00000008,
+ OpenGL_Version_1_5 = 0x00000010,
+ OpenGL_Version_2_0 = 0x00000020,
+ OpenGL_Version_2_1 = 0x00000040,
+ OpenGL_ES_Common_Version_1_0 = 0x00000080,
+ OpenGL_ES_CommonLite_Version_1_0 = 0x00000100,
+ OpenGL_ES_Common_Version_1_1 = 0x00000200,
+ OpenGL_ES_CommonLite_Version_1_1 = 0x00000400,
+ OpenGL_ES_Version_2_0 = 0x00000800,
+ OpenGL_Version_3_0 = 0x00001000
+ };
+ Q_DECLARE_FLAGS(OpenGLVersionFlags, OpenGLVersionFlag)
+
+ static OpenGLVersionFlags openGLVersionFlags();
+
+private:
+ QGLFormatPrivate *d;
+
+ friend Q_OPENGL_EXPORT bool operator==(const QGLFormat&, const QGLFormat&);
+ friend Q_OPENGL_EXPORT bool operator!=(const QGLFormat&, const QGLFormat&);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGLFormat::OpenGLVersionFlags)
+
+Q_OPENGL_EXPORT bool operator==(const QGLFormat&, const QGLFormat&);
+Q_OPENGL_EXPORT bool operator!=(const QGLFormat&, const QGLFormat&);
+
+class Q_OPENGL_EXPORT QGLContext
+{
+ Q_DECLARE_PRIVATE(QGLContext)
+public:
+ QGLContext(const QGLFormat& format, QPaintDevice* device);
+ QGLContext(const QGLFormat& format);
+ virtual ~QGLContext();
+
+ virtual bool create(const QGLContext* shareContext = 0);
+ bool isValid() const;
+ bool isSharing() const;
+ void reset();
+
+ // ### Qt 5: make format() return a const ref instead
+ QGLFormat format() const;
+ QGLFormat requestedFormat() const;
+ void setFormat(const QGLFormat& format);
+
+ // ### Qt 5: return bools + maybe remove virtuals
+ virtual void makeCurrent();
+ virtual void doneCurrent();
+
+ virtual void swapBuffers() const;
+
+ GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D,
+ GLint format = GL_RGBA);
+ GLuint bindTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D,
+ GLint format = GL_RGBA);
+ GLuint bindTexture(const QString &fileName);
+
+ void deleteTexture(GLuint tx_id);
+
+ void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ GLuint bindTexture(const QImage &image, QMacCompatGLenum = GL_TEXTURE_2D,
+ QMacCompatGLint format = GL_RGBA);
+ GLuint bindTexture(const QPixmap &pixmap, QMacCompatGLenum = GL_TEXTURE_2D,
+ QMacCompatGLint format = GL_RGBA);
+
+ void deleteTexture(QMacCompatGLuint tx_id);
+
+ void drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+#endif
+
+ static void setTextureCacheLimit(int size);
+ static int textureCacheLimit();
+
+ void *getProcAddress(const QString &proc) const;
+ QPaintDevice* device() const;
+ QColor overlayTransparentColor() const;
+
+ static const QGLContext* currentContext();
+
+protected:
+ virtual bool chooseContext(const QGLContext* shareContext = 0);
+
+#if defined(Q_WS_WIN)
+ virtual int choosePixelFormat(void* pfd, HDC pdc);
+#endif
+#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
+ virtual void* tryVisual(const QGLFormat& f, int bufDepth = 1);
+ virtual void* chooseVisual();
+#endif
+#if defined(Q_WS_MAC)
+ virtual void* chooseMacVisual(GDHandle);
+#endif
+
+ bool deviceIsPixmap() const;
+ bool windowCreated() const;
+ void setWindowCreated(bool on);
+ bool initialized() const;
+ void setInitialized(bool on);
+ void generateFontDisplayLists(const QFont & fnt, int listBase); // ### Qt 5: remove
+
+ uint colorIndex(const QColor& c) const;
+ void setValid(bool valid);
+ void setDevice(QPaintDevice *pDev);
+
+protected:
+ static QGLContext* currentCtx;
+
+private:
+ QGLContextPrivate* d_ptr;
+
+ friend class QGLPixelBuffer;
+ friend class QGLPixelBufferPrivate;
+ friend class QGLWidget;
+ friend class QGLDrawable;
+ friend class QGLWidgetPrivate;
+ friend class QGLGlyphCache;
+ friend class QOpenGLPaintEngine;
+ friend class QOpenGLPaintEnginePrivate;
+ friend class QGL2PaintEngineEx;
+ friend class QGL2PaintEngineExPrivate;
+ friend class QGLWindowSurface;
+ friend class QGLPixmapData;
+ friend class QGLPixmapFilterBase;
+ friend QGLFormat::OpenGLVersionFlags QGLFormat::openGLVersionFlags();
+#ifdef Q_WS_MAC
+public:
+ void updatePaintDevice();
+private:
+ friend class QMacGLWindowChangeEvent;
+ friend QGLContextPrivate *qt_phonon_get_dptr(const QGLContext *);
+#endif
+#ifdef Q_WS_WIN
+ friend class QGLFramebufferObject;
+ friend class QGLFramebufferObjectPrivate;
+ friend bool qt_resolve_GLSL_functions(QGLContext *ctx);
+ friend bool qt_createGLSLProgram(QGLContext *ctx, GLuint &program, const char *shader_src, GLuint &shader);
+#endif
+private:
+ Q_DISABLE_COPY(QGLContext)
+};
+
+
+class Q_OPENGL_EXPORT QGLWidget : public QWidget
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QGLWidget)
+public:
+ explicit QGLWidget(QWidget* parent=0,
+ const QGLWidget* shareWidget = 0, Qt::WindowFlags f=0);
+ explicit QGLWidget(QGLContext *context, QWidget* parent=0,
+ const QGLWidget* shareWidget = 0, Qt::WindowFlags f=0);
+ explicit QGLWidget(const QGLFormat& format, QWidget* parent=0,
+ const QGLWidget* shareWidget = 0, Qt::WindowFlags f=0);
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QGLWidget(QWidget* parent, const char* name,
+ const QGLWidget* shareWidget = 0, Qt::WindowFlags f=0);
+ QT3_SUPPORT_CONSTRUCTOR QGLWidget(QGLContext *context, QWidget* parent, const char* name,
+ const QGLWidget* shareWidget = 0, Qt::WindowFlags f=0);
+ QT3_SUPPORT_CONSTRUCTOR QGLWidget(const QGLFormat& format, QWidget* parent, const char* name,
+ const QGLWidget* shareWidget = 0, Qt::WindowFlags f=0);
+#endif
+ ~QGLWidget();
+
+ void qglColor(const QColor& c) const;
+ void qglClearColor(const QColor& c) const;
+
+ bool isValid() const;
+ bool isSharing() const;
+
+ // ### Qt 5: return bools
+ void makeCurrent();
+ void doneCurrent();
+
+ bool doubleBuffer() const;
+ void swapBuffers();
+
+ // ### Qt 5: make format() return a const ref instead
+ QGLFormat format() const;
+ void setFormat(const QGLFormat& format);
+
+ const QGLContext* context() const;
+ void setContext(QGLContext* context, const QGLContext* shareContext = 0,
+ bool deleteOldContext = true);
+
+ QPixmap renderPixmap(int w = 0, int h = 0, bool useContext = false);
+ QImage grabFrameBuffer(bool withAlpha = false);
+
+ void makeOverlayCurrent();
+ const QGLContext* overlayContext() const;
+
+ static QImage convertToGLFormat(const QImage& img);
+
+ void setMouseTracking(bool enable);
+
+ const QGLColormap & colormap() const;
+ void setColormap(const QGLColormap & map);
+
+ void renderText(int x, int y, const QString & str,
+ const QFont & fnt = QFont(), int listBase = 2000);
+ void renderText(double x, double y, double z, const QString & str,
+ const QFont & fnt = QFont(), int listBase = 2000);
+ QPaintEngine *paintEngine() const;
+
+ GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D,
+ GLint format = GL_RGBA);
+ GLuint bindTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D,
+ GLint format = GL_RGBA);
+ GLuint bindTexture(const QString &fileName);
+
+ void deleteTexture(GLuint tx_id);
+
+ void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ GLuint bindTexture(const QImage &image, QMacCompatGLenum = GL_TEXTURE_2D,
+ QMacCompatGLint format = GL_RGBA);
+ GLuint bindTexture(const QPixmap &pixmap, QMacCompatGLenum = GL_TEXTURE_2D,
+ QMacCompatGLint format = GL_RGBA);
+
+ void deleteTexture(QMacCompatGLuint tx_id);
+
+ void drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+#endif
+
+public Q_SLOTS:
+ virtual void updateGL();
+ virtual void updateOverlayGL();
+
+protected:
+ bool event(QEvent *);
+ virtual void initializeGL();
+ virtual void resizeGL(int w, int h);
+ virtual void paintGL();
+
+ virtual void initializeOverlayGL();
+ virtual void resizeOverlayGL(int w, int h);
+ virtual void paintOverlayGL();
+
+ void setAutoBufferSwap(bool on);
+ bool autoBufferSwap() const;
+
+ void paintEvent(QPaintEvent*);
+ void resizeEvent(QResizeEvent*);
+
+ virtual void glInit();
+ virtual void glDraw();
+ int fontDisplayListBase(const QFont & fnt, int listBase = 2000); // ### Qt 5: remove
+
+private:
+ Q_DISABLE_COPY(QGLWidget)
+
+#ifdef Q_WS_MAC
+ friend class QMacGLWindowChangeEvent;
+#endif
+ friend class QGLDrawable;
+ friend class QGLPixelBuffer;
+ friend class QGLPixelBufferPrivate;
+ friend class QGLContext;
+ friend class QGLOverlayWidget;
+ friend class QOpenGLPaintEngine;
+};
+
+
+//
+// QGLFormat inline functions
+//
+
+inline bool QGLFormat::doubleBuffer() const
+{
+ return testOption(QGL::DoubleBuffer);
+}
+
+inline bool QGLFormat::depth() const
+{
+ return testOption(QGL::DepthBuffer);
+}
+
+inline bool QGLFormat::rgba() const
+{
+ return testOption(QGL::Rgba);
+}
+
+inline bool QGLFormat::alpha() const
+{
+ return testOption(QGL::AlphaChannel);
+}
+
+inline bool QGLFormat::accum() const
+{
+ return testOption(QGL::AccumBuffer);
+}
+
+inline bool QGLFormat::stencil() const
+{
+ return testOption(QGL::StencilBuffer);
+}
+
+inline bool QGLFormat::stereo() const
+{
+ return testOption(QGL::StereoBuffers);
+}
+
+inline bool QGLFormat::directRendering() const
+{
+ return testOption(QGL::DirectRendering);
+}
+
+inline bool QGLFormat::hasOverlay() const
+{
+ return testOption(QGL::HasOverlay);
+}
+
+inline bool QGLFormat::sampleBuffers() const
+{
+ return testOption(QGL::SampleBuffers);
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGL_H
diff --git a/src/opengl/qgl_cl_p.h b/src/opengl/qgl_cl_p.h
new file mode 100644
index 0000000000..e514ff524e
--- /dev/null
+++ b/src/opengl/qgl_cl_p.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+
+#ifdef QT_OPENGL_ES_1_CL
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QGLWidget class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+inline void glTexParameterf (GLenum target, GLenum pname, GLfloat param)
+{
+ glTexParameterx(target, pname, param);
+}
+inline void glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
+{
+ glClearColorx(FLOAT2X(red) ,FLOAT2X(green), FLOAT2X(blue), FLOAT2X(alpha));
+}
+inline void glColor4f (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+{
+ glColor4x(FLOAT2X(red) ,FLOAT2X(green), FLOAT2X(blue), FLOAT2X(alpha));
+}
+
+inline void glOrthof (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar)
+{
+ glOrthox(FLOAT2X(left), FLOAT2X(right), FLOAT2X(bottom), FLOAT2X(top), FLOAT2X(zNear), FLOAT2X(zFar));
+}
+
+inline void glPointSize (GLfloat size)
+{
+ glPointSizex(FLOAT2X(size));
+}
+
+inline void glPolygonOffset (GLfloat factor, GLfloat units)
+{
+ glPolygonOffsetx (FLOAT2X(factor), FLOAT2X(units));
+}
+
+inline void glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
+{
+ glRotatex(FLOAT2X(angle), FLOAT2X(x), FLOAT2X(y), FLOAT2X(z));
+}
+
+inline void glTranslatef (GLfloat x, GLfloat y, GLfloat z)
+{
+ glTranslatex(FLOAT2X(x) ,FLOAT2X(y) ,FLOAT2X(z));
+}
+
+inline void glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz)
+{
+ glNormal3x(FLOAT2X(nx), FLOAT2X(ny), FLOAT2X(nz));
+}
+
+inline void glScalef(GLfloat x, GLfloat y, GLfloat z)
+{
+ glScalex(FLOAT2X(x), FLOAT2X(y), FLOAT2X(z));
+}
+
+inline void glClearDepthf (GLclampf depth)
+{
+ glClearDepthx(FLOAT2X(depth));
+}
+
+inline void glAlphaFunc (GLenum func, GLclampf ref)
+{
+ glAlphaFuncx(func, FLOAT2X(ref));
+}
+
+inline void glLoadMatrixf (const GLfloat *_m)
+{
+ GLfixed m[16];
+ for (int i =0; i < 16; i++)
+ m[i] = FLOAT2X(_m[i]);
+ glLoadMatrixx(m);
+}
+
+inline void glMultMatrixf (const GLfloat *_m)
+{
+ GLfixed m[16];
+ for (int i =0; i < 16; i++)
+ m[i] = FLOAT2X(_m[i]);
+ glMultMatrixx (m);
+}
+
+
+inline void glLineWidth (GLfloat width)
+{
+ glLineWidthx(FLOAT2X(width));
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_OPENGL_ES_1_CL
+
diff --git a/src/opengl/qgl_egl.cpp b/src/opengl/qgl_egl.cpp
new file mode 100644
index 0000000000..98c5710420
--- /dev/null
+++ b/src/opengl/qgl_egl.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** 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 <QtOpenGL/qgl.h>
+#include "qgl_egl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// Set device configuration attributes from a QGLFormat instance.
+void qt_egl_set_format(QEglProperties& props, int deviceType, const QGLFormat& f)
+{
+ if (deviceType == QInternal::Pixmap || deviceType == QInternal::Image)
+ props.setValue(EGL_SURFACE_TYPE, EGL_PIXMAP_BIT);
+ else if (deviceType == QInternal::Pbuffer)
+ props.setValue(EGL_SURFACE_TYPE, EGL_PBUFFER_BIT);
+ else
+ props.setValue(EGL_SURFACE_TYPE, EGL_WINDOW_BIT);
+
+ // Set the pixel format to that contained in the QGLFormat
+ // if the system hasn't already chosen a fixed format to
+ // match the pixmap, widget, etc.
+ if (props.value(EGL_RED_SIZE) == EGL_DONT_CARE || f.redBufferSize() != -1)
+ props.setValue(EGL_RED_SIZE, f.redBufferSize() == -1 ? 1 : f.redBufferSize());
+ if (props.value(EGL_GREEN_SIZE) == EGL_DONT_CARE || f.greenBufferSize() != -1)
+ props.setValue(EGL_GREEN_SIZE, f.greenBufferSize() == -1 ? 1 : f.greenBufferSize());
+ if (props.value(EGL_BLUE_SIZE) == EGL_DONT_CARE || f.blueBufferSize() != -1)
+ props.setValue(EGL_BLUE_SIZE, f.blueBufferSize() == -1 ? 1 : f.blueBufferSize());
+ if (f.alpha()) {
+ if (props.value(EGL_ALPHA_SIZE) == EGL_DONT_CARE || f.alphaBufferSize() != -1)
+ props.setValue(EGL_ALPHA_SIZE, f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize());
+ }
+
+ if (f.depth())
+ props.setValue(EGL_DEPTH_SIZE, f.depthBufferSize() == -1 ? 1 : f.depthBufferSize());
+ if (f.stencil())
+ props.setValue(EGL_STENCIL_SIZE, f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize());
+ if (f.sampleBuffers()) {
+ props.setValue(EGL_SAMPLE_BUFFERS, 1);
+ props.setValue(EGL_SAMPLES, f.samples());
+ } else {
+ props.setValue(EGL_SAMPLE_BUFFERS, 0);
+ }
+ if (deviceType == QInternal::Widget)
+ props.setValue(EGL_LEVEL, f.plane());
+}
+
+// Updates "format" with the parameters of the selected configuration.
+void qt_egl_update_format(const QEglContext& context, QGLFormat& format)
+{
+ EGLint value = 0;
+
+ if (context.configAttrib(EGL_RED_SIZE, &value))
+ format.setRedBufferSize(value);
+ if (context.configAttrib(EGL_GREEN_SIZE, &value))
+ format.setGreenBufferSize(value);
+ if (context.configAttrib(EGL_BLUE_SIZE, &value))
+ format.setBlueBufferSize(value);
+ if (context.configAttrib(EGL_ALPHA_SIZE, &value)) {
+ format.setAlpha(value != 0);
+ if (format.alpha())
+ format.setAlphaBufferSize(value);
+ }
+
+ if (context.configAttrib(EGL_DEPTH_SIZE, &value)) {
+ format.setDepth(value != 0);
+ if (format.depth())
+ format.setDepthBufferSize(value);
+ }
+
+ if (context.configAttrib(EGL_LEVEL, &value))
+ format.setPlane(value);
+
+ if (context.configAttrib(EGL_SAMPLE_BUFFERS, &value)) {
+ format.setSampleBuffers(value != 0);
+ if (format.sampleBuffers()) {
+ context.configAttrib(EGL_SAMPLES, &value);
+ format.setSamples(value);
+ }
+ }
+
+ if (context.configAttrib(EGL_STENCIL_SIZE, &value)) {
+ format.setStencil(value != 0);
+ if (format.stencil())
+ format.setStencilBufferSize(value);
+ }
+
+ // Clear the EGL error state because some of the above may
+ // have errored out because the attribute is not applicable
+ // to the surface type. Such errors don't matter.
+ context.clearError();
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_egl_p.h b/src/opengl/qgl_egl_p.h
new file mode 100644
index 0000000000..39f25e2b19
--- /dev/null
+++ b/src/opengl/qgl_egl_p.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** 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 QGL_EGL_P_H
+#define QGL_EGL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QGLWidget class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qegl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QGLFormat;
+
+void qt_egl_set_format(QEglProperties& props, int deviceType, const QGLFormat& f);
+void qt_egl_update_format(const QEglContext& context, QGLFormat& format);
+void qt_egl_add_platform_config(QEglProperties& props, QPaintDevice *device);
+
+QT_END_NAMESPACE
+
+#endif // QGL_EGL_P_H
diff --git a/src/opengl/qgl_mac.mm b/src/opengl/qgl_mac.mm
new file mode 100644
index 0000000000..314c6595e3
--- /dev/null
+++ b/src/opengl/qgl_mac.mm
@@ -0,0 +1,982 @@
+/****************************************************************************
+**
+** 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 "qgl.h"
+
+// There are functions that are deprecated in 10.5, but really there's no way around them
+// for Carbon, so just undefine them.
+#undef DEPRECATED_ATTRIBUTE
+#define DEPRECATED_ATTRIBUTE
+#if defined(Q_WS_MAC)
+#ifndef QT_MAC_USE_COCOA
+#ifdef qDebug
+# undef qDebug
+# include <AGL/agl.h>
+# include <AGL/aglRenderers.h>
+# include <OpenGL/gl.h>
+# ifdef QT_NO_DEBUG
+# define qDebug qt_noop(),1?(void)0:qDebug
+# endif
+#else
+# include <AGL/agl.h>
+# include <AGL/aglRenderers.h>
+# include <OpenGL/gl.h>
+#endif
+#else
+#include <private/qcocoaview_mac_p.h>
+#endif
+
+
+#include <OpenGL/gl.h>
+#include <CoreServices/CoreServices.h>
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qgl_p.h>
+#include <private/qpaintengine_opengl_p.h>
+#include <private/qt_mac_p.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+#include <qapplication.h>
+#include <qstack.h>
+#include <qdesktopwidget.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+#ifdef QT_MAC_USE_COCOA
+QT_END_NAMESPACE
+
+QT_FORWARD_DECLARE_CLASS(QWidget)
+QT_FORWARD_DECLARE_CLASS(QWidgetPrivate)
+QT_FORWARD_DECLARE_CLASS(QGLWidgetPrivate)
+
+@interface QT_MANGLE_NAMESPACE(QCocoaOpenGLView) : QT_MANGLE_NAMESPACE(QCocoaView)
+{
+}
+- (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate;
+@end
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaOpenGLView)
+- (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate
+{
+ self = [super initWithQWidget:widget widgetPrivate:widgetprivate];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(_surfaceNeedsUpdate:)
+ name:NSViewGlobalFrameDidChangeNotification
+ object:self];
+ return self;
+}
+
+- (void) _surfaceNeedsUpdate:(NSNotification*)notification
+{
+ Q_UNUSED(notification);
+ static_cast<QGLWidgetPrivate *>(qwidgetprivate)->glcx->updatePaintDevice();
+}
+@end
+QT_BEGIN_NAMESPACE
+
+void *qt_current_nsopengl_context()
+{
+ return [NSOpenGLContext currentContext];
+}
+
+static GLint attribValue(NSOpenGLPixelFormat *fmt, NSOpenGLPixelFormatAttribute attrib)
+{
+ GLint res;
+ [fmt getValues:&res forAttribute:attrib forVirtualScreen:0];
+ return res;
+}
+
+static int def(int val, int defVal)
+{
+ return val != -1 ? val : defVal;
+}
+#else
+QRegion qt_mac_get_widget_rgn(const QWidget *widget);
+#endif
+
+extern quint32 *qt_mac_pixmap_get_base(const QPixmap *);
+extern int qt_mac_pixmap_get_bytes_per_line(const QPixmap *);
+extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
+extern void qt_mac_dispose_rgn(RgnHandle); //qregion_mac.cpp
+extern QRegion qt_mac_convert_mac_region(RgnHandle); //qregion_mac.cpp
+extern void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding encoding=0, int len=-1); //qglobal.cpp
+
+bool QGLFormat::hasOpenGL()
+{
+ return true;
+}
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ return false;
+}
+
+bool QGLContext::chooseContext(const QGLContext *shareContext)
+{
+ QMacCocoaAutoReleasePool pool;
+ Q_D(QGLContext);
+ d->cx = 0;
+ d->vi = chooseMacVisual(0);
+ if (!d->vi)
+ return false;
+
+#ifndef QT_MAC_USE_COCOA
+ AGLPixelFormat fmt = (AGLPixelFormat)d->vi;
+ GLint res;
+ aglDescribePixelFormat(fmt, AGL_LEVEL, &res);
+ d->glFormat.setPlane(res);
+ if (deviceIsPixmap())
+ res = 0;
+ else
+ aglDescribePixelFormat(fmt, AGL_DOUBLEBUFFER, &res);
+ d->glFormat.setDoubleBuffer(res);
+ aglDescribePixelFormat(fmt, AGL_DEPTH_SIZE, &res);
+ d->glFormat.setDepth(res);
+ if (d->glFormat.depth())
+ d->glFormat.setDepthBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_RGBA, &res);
+ d->glFormat.setRgba(res);
+ aglDescribePixelFormat(fmt, AGL_RED_SIZE, &res);
+ d->glFormat.setRedBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_GREEN_SIZE, &res);
+ d->glFormat.setGreenBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_BLUE_SIZE, &res);
+ d->glFormat.setBlueBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_ALPHA_SIZE, &res);
+ d->glFormat.setAlpha(res);
+ if (d->glFormat.alpha())
+ d->glFormat.setAlphaBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_ACCUM_RED_SIZE, &res);
+ // Bug in Apple OpenGL (rdr://5015603), when we don't have an accumulation
+ // buffer, it still claims that we have a 16-bit one (which is pretty rare).
+ // So, we just assume we can never have a buffer that small.
+ d->glFormat.setAccum(res > 5);
+ if (d->glFormat.accum())
+ d->glFormat.setAccumBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_STENCIL_SIZE, &res);
+ d->glFormat.setStencil(res);
+ if (d->glFormat.stencil())
+ d->glFormat.setStencilBufferSize(res);
+ aglDescribePixelFormat(fmt, AGL_STEREO, &res);
+ d->glFormat.setStereo(res);
+ aglDescribePixelFormat(fmt, AGL_SAMPLE_BUFFERS_ARB, &res);
+ d->glFormat.setSampleBuffers(res);
+ if (d->glFormat.sampleBuffers()) {
+ aglDescribePixelFormat(fmt, AGL_SAMPLES_ARB, &res);
+ d->glFormat.setSamples(res);
+ }
+#else
+ NSOpenGLPixelFormat *fmt = static_cast<NSOpenGLPixelFormat *>(d->vi);
+
+ d->glFormat = QGLFormat();
+
+ // ### make sure to reset other options
+ d->glFormat.setDoubleBuffer(attribValue(fmt, NSOpenGLPFADoubleBuffer));
+
+ int depthSize = attribValue(fmt, NSOpenGLPFADepthSize);
+ d->glFormat.setDepth(depthSize > 0);
+ if (depthSize > 0)
+ d->glFormat.setDepthBufferSize(depthSize);
+
+ int alphaSize = attribValue(fmt, NSOpenGLPFAAlphaSize);
+ d->glFormat.setAlpha(alphaSize > 0);
+ if (alphaSize > 0)
+ d->glFormat.setAlphaBufferSize(alphaSize);
+
+ int accumSize = attribValue(fmt, NSOpenGLPFAAccumSize);
+ d->glFormat.setAccum(accumSize > 0);
+ if (accumSize > 0)
+ d->glFormat.setAccumBufferSize(accumSize);
+
+ int stencilSize = attribValue(fmt, NSOpenGLPFAStencilSize);
+ d->glFormat.setStencil(stencilSize > 0);
+ if (stencilSize > 0)
+ d->glFormat.setStencilBufferSize(stencilSize);
+
+ d->glFormat.setStereo(attribValue(fmt, NSOpenGLPFAStereo));
+
+ int sampleBuffers = attribValue(fmt, NSOpenGLPFASampleBuffers);
+ d->glFormat.setSampleBuffers(sampleBuffers);
+ if (sampleBuffers > 0)
+ d->glFormat.setSamples(attribValue(fmt, NSOpenGLPFASamples));
+#endif
+ if (shareContext && (!shareContext->isValid() || !shareContext->d_func()->cx)) {
+ qWarning("QGLContext::chooseContext: Cannot share with invalid context");
+ shareContext = 0;
+ }
+
+ // sharing between rgba and color-index will give wrong colors
+ if (shareContext && (format().rgba() != shareContext->format().rgba()))
+ shareContext = 0;
+
+#ifndef QT_MAC_USE_COCOA
+ AGLContext ctx = aglCreateContext(fmt, (AGLContext) (shareContext ? shareContext->d_func()->cx : 0));
+#else
+ NSOpenGLContext *ctx = [[NSOpenGLContext alloc] initWithFormat:fmt
+ shareContext:(shareContext ? static_cast<NSOpenGLContext *>(shareContext->d_func()->cx)
+ : 0)];
+#endif
+ if (!ctx) {
+#ifndef QT_MAC_USE_COCOA
+ GLenum err = aglGetError();
+ if (err == AGL_BAD_MATCH || err == AGL_BAD_CONTEXT) {
+ if (shareContext && shareContext->d_func()->cx) {
+ qWarning("QGLContext::chooseContext(): Context sharing mismatch!");
+ if (!(ctx = aglCreateContext(fmt, 0)))
+ return false;
+ shareContext = 0;
+ }
+ }
+#else
+ if (shareContext) {
+ ctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:0];
+ if (ctx) {
+ qWarning("QGLContext::chooseContext: Context sharing mismatch");
+ shareContext = 0;
+ }
+ }
+#endif
+ if (!ctx) {
+ qWarning("QGLContext::chooseContext: Unable to create QGLContext");
+ return false;
+ }
+ }
+ d->cx = ctx;
+ if (shareContext && shareContext->d_func()->cx) {
+ QGLContext *share = const_cast<QGLContext *>(shareContext);
+ d->sharing = true;
+ share->d_func()->sharing = true;
+ }
+ if (deviceIsPixmap())
+ updatePaintDevice();
+
+ // vblank syncing
+ GLint interval = d->reqFormat.swapInterval();
+ if (interval != -1) {
+#ifndef QT_MAC_USE_COCOA
+ aglSetInteger((AGLContext)d->cx, AGL_SWAP_INTERVAL, &interval);
+ if (interval != 0)
+ aglEnable((AGLContext)d->cx, AGL_SWAP_INTERVAL);
+ else
+ aglDisable((AGLContext)d->cx, AGL_SWAP_INTERVAL);
+#else
+ [ctx setValues:&interval forParameter:NSOpenGLCPSwapInterval];
+#endif
+ }
+#ifndef QT_MAC_USE_COCOA
+ aglGetInteger((AGLContext)d->cx, AGL_SWAP_INTERVAL, &interval);
+#else
+ [ctx getValues:&interval forParameter:NSOpenGLCPSwapInterval];
+#endif
+ d->glFormat.setSwapInterval(interval);
+ return true;
+}
+
+void *QGLContextPrivate::tryFormat(const QGLFormat &format)
+{
+ static const int Max = 40;
+#ifndef QT_MAC_USE_COCOA
+ GLint attribs[Max], cnt = 0;
+ bool device_is_pixmap = (paintDevice->devType() == QInternal::Pixmap);
+
+ attribs[cnt++] = AGL_RGBA;
+ attribs[cnt++] = AGL_BUFFER_SIZE;
+ attribs[cnt++] = device_is_pixmap ? static_cast<QPixmap *>(paintDevice)->depth() : 32;
+ attribs[cnt++] = AGL_LEVEL;
+ attribs[cnt++] = format.plane();
+
+ if (format.redBufferSize() != -1) {
+ attribs[cnt++] = AGL_RED_SIZE;
+ attribs[cnt++] = format.redBufferSize();
+ }
+ if (format.greenBufferSize() != -1) {
+ attribs[cnt++] = AGL_GREEN_SIZE;
+ attribs[cnt++] = format.greenBufferSize();
+ }
+ if (format.blueBufferSize() != -1) {
+ attribs[cnt++] = AGL_BLUE_SIZE;
+ attribs[cnt++] = format.blueBufferSize();
+ }
+ if (device_is_pixmap) {
+ attribs[cnt++] = AGL_PIXEL_SIZE;
+ attribs[cnt++] = static_cast<QPixmap *>(paintDevice)->depth();
+ attribs[cnt++] = AGL_OFFSCREEN;
+ if (!format.alpha()) {
+ attribs[cnt++] = AGL_ALPHA_SIZE;
+ attribs[cnt++] = 8;
+ }
+ } else {
+ if (format.doubleBuffer())
+ attribs[cnt++] = AGL_DOUBLEBUFFER;
+ }
+
+ if (format.stereo())
+ attribs[cnt++] = AGL_STEREO;
+ if (format.alpha()) {
+ attribs[cnt++] = AGL_ALPHA_SIZE;
+ attribs[cnt++] = format.alphaBufferSize() == -1 ? 8 : format.alphaBufferSize();
+ }
+ if (format.stencil()) {
+ attribs[cnt++] = AGL_STENCIL_SIZE;
+ attribs[cnt++] = format.stencilBufferSize() == -1 ? 8 : format.stencilBufferSize();
+ }
+ if (format.depth()) {
+ attribs[cnt++] = AGL_DEPTH_SIZE;
+ attribs[cnt++] = format.depthBufferSize() == -1 ? 32 : format.depthBufferSize();
+ }
+ if (format.accum()) {
+ attribs[cnt++] = AGL_ACCUM_RED_SIZE;
+ attribs[cnt++] = format.accumBufferSize() == -1 ? 1 : format.accumBufferSize();
+ attribs[cnt++] = AGL_ACCUM_BLUE_SIZE;
+ attribs[cnt++] = format.accumBufferSize() == -1 ? 1 : format.accumBufferSize();
+ attribs[cnt++] = AGL_ACCUM_GREEN_SIZE;
+ attribs[cnt++] = format.accumBufferSize() == -1 ? 1 : format.accumBufferSize();
+ attribs[cnt++] = AGL_ACCUM_ALPHA_SIZE;
+ attribs[cnt++] = format.accumBufferSize() == -1 ? 1 : format.accumBufferSize();
+ }
+ if (format.sampleBuffers()) {
+ attribs[cnt++] = AGL_SAMPLE_BUFFERS_ARB;
+ attribs[cnt++] = 1;
+ attribs[cnt++] = AGL_SAMPLES_ARB;
+ attribs[cnt++] = format.samples() == -1 ? 4 : format.samples();
+ }
+
+ attribs[cnt] = AGL_NONE;
+ Q_ASSERT(cnt < Max);
+ return aglChoosePixelFormat(0, 0, attribs);
+#else
+ NSOpenGLPixelFormatAttribute attribs[Max];
+ int cnt = 0;
+ int devType = paintDevice->devType();
+ bool device_is_pixmap = (devType == QInternal::Pixmap);
+ int depth = device_is_pixmap ? static_cast<QPixmap *>(paintDevice)->depth() : 32;
+
+ attribs[cnt++] = NSOpenGLPFAColorSize;
+ attribs[cnt++] = depth;
+
+ if (device_is_pixmap) {
+ attribs[cnt++] = NSOpenGLPFAOffScreen;
+ } else {
+ if (format.doubleBuffer())
+ attribs[cnt++] = NSOpenGLPFADoubleBuffer;
+ }
+ if (glFormat.stereo())
+ attribs[cnt++] = NSOpenGLPFAStereo;
+ if (device_is_pixmap || format.alpha()) {
+ attribs[cnt++] = NSOpenGLPFAAlphaSize;
+ attribs[cnt++] = def(format.alphaBufferSize(), 8);
+ }
+ if (format.stencil()) {
+ attribs[cnt++] = NSOpenGLPFAStencilSize;
+ attribs[cnt++] = def(format.stencilBufferSize(), 8);
+ }
+ if (format.depth()) {
+ attribs[cnt++] = NSOpenGLPFADepthSize;
+ attribs[cnt++] = def(format.depthBufferSize(), 32);
+ }
+ if (format.accum()) {
+ attribs[cnt++] = NSOpenGLPFAAccumSize;
+ attribs[cnt++] = def(format.accumBufferSize(), 1);
+ }
+ if (format.sampleBuffers()) {
+ attribs[cnt++] = NSOpenGLPFASampleBuffers;
+ attribs[cnt++] = 1;
+ attribs[cnt++] = NSOpenGLPFASamples;
+ attribs[cnt++] = def(format.samples(), 4);
+ }
+
+ if (format.directRendering())
+ attribs[cnt++] = NSOpenGLPFAAccelerated;
+
+ if (devType == QInternal::Pbuffer)
+ attribs[cnt++] = NSOpenGLPFAPixelBuffer;
+
+ attribs[cnt] = 0;
+ Q_ASSERT(cnt < Max);
+ return [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
+#endif
+}
+
+/*!
+ \bold{Mac OS X only:} This virtual function tries to find a visual that
+ matches the format, reducing the demands if the original request
+ cannot be met.
+
+ The algorithm for reducing the demands of the format is quite
+ simple-minded, so override this method in your subclass if your
+ application has spcific requirements on visual selection.
+
+ The \a handle argument is always zero and is not used
+
+ \sa chooseContext()
+*/
+
+void *QGLContext::chooseMacVisual(GDHandle /* handle */)
+{
+ Q_D(QGLContext);
+
+ void *fmt = d->tryFormat(d->glFormat);
+ if (!fmt && d->glFormat.stereo()) {
+ d->glFormat.setStereo(false);
+ fmt = d->tryFormat(d->glFormat);
+ }
+ if (!fmt && d->glFormat.sampleBuffers()) {
+ d->glFormat.setSampleBuffers(false);
+ fmt = d->tryFormat(d->glFormat);
+ }
+ if (!fmt)
+ qWarning("QGLContext::chooseMacVisual: Unable to choose a pixel format");
+ return fmt;
+}
+
+void QGLContext::reset()
+{
+ Q_D(QGLContext);
+ if (!d->valid)
+ return;
+ d->cleanup();
+ doneCurrent();
+#ifndef QT_MAC_USE_COCOA
+ if (d->cx)
+ aglDestroyContext((AGLContext)d->cx);
+#else
+ [static_cast<NSOpenGLContext *>(d->cx) release];
+#endif
+ d->cx = 0;
+#ifndef QT_MAC_USE_COCOA
+ if (d->vi)
+ aglDestroyPixelFormat((AGLPixelFormat)d->vi);
+#else
+ [static_cast<NSOpenGLPixelFormat *>(d->vi) release];
+#endif
+ d->vi = 0;
+ d->crWin = false;
+ d->sharing = false;
+ d->valid = false;
+ d->transpColor = QColor();
+ d->initDone = false;
+ qgl_share_reg()->removeShare(this);
+}
+
+void QGLContext::makeCurrent()
+{
+ Q_D(QGLContext);
+
+ if (!d->valid) {
+ qWarning("QGLContext::makeCurrent: Cannot make invalid context current");
+ return;
+ }
+#ifndef QT_MAC_USE_COCOA
+ aglSetCurrentContext((AGLContext)d->cx);
+ if (d->update)
+ updatePaintDevice();
+#else
+ [static_cast<NSOpenGLContext *>(d->cx) makeCurrentContext];
+#endif
+ currentCtx = this;
+ if (!qgl_context_storage.hasLocalData() && QThread::currentThread())
+ qgl_context_storage.setLocalData(new QGLThreadContext);
+ if (qgl_context_storage.hasLocalData())
+ qgl_context_storage.localData()->context = this;
+}
+
+#ifndef QT_MAC_USE_COCOA
+/*
+ Returns the effective scale factor for a widget. For this value to be
+ different than 1, the following must be true:
+ - The system scale factor must be greater than 1.
+ - The widget window must have WA_MacFrameworkScaled set.
+*/
+float qt_mac_get_scale_factor(QWidget *widget)
+{
+ if (!widget | !widget->window())
+ return 1;
+
+ if (widget->window()->testAttribute(Qt::WA_MacFrameworkScaled) == false)
+ return 1;
+
+ float systemScale = QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4 ? HIGetScaleFactor() : 1;
+ if (systemScale == float(1))
+ return 1;
+
+ return systemScale;
+}
+#endif
+
+/*! \internal
+*/
+void QGLContext::updatePaintDevice()
+{
+ Q_D(QGLContext);
+#ifndef QT_MAC_USE_COCOA
+ d->update = false;
+ if (d->paintDevice->devType() == QInternal::Widget) {
+ //get control information
+ QWidget *w = (QWidget *)d->paintDevice;
+ HIViewRef hiview = (HIViewRef)w->winId();
+ WindowRef window = HIViewGetWindow(hiview);
+#ifdef DEBUG_OPENGL_REGION_UPDATE
+ static int serial_no_gl = 0;
+ qDebug("[%d] %p setting on %s::%s %p/%p [%s]", ++serial_no_gl, w,
+ w->metaObject()->className(), w->objectName().toLatin1().constData(),
+ hiview, window, w->handle() ? "Inside" : "Outside");
+#endif
+
+ //update drawable
+ if (0 && w->isWindow() && w->isFullScreen()) {
+ aglSetDrawable((AGLContext)d->cx, 0);
+ aglSetFullScreen((AGLContext)d->cx, w->width(), w->height(), 0, QApplication::desktop()->screenNumber(w));
+ w->hide();
+ } else {
+ AGLDrawable old_draw = aglGetDrawable((AGLContext)d->cx), new_draw = GetWindowPort(window);
+ if (old_draw != new_draw)
+ aglSetDrawable((AGLContext)d->cx, new_draw);
+ }
+
+ float scale = qt_mac_get_scale_factor(w);
+
+ if (!w->isWindow()) {
+ QRegion clp = qt_mac_get_widget_rgn(w); //get drawable area
+
+#ifdef DEBUG_OPENGL_REGION_UPDATE
+ if (clp.isEmpty()) {
+ qDebug(" Empty area!");
+ } else {
+ QVector<QRect> rs = clp.rects();
+ for(int i = 0; i < rs.count(); i++)
+ qDebug(" %d %d %d %d", rs[i].x(), rs[i].y(), rs[i].width(), rs[i].height());
+ }
+#endif
+ //update the clip
+ if (!aglIsEnabled((AGLContext)d->cx, AGL_BUFFER_RECT))
+ aglEnable((AGLContext)d->cx, AGL_BUFFER_RECT);
+ if (clp.isEmpty()) {
+ GLint offs[4] = { 0, 0, 0, 0 };
+ aglSetInteger((AGLContext)d->cx, AGL_BUFFER_RECT, offs);
+ if (aglIsEnabled((AGLContext)d->cx, AGL_CLIP_REGION))
+ aglDisable((AGLContext)d->cx, AGL_CLIP_REGION);
+ } else {
+ HIPoint origin = { 0., 0. };
+ HIViewConvertPoint(&origin, HIViewRef(w->winId()), 0);
+ const GLint offs[4] = { qRound(origin.x),
+ w->window()->frameGeometry().height() * scale
+ - (qRound(origin.y) + w->height() * scale),
+ w->width() * scale, w->height() * scale};
+
+ RgnHandle region = clp.handle(true);
+
+ if (scale != float(1)) {
+ // Sacle the clip region by the scale factor
+ Rect regionBounds;
+ GetRegionBounds(region, &regionBounds);
+ Rect regionBoundsDest = regionBounds;
+ regionBoundsDest.bottom *= scale;
+ regionBoundsDest.right *= scale;
+ MapRgn(region, &regionBounds, &regionBoundsDest);
+ }
+
+ aglSetInteger((AGLContext)d->cx, AGL_BUFFER_RECT, offs);
+ aglSetInteger((AGLContext)d->cx, AGL_CLIP_REGION, (const GLint *)region);
+ if (!aglIsEnabled((AGLContext)d->cx, AGL_CLIP_REGION))
+ aglEnable((AGLContext)d->cx, AGL_CLIP_REGION);
+ }
+ } else {
+ // Set the buffer rect for top-level gl contexts when scaled.
+ if (scale != float(1)) {
+ aglEnable((AGLContext)d->cx, AGL_BUFFER_RECT);
+ const GLint offs[4] = { 0, 0, w->width() * scale , w->height() * scale};
+ aglSetInteger((AGLContext)d->cx, AGL_BUFFER_RECT, offs);
+ }
+ }
+ } else if (d->paintDevice->devType() == QInternal::Pixmap) {
+ QPixmap *pm = (QPixmap *)d->paintDevice;
+ PixMapHandle mac_pm = GetGWorldPixMap((GWorldPtr)pm->macQDHandle());
+ aglSetOffScreen((AGLContext)d->cx, pm->width(), pm->height(),
+ GetPixRowBytes(mac_pm), GetPixBaseAddr(mac_pm));
+ } else {
+ qWarning("QGLContext::updatePaintDevice(): Not sure how to render OpenGL on this device!");
+ }
+ aglUpdateContext((AGLContext)d->cx);
+
+#else
+ QMacCocoaAutoReleasePool pool;
+
+ if (d->paintDevice->devType() == QInternal::Widget) {
+ //get control information
+ QWidget *w = (QWidget *)d->paintDevice;
+ NSView *view = qt_mac_nativeview_for(w);
+
+ // ideally we would use QWidget::isVisible(), but we get "invalid drawable" errors
+ if (![(NSWindow *)qt_mac_window_for(w) isVisible])
+ return;
+ if ([static_cast<NSOpenGLContext *>(d->cx) view] != view)
+ [static_cast<NSOpenGLContext *>(d->cx) setView:view];
+ } else if (d->paintDevice->devType() == QInternal::Pixmap) {
+ const QPixmap *pm = static_cast<const QPixmap *>(d->paintDevice);
+ [static_cast<NSOpenGLContext *>(d->cx) setOffScreen:qt_mac_pixmap_get_base(pm)
+ width:pm->width()
+ height:pm->height()
+ rowbytes:qt_mac_pixmap_get_bytes_per_line(pm)];
+ } else {
+ qWarning("QGLContext::updatePaintDevice: Not sure how to render OpenGL on this device");
+ }
+ [static_cast<NSOpenGLContext *>(d->cx) update];
+#endif
+}
+
+void QGLContext::doneCurrent()
+{
+
+ if (
+#ifndef QT_MAC_USE_COCOA
+ aglGetCurrentContext() != (AGLContext) d_func()->cx
+#else
+ [NSOpenGLContext currentContext] != d_func()->cx
+#endif
+ )
+ return;
+
+ currentCtx = 0;
+ if (qgl_context_storage.hasLocalData())
+ qgl_context_storage.localData()->context = 0;
+#ifndef QT_MAC_USE_COCOA
+ aglSetCurrentContext(0);
+#else
+ [NSOpenGLContext clearCurrentContext];
+#endif
+}
+
+void QGLContext::swapBuffers() const
+{
+ Q_D(const QGLContext);
+ if (!d->valid)
+ return;
+#ifndef QT_MAC_USE_COCOA
+ aglSwapBuffers((AGLContext)d->cx);
+#else
+ [static_cast<NSOpenGLContext *>(d->cx) flushBuffer];
+#endif
+}
+
+QColor QGLContext::overlayTransparentColor() const
+{
+ return QColor(0, 0, 0); // Invalid color
+}
+
+#ifndef QT_MAC_USE_COCOA
+static QColor cmap[256];
+static bool cmap_init = false;
+#endif
+uint QGLContext::colorIndex(const QColor &c) const
+{
+#ifndef QT_MAC_USE_COCOA
+ int ret = -1;
+ if(!cmap_init) {
+ cmap_init = true;
+ for(int i = 0; i < 256; i++)
+ cmap[i] = QColor();
+ } else {
+ for(int i = 0; i < 256; i++) {
+ if(cmap[i].isValid() && cmap[i] == c) {
+ ret = i;
+ break;
+ }
+ }
+ }
+ if(ret == -1) {
+ for(ret = 0; ret < 256; ret++)
+ if(!cmap[ret].isValid())
+ break;
+ if(ret == 256) {
+ ret = -1;
+ qWarning("QGLContext::colorIndex(): Internal error!");
+ } else {
+ cmap[ret] = c;
+
+ GLint vals[4];
+ vals[0] = ret;
+ vals[1] = c.red();
+ vals[2] = c.green();
+ vals[3] = c.blue();
+ aglSetInteger((AGLContext)d_func()->cx, AGL_COLORMAP_ENTRY, vals);
+ }
+ }
+ return (uint)(ret == -1 ? 0 : ret);
+#else
+ Q_UNUSED(c);
+ return 0;
+#endif
+}
+
+void QGLContext::generateFontDisplayLists(const QFont & /* fnt */, int /* listBase */)
+{
+}
+
+static CFBundleRef qt_getOpenGLBundle()
+{
+ CFBundleRef bundle = 0;
+ QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
+ QCFString::toCFStringRef(QLatin1String("/System/Library/Frameworks/OpenGL.framework")), kCFURLPOSIXPathStyle, false);
+ if (url)
+ bundle = CFBundleCreate(kCFAllocatorDefault, url);
+ return bundle;
+}
+
+void *QGLContext::getProcAddress(const QString &proc) const
+{
+ return CFBundleGetFunctionPointerForName(QCFType<CFBundleRef>(qt_getOpenGLBundle()),
+ QCFString(proc));
+}
+#ifndef QT_MAC_USE_COCOA
+/*****************************************************************************
+ QGLWidget AGL-specific code
+ *****************************************************************************/
+
+/****************************************************************************
+ Hacks to glue AGL to an HIView
+ ***************************************************************************/
+QRegion qt_mac_get_widget_rgn(const QWidget *widget)
+{
+ if(!widget->isVisible() || widget->isMinimized())
+ return QRegion();
+ const QRect wrect = QRect(qt_mac_posInWindow(widget), widget->size());
+ if(!wrect.isValid())
+ return QRegion();
+
+ RgnHandle macr = qt_mac_get_rgn();
+ GetControlRegion((HIViewRef)widget->winId(), kControlStructureMetaPart, macr);
+ OffsetRgn(macr, wrect.x(), wrect.y());
+ QRegion ret = qt_mac_convert_mac_region(macr);
+
+ QPoint clip_pos = wrect.topLeft();
+ for(const QWidget *last_clip = 0, *clip = widget; clip; last_clip = clip, clip = clip->parentWidget()) {
+ if(clip != widget) {
+ GetControlRegion((HIViewRef)clip->winId(), kControlStructureMetaPart, macr);
+ OffsetRgn(macr, clip_pos.x(), clip_pos.y());
+ ret &= qt_mac_convert_mac_region(macr);
+ }
+ const QObjectList &children = clip->children();
+ for(int i = children.size()-1; i >= 0; --i) {
+ if(QWidget *child = qobject_cast<QWidget*>(children.at(i))) {
+ if(child == last_clip)
+ break;
+
+ // This check may seem weird, but when we are using a unified toolbar
+ // The widget is actually being owned by that toolbar and not by Qt.
+ // This means that the geometry it reports will be wrong
+ // and will accidentally cause problems when calculating the region
+ // So, it is better to skip these widgets since they aren't the hierarchy
+ // anyway.
+ if (HIViewGetSuperview(HIViewRef(child->winId())) != HIViewRef(clip->winId()))
+ continue;
+
+ if(child->isVisible() && !child->isMinimized() && !child->isTopLevel()) {
+ const QRect childRect = QRect(clip_pos+child->pos(), child->size());
+ if(childRect.isValid() && wrect.intersects(childRect)) {
+ GetControlRegion((HIViewRef)child->winId(), kControlStructureMetaPart, macr);
+ OffsetRgn(macr, childRect.x(), childRect.y());
+ ret -= qt_mac_convert_mac_region(macr);
+ }
+ }
+ }
+ }
+ if(clip->isWindow())
+ break;
+ clip_pos -= clip->pos();
+ }
+ qt_mac_dispose_rgn(macr);
+ return ret;
+}
+
+#endif
+
+void QGLWidget::setMouseTracking(bool enable)
+{
+ QWidget::setMouseTracking(enable);
+}
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+#ifndef QT_MAC_USE_COCOA
+ if (!isWindow())
+ d->glcx->d_func()->update = true;
+#endif
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+#ifdef QT_MAC_USE_COCOA
+ d->glcx->updatePaintDevice();
+#endif
+#ifndef QT_MAC_USE_COCOA
+ float scale = qt_mac_get_scale_factor(this);
+ resizeGL(width() * scale, height() * scale);
+#else
+ resizeGL(width(), height());
+#endif
+}
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return 0;
+}
+
+void QGLWidget::makeOverlayCurrent()
+{
+}
+
+void QGLWidget::updateOverlayGL()
+{
+}
+
+void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+ if (!d->glcx->isValid())
+ d->glcx->create(shareContext ? shareContext : oldcx);
+ if (deleteOldContext && oldcx)
+ delete oldcx;
+}
+
+void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget *shareWidget)
+{
+ Q_Q(QGLWidget);
+
+ initContext(context, shareWidget);
+
+ QWidget *current = q;
+ while (current) {
+ qt_widget_private(current)->glWidgets.append(QWidgetPrivate::GlWidgetInfo(q));
+ if (current->isWindow())
+ break;
+ current = current->parentWidget();
+ }
+
+ isGLWidget = 1;
+}
+
+bool QGLWidgetPrivate::renderCxPm(QPixmap*)
+{
+ return false;
+}
+
+void QGLWidgetPrivate::cleanupColormaps()
+{
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ return d_func()->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap &)
+{
+}
+
+void QGLWidgetPrivate::updatePaintDevice()
+{
+ Q_Q(QGLWidget);
+ glcx->updatePaintDevice();
+ q->update();
+}
+
+
+void QGLExtensions::init()
+{
+ static bool init_done = false;
+
+ if (init_done)
+ return;
+ init_done = true;
+
+#ifndef QT_MAC_USE_COCOA
+ GLint attribs[] = { AGL_RGBA, AGL_NONE };
+ AGLPixelFormat fmt = aglChoosePixelFormat(0, 0, attribs);
+ if (!fmt) {
+ qDebug("QGLExtensions: Couldn't find any RGB visuals");
+ return;
+ }
+ AGLContext ctx = aglCreateContext(fmt, 0);
+ if (!ctx) {
+ qDebug("QGLExtensions: Unable to create context");
+ } else {
+ aglSetCurrentContext(ctx);
+ init_extensions();
+ aglSetCurrentContext(0);
+ aglDestroyContext(ctx);
+ }
+ aglDestroyPixelFormat(fmt);
+#else
+ QMacCocoaAutoReleasePool pool;
+ NSOpenGLPixelFormatAttribute attribs[] = { 0 };
+ NSOpenGLPixelFormat *fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
+ if (!fmt) {
+ qWarning("QGLExtensions: Cannot find any visuals");
+ return;
+ }
+
+ NSOpenGLContext *ctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:0];
+ if (!ctx) {
+ qWarning("QGLExtensions: Cannot create context");
+ } else {
+ [ctx makeCurrentContext];
+ init_extensions();
+ [NSOpenGLContext clearCurrentContext];
+ [ctx release];
+ }
+ [fmt release];
+#endif
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h
new file mode 100644
index 0000000000..b8bbeaf110
--- /dev/null
+++ b/src/opengl/qgl_p.h
@@ -0,0 +1,394 @@
+/****************************************************************************
+**
+** 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 QGL_P_H
+#define QGL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QGLWidget class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtOpenGL/qgl.h"
+#include "QtOpenGL/qglcolormap.h"
+#include "QtCore/qmap.h"
+#include "QtCore/qthread.h"
+#include "QtCore/qthreadstorage.h"
+#include "QtCore/qhash.h"
+#include "private/qwidget_p.h"
+
+#ifndef QT_OPENGL_ES_1_CL
+#define q_vertexType float
+#define q_vertexTypeEnum GL_FLOAT
+#define f2vt(f) (f)
+#define vt2f(x) (x)
+#define i2vt(i) (float(i))
+#else
+#define FLOAT2X(f) (int( (f) * (65536)))
+#define X2FLOAT(x) (float(x) / 65536.0f)
+#define f2vt(f) FLOAT2X(f)
+#define i2vt(i) ((i)*65536)
+#define vt2f(x) X2FLOAT(x)
+#define q_vertexType GLfixed
+#define q_vertexTypeEnum GL_FIXED
+#endif //QT_OPENGL_ES_1_CL
+
+#ifdef QT_OPENGL_ES
+QT_BEGIN_INCLUDE_NAMESPACE
+#if defined(QT_OPENGL_ES_2)
+#include <EGL/egl.h>
+#else
+#include <GLES/egl.h>
+#endif
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QGLContext;
+class QGLOverlayWidget;
+class QPixmap;
+class QPixmapFilter;
+#ifdef Q_WS_MAC
+# ifdef qDebug
+# define old_qDebug qDebug
+# undef qDebug
+# endif
+QT_BEGIN_INCLUDE_NAMESPACE
+#ifndef QT_MAC_USE_COCOA
+# include <AGL/agl.h>
+#endif
+QT_END_INCLUDE_NAMESPACE
+# ifdef old_qDebug
+# undef qDebug
+# define qDebug QT_QDEBUG_MACRO
+# undef old_qDebug
+# endif
+class QMacWindowChangeEvent;
+#endif
+
+#ifdef Q_WS_QWS
+class QWSGLWindowSurface;
+#endif
+
+#if defined(QT_OPENGL_ES)
+class QEglContext;
+#endif
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <QtOpenGL/private/qglextensions_p.h>
+QT_END_INCLUDE_NAMESPACE
+
+class QGLFormatPrivate
+{
+public:
+ QGLFormatPrivate() {
+ opts = QGL::DoubleBuffer | QGL::DepthBuffer | QGL::Rgba | QGL::DirectRendering | QGL::StencilBuffer;
+#if defined(QT_OPENGL_ES_2)
+ opts |= QGL::SampleBuffers;
+#endif
+ pln = 0;
+ depthSize = accumSize = stencilSize = redSize = greenSize = blueSize = alphaSize = -1;
+ numSamples = -1;
+ swapInterval = -1;
+ }
+ QGL::FormatOptions opts;
+ int pln;
+ int depthSize;
+ int accumSize;
+ int stencilSize;
+ int redSize;
+ int greenSize;
+ int blueSize;
+ int alphaSize;
+ int numSamples;
+ int swapInterval;
+};
+
+class QGLWidgetPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QGLWidget)
+public:
+ QGLWidgetPrivate() : QWidgetPrivate()
+#ifdef Q_WS_QWS
+ , wsurf(0)
+#endif
+#if defined(Q_WS_X11) && defined(QT_OPENGL_ES)
+ , eglSurfaceWindowId(0)
+#endif
+ {}
+
+ ~QGLWidgetPrivate() {}
+
+ void init(QGLContext *context, const QGLWidget* shareWidget);
+ void initContext(QGLContext *context, const QGLWidget* shareWidget);
+ bool renderCxPm(QPixmap *pixmap);
+ void cleanupColormaps();
+
+ QGLContext *glcx;
+ bool autoSwap;
+
+ QGLColormap cmap;
+ QMap<QString, int> displayListCache;
+
+#if defined(Q_WS_WIN)
+ void updateColormap();
+ QGLContext *olcx;
+#elif defined(Q_WS_X11)
+ QGLOverlayWidget *olw;
+#if defined(QT_OPENGL_ES)
+ void recreateEglSurface(bool force);
+ WId eglSurfaceWindowId;
+#endif
+#elif defined(Q_WS_MAC)
+ QGLContext *olcx;
+ void updatePaintDevice();
+#elif defined(Q_WS_QWS)
+ QWSGLWindowSurface *wsurf;
+#endif
+};
+
+class QGLContextPrivate
+{
+ Q_DECLARE_PUBLIC(QGLContext)
+public:
+ explicit QGLContextPrivate(QGLContext *context) : internal_context(false), q_ptr(context) {}
+ ~QGLContextPrivate() {}
+ GLuint bindTexture(const QImage &image, GLenum target, GLint format, const qint64 key,
+ bool clean = false);
+ GLuint bindTexture(const QPixmap &pixmap, GLenum target, GLint format, bool clean);
+ GLuint bindTexture(const QImage &image, GLenum target, GLint format, bool clean);
+ bool textureCacheLookup(const qint64 key, GLenum target, GLuint *id);
+ void init(QPaintDevice *dev, const QGLFormat &format);
+ QImage convertToGLFormat(const QImage &image, bool force_premul, GLenum texture_format);
+ int maxTextureSize();
+
+ void cleanup();
+
+#if defined(Q_WS_WIN)
+ HGLRC rc;
+ HDC dc;
+ WId win;
+ int pixelFormatId;
+ QGLCmap* cmap;
+ HBITMAP hbitmap;
+ HDC hbitmap_hdc;
+#endif
+#if defined(QT_OPENGL_ES)
+ QEglContext *eglContext;
+#elif defined(Q_WS_X11) || defined(Q_WS_MAC)
+ void* cx;
+#endif
+#if defined(Q_WS_X11) || defined(Q_WS_MAC)
+ void* vi;
+#endif
+#if defined(Q_WS_X11)
+ void* pbuf;
+ quint32 gpm;
+ int screen;
+#endif
+#if defined(Q_WS_MAC)
+ bool update;
+ void *tryFormat(const QGLFormat &format);
+#endif
+ QGLFormat glFormat;
+ QGLFormat reqFormat;
+ GLuint pbo;
+
+ uint valid : 1;
+ uint sharing : 1;
+ uint initDone : 1;
+ uint crWin : 1;
+ uint clear_on_painter_begin : 1;
+ uint internal_context : 1;
+ uint version_flags_cached : 1;
+ QPaintDevice *paintDevice;
+ QColor transpColor;
+ QGLContext *q_ptr;
+ QGLFormat::OpenGLVersionFlags version_flags;
+
+ QGLExtensionFuncs extensionFuncs;
+ GLint max_texture_size;
+
+#ifdef Q_WS_WIN
+ static inline QGLExtensionFuncs& qt_get_extension_funcs(const QGLContext *ctx) { return ctx->d_ptr->extensionFuncs; }
+#endif
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS)
+ static QGLExtensionFuncs qt_extensionFuncs;
+ static inline QGLExtensionFuncs& qt_get_extension_funcs(const QGLContext *) { return qt_extensionFuncs; }
+#endif
+
+ QPixmapFilter *createPixmapFilter(int type) const;
+};
+
+// ### make QGLContext a QObject in 5.0 and remove the proxy stuff
+class QGLSignalProxy : public QObject
+{
+ Q_OBJECT
+public:
+ QGLSignalProxy() : QObject() {}
+ void emitAboutToDestroyContext(const QGLContext *context) {
+ emit aboutToDestroyContext(context);
+ }
+ static QGLSignalProxy *instance();
+Q_SIGNALS:
+ void aboutToDestroyContext(const QGLContext *context);
+};
+
+// GL extension definitions
+class QGLExtensions {
+public:
+ enum Extension {
+ TextureRectangle = 0x00000001,
+ SampleBuffers = 0x00000002,
+ GenerateMipmap = 0x00000004,
+ TextureCompression = 0x00000008,
+ FragmentProgram = 0x00000010,
+ MirroredRepeat = 0x00000020,
+ FramebufferObject = 0x00000040,
+ StencilTwoSide = 0x00000080,
+ StencilWrap = 0x00000100,
+ PackedDepthStencil = 0x00000200,
+ NVFloatBuffer = 0x00000400,
+ PixelBufferObject = 0x00000800
+ };
+ Q_DECLARE_FLAGS(Extensions, Extension)
+
+ static Extensions glExtensions;
+ static bool nvidiaFboNeedsFinish;
+ static void init(); // sys dependent
+ static void init_extensions(); // general: called by init()
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGLExtensions::Extensions)
+
+
+struct QGLThreadContext {
+ QGLContext *context;
+};
+extern QThreadStorage<QGLThreadContext *> qgl_context_storage;
+
+typedef QMultiHash<const QGLContext *, const QGLContext *> QGLSharingHash;
+class QGLShareRegister
+{
+public:
+ QGLShareRegister() {}
+ ~QGLShareRegister() { reg.clear(); }
+
+ bool checkSharing(const QGLContext *context1, const QGLContext *context2, const QGLContext * skip=0) {
+ if (context1 == context2)
+ return true;
+ QList<const QGLContext *> shares = reg.values(context1);
+ for (int k=0; k<shares.size(); ++k) {
+ const QGLContext *ctx = shares.at(k);
+ if (ctx == skip) // avoid an indirect circular loop (infinite recursion)
+ continue;
+ if (ctx == context2)
+ return true;
+ if (checkSharing(ctx, context2, context1))
+ return true;
+ }
+ return false;
+ }
+
+ void addShare(const QGLContext *context, const QGLContext *share) {
+ reg.insert(context, share); // context sharing works both ways
+ reg.insert(share, context);
+ }
+
+ void removeShare(const QGLContext *context) {
+ QGLSharingHash::iterator it = reg.begin();
+ while (it != reg.end()) {
+ if (it.key() == context || it.value() == context)
+ it = reg.erase(it);
+ else
+ ++it;
+ }
+ }
+
+ void replaceShare(const QGLContext *oldContext, const QGLContext *newContext) {
+ QGLSharingHash::iterator it = reg.begin();
+ while (it != reg.end()) {
+ if (it.key() == oldContext)
+ reg.insert(newContext, it.value());
+ else if (it.value() == oldContext)
+ reg.insert(it.key(), newContext);
+ ++it;
+ }
+ removeShare(oldContext);
+ }
+
+private:
+ QGLSharingHash reg;
+};
+
+extern Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg();
+
+#ifdef Q_WS_QWS
+class QOpenGLPaintEngine;
+extern QOpenGLPaintEngine* qt_qgl_paint_engine();
+
+extern EGLDisplay qt_qgl_egl_display();
+#endif
+
+inline GLenum qt_gl_preferredTextureFormat()
+{
+ return QSysInfo::ByteOrder == QSysInfo::BigEndian ? GL_RGBA : GL_BGRA;
+}
+
+inline GLenum qt_gl_preferredTextureTarget()
+{
+ return (QGLExtensions::glExtensions & QGLExtensions::TextureRectangle)
+ ? GL_TEXTURE_RECTANGLE_NV
+ : GL_TEXTURE_2D;
+}
+
+
+QT_END_NAMESPACE
+
+#endif // QGL_P_H
diff --git a/src/opengl/qgl_qws.cpp b/src/opengl/qgl_qws.cpp
new file mode 100644
index 0000000000..cb9aa89720
--- /dev/null
+++ b/src/opengl/qgl_qws.cpp
@@ -0,0 +1,364 @@
+/****************************************************************************
+**
+** 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 "qgl.h"
+#include "qgl_egl_p.h"
+
+#include <qglscreen_qws.h>
+#include <qscreenproxy_qws.h>
+#include <private/qglwindowsurface_qws_p.h>
+
+#include <private/qbackingstore_p.h>
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qgl_p.h>
+#include <private/qpaintengine_opengl_p.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+#include <qapplication.h>
+#include <qstack.h>
+#include <qdesktopwidget.h>
+#include <qdebug.h>
+#include <qvarlengtharray.h>
+
+QT_BEGIN_NAMESPACE
+
+static QGLScreen *glScreenForDevice(QPaintDevice *device)
+{
+ QScreen *screen = qt_screen;
+ if (screen->classId() == QScreen::MultiClass) {
+ int screenNumber;
+ if (device && device->devType() == QInternal::Widget)
+ screenNumber = qApp->desktop()->screenNumber(static_cast<QWidget *>(device));
+ else
+ screenNumber = 0;
+ screen = screen->subScreens()[screenNumber];
+ }
+ while (screen->classId() == QScreen::ProxyClass) {
+ screen = static_cast<QProxyScreen *>(screen)->screen();
+ }
+ if (screen->classId() == QScreen::GLClass)
+ return static_cast<QGLScreen *>(screen);
+ else
+ return 0;
+}
+
+/*****************************************************************************
+ QOpenGL debug facilities
+ *****************************************************************************/
+//#define DEBUG_OPENGL_REGION_UPDATE
+
+bool QGLFormat::hasOpenGL()
+{
+ return true;
+}
+
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ QGLScreen *glScreen = glScreenForDevice(0);
+ if (glScreen)
+ return (glScreen->options() & QGLScreen::Overlays);
+ else
+ return false;
+}
+
+void qt_egl_add_platform_config(QEglProperties& props, QPaintDevice *device)
+{
+ // Find the QGLScreen for this paint device.
+ QGLScreen *glScreen = glScreenForDevice(device);
+ if (!glScreen) {
+ qWarning("QGLContext::chooseContext(): The screen is not a QGLScreen");
+ return;
+ }
+ int devType = device->devType();
+ if (devType == QInternal::Image)
+ props.setPixelFormat(static_cast<QImage *>(device)->format());
+ else
+ props.setPixelFormat(glScreen->pixelFormat());
+}
+
+bool QGLContext::chooseContext(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+
+ // Validate the device.
+ if (!device())
+ return false;
+ int devType = device()->devType();
+ if (devType != QInternal::Pixmap && devType != QInternal::Image && devType != QInternal::Widget) {
+ qWarning("QGLContext::chooseContext(): Cannot create QGLContext's for paint device type %d", devType);
+ return false;
+ }
+
+ // Get the display and initialize it.
+ d->eglContext = new QEglContext();
+ d->eglContext->setApi(QEglContext::OpenGL);
+ if (!d->eglContext->openDisplay(device())) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ // Construct the configuration we need for this surface.
+ QEglProperties configProps;
+ qt_egl_add_platform_config(configProps, device());
+ qt_egl_set_format(configProps, devType, d->glFormat);
+ configProps.setRenderableType(QEglContext::OpenGL);
+
+ // Search for a matching configuration, reducing the complexity
+ // each time until we get something that matches.
+ if (!d->eglContext->chooseConfig(configProps)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ // Inform the higher layers about the actual format properties.
+ qt_egl_update_format(*(d->eglContext), d->glFormat);
+
+ // Create a new context for the configuration.
+ if (!d->eglContext->createContext
+ (shareContext ? shareContext->d_func()->eglContext : 0)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+#if defined(EGL_VERSION_1_1)
+ if (d->glFormat.swapInterval() != -1 && devType == QInternal::Widget)
+ eglSwapInterval(d->eglContext->display(), d->glFormat.swapInterval());
+#endif
+
+ // Create the EGL surface to draw into.
+ if (!d->eglContext->createSurface(device())) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ return true;
+}
+
+
+void QGLContext::reset()
+{
+ Q_D(QGLContext);
+ if (!d->valid)
+ return;
+ d->cleanup();
+ doneCurrent();
+ if (d->eglContext) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ }
+ d->crWin = false;
+ d->sharing = false;
+ d->valid = false;
+ d->transpColor = QColor();
+ d->initDone = false;
+ qgl_share_reg()->removeShare(this);
+}
+
+void QGLContext::makeCurrent()
+{
+ Q_D(QGLContext);
+ if(!d->valid || !d->eglContext) {
+ qWarning("QGLContext::makeCurrent(): Cannot make invalid context current");
+ return;
+ }
+
+ if (d->eglContext->makeCurrent()) {
+ if (!qgl_context_storage.hasLocalData() && QThread::currentThread())
+ qgl_context_storage.setLocalData(new QGLThreadContext);
+ if (qgl_context_storage.hasLocalData())
+ qgl_context_storage.localData()->context = this;
+ currentCtx = this;
+ }
+}
+
+void QGLContext::doneCurrent()
+{
+ Q_D(QGLContext);
+ if (d->eglContext)
+ d->eglContext->doneCurrent();
+
+ if (qgl_context_storage.hasLocalData())
+ qgl_context_storage.localData()->context = 0;
+ currentCtx = 0;
+}
+
+
+void QGLContext::swapBuffers() const
+{
+ Q_D(const QGLContext);
+ if(!d->valid || !d->eglContext)
+ return;
+
+ d->eglContext->swapBuffers();
+}
+
+QColor QGLContext::overlayTransparentColor() const
+{
+ return QColor(0, 0, 0); // Invalid color
+}
+
+uint QGLContext::colorIndex(const QColor &c) const
+{
+ //### color index doesn't work on egl
+ Q_UNUSED(c);
+ return 0;
+}
+
+void QGLContext::generateFontDisplayLists(const QFont & fnt, int listBase)
+{
+ Q_UNUSED(fnt);
+ Q_UNUSED(listBase);
+}
+
+void *QGLContext::getProcAddress(const QString &proc) const
+{
+ return (void*)eglGetProcAddress(reinterpret_cast<const char *>(proc.toLatin1().data()));
+}
+
+bool QGLWidget::event(QEvent *e)
+{
+ return QWidget::event(e);
+}
+
+void QGLWidget::setMouseTracking(bool enable)
+{
+ QWidget::setMouseTracking(enable);
+}
+
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ resizeGL(width(), height());
+ //handle overlay
+}
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return 0;
+}
+
+void QGLWidget::makeOverlayCurrent()
+{
+ //handle overlay
+}
+
+void QGLWidget::updateOverlayGL()
+{
+ //handle overlay
+}
+
+void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if(context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+
+ if(d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+ if(!d->glcx->isValid())
+ d->glcx->create(shareContext ? shareContext : oldcx);
+ if(deleteOldContext)
+ delete oldcx;
+}
+
+void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget* shareWidget)
+{
+ Q_Q(QGLWidget);
+
+ QGLScreen *glScreen = glScreenForDevice(q);
+ if (glScreen) {
+ wsurf = static_cast<QWSGLWindowSurface*>(glScreen->createSurface(q));
+ q->setWindowSurface(wsurf);
+ }
+
+ initContext(context, shareWidget);
+
+ if(q->isValid() && glcx->format().hasOverlay()) {
+ //no overlay
+ qWarning("QtOpenGL ES doesn't currently support overlays");
+ }
+}
+
+bool QGLWidgetPrivate::renderCxPm(QPixmap*)
+{
+ return false;
+}
+
+void QGLWidgetPrivate::cleanupColormaps()
+{
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ return d_func()->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap &)
+{
+}
+
+void QGLExtensions::init()
+{
+ static bool init_done = false;
+
+ if (init_done)
+ return;
+ init_done = true;
+ init_extensions();
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_win.cpp b/src/opengl/qgl_win.cpp
new file mode 100644
index 0000000000..bd8569a30a
--- /dev/null
+++ b/src/opengl/qgl_win.cpp
@@ -0,0 +1,1499 @@
+/****************************************************************************
+**
+** 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 <qgl.h>
+#include <qlist.h>
+#include <qmap.h>
+#include <qpixmap.h>
+#include <qevent.h>
+#include <private/qgl_p.h>
+#include <qcolormap.h>
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+#include <qcolor.h>
+
+#include <windows.h>
+
+typedef bool (APIENTRY *PFNWGLGETPIXELFORMATATTRIBIVARB)(HDC hdc,
+ int iPixelFormat,
+ int iLayerPlane,
+ uint nAttributes,
+ const int *piAttributes,
+ int *piValues);
+typedef bool (APIENTRY *PFNWGLCHOOSEPIXELFORMATARB)(HDC hdc,
+ const int *piAttribList,
+ const float *pfAttribFList,
+ uint nMaxFormats,
+ int *piFormats,
+ UINT *nNumFormats);
+#ifndef WGL_ARB_multisample
+#define WGL_SAMPLE_BUFFERS_ARB 0x2041
+#define WGL_SAMPLES_ARB 0x2042
+#endif
+
+#ifndef WGL_ARB_pixel_format
+#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
+#define WGL_DRAW_TO_WINDOW_ARB 0x2001
+#define WGL_DRAW_TO_BITMAP_ARB 0x2002
+#define WGL_ACCELERATION_ARB 0x2003
+#define WGL_NEED_PALETTE_ARB 0x2004
+#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
+#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
+#define WGL_SWAP_METHOD_ARB 0x2007
+#define WGL_NUMBER_OVERLAYS_ARB 0x2008
+#define WGL_NUMBER_UNDERLAYS_ARB 0x2009
+#define WGL_TRANSPARENT_ARB 0x200A
+#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
+#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
+#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
+#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
+#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
+#define WGL_SHARE_DEPTH_ARB 0x200C
+#define WGL_SHARE_STENCIL_ARB 0x200D
+#define WGL_SHARE_ACCUM_ARB 0x200E
+#define WGL_SUPPORT_GDI_ARB 0x200F
+#define WGL_SUPPORT_OPENGL_ARB 0x2010
+#define WGL_DOUBLE_BUFFER_ARB 0x2011
+#define WGL_STEREO_ARB 0x2012
+#define WGL_PIXEL_TYPE_ARB 0x2013
+#define WGL_COLOR_BITS_ARB 0x2014
+#define WGL_RED_BITS_ARB 0x2015
+#define WGL_RED_SHIFT_ARB 0x2016
+#define WGL_GREEN_BITS_ARB 0x2017
+#define WGL_GREEN_SHIFT_ARB 0x2018
+#define WGL_BLUE_BITS_ARB 0x2019
+#define WGL_BLUE_SHIFT_ARB 0x201A
+#define WGL_ALPHA_BITS_ARB 0x201B
+#define WGL_ALPHA_SHIFT_ARB 0x201C
+#define WGL_ACCUM_BITS_ARB 0x201D
+#define WGL_ACCUM_RED_BITS_ARB 0x201E
+#define WGL_ACCUM_GREEN_BITS_ARB 0x201F
+#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
+#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
+#define WGL_DEPTH_BITS_ARB 0x2022
+#define WGL_STENCIL_BITS_ARB 0x2023
+#define WGL_AUX_BUFFERS_ARB 0x2024
+#define WGL_NO_ACCELERATION_ARB 0x2025
+#define WGL_GENERIC_ACCELERATION_ARB 0x2026
+#define WGL_FULL_ACCELERATION_ARB 0x2027
+#define WGL_SWAP_EXCHANGE_ARB 0x2028
+#define WGL_SWAP_COPY_ARB 0x2029
+#define WGL_SWAP_UNDEFINED_ARB 0x202A
+#define WGL_TYPE_RGBA_ARB 0x202B
+#define WGL_TYPE_COLORINDEX_ARB 0x202C
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QGLCmapPrivate
+{
+public:
+ QGLCmapPrivate() : count(1) { }
+ void ref() { ++count; }
+ bool deref() { return !--count; }
+ uint count;
+
+ enum AllocState{ UnAllocated = 0, Allocated = 0x01, Reserved = 0x02 };
+
+ int maxSize;
+ QVector<uint> colorArray;
+ QVector<quint8> allocArray;
+ QVector<quint8> contextArray;
+ QMap<uint,int> colorMap;
+};
+
+/*****************************************************************************
+ QColorMap class - temporarily here, until it is ready for prime time
+ *****************************************************************************/
+
+/****************************************************************************
+**
+** Definition of QColorMap class
+**
+****************************************************************************/
+
+class QGLCmapPrivate;
+
+class /*Q_EXPORT*/ QGLCmap
+{
+public:
+ enum Flags { Reserved = 0x01 };
+
+ QGLCmap(int maxSize = 256);
+ QGLCmap(const QGLCmap& map);
+ ~QGLCmap();
+
+ QGLCmap& operator=(const QGLCmap& map);
+
+ // isEmpty and/or isNull ?
+ int size() const;
+ int maxSize() const;
+
+ void resize(int newSize);
+
+ int find(QRgb color) const;
+ int findNearest(QRgb color) const;
+ int allocate(QRgb color, uint flags = 0, quint8 context = 0);
+
+ void setEntry(int idx, QRgb color, uint flags = 0, quint8 context = 0);
+
+ const QRgb* colors() const;
+
+private:
+ void detach();
+ QGLCmapPrivate* d;
+};
+
+
+QGLCmap::QGLCmap(int maxSize) // add a bool prealloc?
+{
+ d = new QGLCmapPrivate;
+ d->maxSize = maxSize;
+}
+
+
+QGLCmap::QGLCmap(const QGLCmap& map)
+{
+ d = map.d;
+ d->ref();
+}
+
+
+QGLCmap::~QGLCmap()
+{
+ if (d && d->deref())
+ delete d;
+ d = 0;
+}
+
+
+QGLCmap& QGLCmap::operator=(const QGLCmap& map)
+{
+ map.d->ref();
+ if (d->deref())
+ delete d;
+ d = map.d;
+ return *this;
+}
+
+
+int QGLCmap::size() const
+{
+ return d->colorArray.size();
+}
+
+
+int QGLCmap::maxSize() const
+{
+ return d->maxSize;
+}
+
+
+void QGLCmap::detach()
+{
+ if (d->count != 1) {
+ d->deref();
+ QGLCmapPrivate* newd = new QGLCmapPrivate;
+ newd->maxSize = d->maxSize;
+ newd->colorArray = d->colorArray;
+ newd->allocArray = d->allocArray;
+ newd->contextArray = d->contextArray;
+ newd->colorArray.detach();
+ newd->allocArray.detach();
+ newd->contextArray.detach();
+ newd->colorMap = d->colorMap;
+ d = newd;
+ }
+}
+
+
+void QGLCmap::resize(int newSize)
+{
+ if (newSize < 0 || newSize > d->maxSize) {
+ qWarning("QGLCmap::resize(): size out of range");
+ return;
+ }
+ int oldSize = size();
+ detach();
+ //if shrinking; remove the lost elems from colorMap
+ d->colorArray.resize(newSize);
+ d->allocArray.resize(newSize);
+ d->contextArray.resize(newSize);
+ if (newSize > oldSize) {
+ memset(d->allocArray.data() + oldSize, 0, newSize - oldSize);
+ memset(d->contextArray.data() + oldSize, 0, newSize - oldSize);
+ }
+}
+
+
+int QGLCmap::find(QRgb color) const
+{
+ QMap<uint,int>::ConstIterator it = d->colorMap.find(color);
+ if (it != d->colorMap.end())
+ return *it;
+ return -1;
+}
+
+
+int QGLCmap::findNearest(QRgb color) const
+{
+ int idx = find(color);
+ if (idx >= 0)
+ return idx;
+ int mapSize = size();
+ int mindist = 200000;
+ int r = qRed(color);
+ int g = qGreen(color);
+ int b = qBlue(color);
+ int rx, gx, bx, dist;
+ for (int i=0; i < mapSize; i++) {
+ if (!(d->allocArray[i] & QGLCmapPrivate::Allocated))
+ continue;
+ QRgb ci = d->colorArray[i];
+ rx = r - qRed(ci);
+ gx = g - qGreen(ci);
+ bx = b - qBlue(ci);
+ dist = rx*rx + gx*gx + bx*bx; // calculate distance
+ if (dist < mindist) { // minimal?
+ mindist = dist;
+ idx = i;
+ }
+ }
+ return idx;
+}
+
+
+
+
+// Does not always allocate; returns existing c idx if found
+
+int QGLCmap::allocate(QRgb color, uint flags, quint8 context)
+{
+ int idx = find(color);
+ if (idx >= 0)
+ return idx;
+
+ int mapSize = d->colorArray.size();
+ int newIdx = d->allocArray.indexOf(QGLCmapPrivate::UnAllocated);
+
+ if (newIdx < 0) { // Must allocate more room
+ if (mapSize < d->maxSize) {
+ newIdx = mapSize;
+ mapSize++;
+ resize(mapSize);
+ }
+ else {
+ //# add a bool param that says what to do in case no more room -
+ // fail (-1) or return nearest?
+ return -1;
+ }
+ }
+
+ d->colorArray[newIdx] = color;
+ if (flags & QGLCmap::Reserved) {
+ d->allocArray[newIdx] = QGLCmapPrivate::Reserved;
+ }
+ else {
+ d->allocArray[newIdx] = QGLCmapPrivate::Allocated;
+ d->colorMap.insert(color, newIdx);
+ }
+ d->contextArray[newIdx] = context;
+ return newIdx;
+}
+
+
+void QGLCmap::setEntry(int idx, QRgb color, uint flags, quint8 context)
+{
+ if (idx < 0 || idx >= d->maxSize) {
+ qWarning("QGLCmap::set(): Index out of range");
+ return;
+ }
+ detach();
+ int mapSize = size();
+ if (idx >= mapSize) {
+ mapSize = idx + 1;
+ resize(mapSize);
+ }
+ d->colorArray[idx] = color;
+ if (flags & QGLCmap::Reserved) {
+ d->allocArray[idx] = QGLCmapPrivate::Reserved;
+ }
+ else {
+ d->allocArray[idx] = QGLCmapPrivate::Allocated;
+ d->colorMap.insert(color, idx);
+ }
+ d->contextArray[idx] = context;
+}
+
+
+const QRgb* QGLCmap::colors() const
+{
+ return d->colorArray.data();
+}
+
+
+
+/*****************************************************************************
+ QGLFormat Win32/WGL-specific code
+ *****************************************************************************/
+
+
+void qwglError(const char* method, const char* func)
+{
+#ifndef QT_NO_DEBUG
+ char* lpMsgBuf;
+ FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (char*) &lpMsgBuf, 0, 0);
+ qWarning("%s : %s failed: %s", method, func, lpMsgBuf);
+ LocalFree(lpMsgBuf);
+#else
+ Q_UNUSED(method);
+ Q_UNUSED(func);
+#endif
+}
+
+
+
+bool QGLFormat::hasOpenGL()
+{
+ return true;
+}
+
+static bool opengl32dll = false;
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ // workaround for matrox driver:
+ // make a cheap call to opengl to force loading of DLL
+ if (!opengl32dll) {
+ GLint params;
+ glGetIntegerv(GL_DEPTH_BITS, &params);
+ opengl32dll = true;
+ }
+
+ static bool checkDone = false;
+ static bool hasOl = false;
+
+ if (!checkDone) {
+ checkDone = true;
+ HDC display_dc = GetDC(0);
+ int pfiMax = DescribePixelFormat(display_dc, 0, 0, NULL);
+ PIXELFORMATDESCRIPTOR pfd;
+ for (int pfi = 1; pfi <= pfiMax; pfi++) {
+ DescribePixelFormat(display_dc, pfi, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
+ if ((pfd.bReserved & 0x0f) && (pfd.dwFlags & PFD_SUPPORT_OPENGL)) {
+ // This format has overlays/underlays
+ LAYERPLANEDESCRIPTOR lpd;
+ wglDescribeLayerPlane(display_dc, pfi, 1,
+ sizeof(LAYERPLANEDESCRIPTOR), &lpd);
+ if (lpd.dwFlags & LPD_SUPPORT_OPENGL) {
+ hasOl = true;
+ break;
+ }
+ }
+ }
+ ReleaseDC(0, display_dc);
+ }
+ return hasOl;
+}
+
+
+/*****************************************************************************
+ QGLContext Win32/WGL-specific code
+ *****************************************************************************/
+
+static uchar qgl_rgb_palette_comp(int idx, uint nbits, uint shift)
+{
+ const uchar map_3_to_8[8] = {
+ 0, 0111>>1, 0222>>1, 0333>>1, 0444>>1, 0555>>1, 0666>>1, 0377
+ };
+ const uchar map_2_to_8[4] = {
+ 0, 0x55, 0xaa, 0xff
+ };
+ const uchar map_1_to_8[2] = {
+ 0, 255
+ };
+
+ uchar val = (uchar) (idx >> shift);
+ uchar res = 0;
+ switch (nbits) {
+ case 1:
+ val &= 0x1;
+ res = map_1_to_8[val];
+ break;
+ case 2:
+ val &= 0x3;
+ res = map_2_to_8[val];
+ break;
+ case 3:
+ val &= 0x7;
+ res = map_3_to_8[val];
+ break;
+ default:
+ res = 0;
+ }
+ return res;
+}
+
+
+static QRgb* qgl_create_rgb_palette(const PIXELFORMATDESCRIPTOR* pfd)
+{
+ if ((pfd->iPixelType != PFD_TYPE_RGBA) ||
+ !(pfd->dwFlags & PFD_NEED_PALETTE) ||
+ (pfd->cColorBits != 8))
+ return 0;
+ int numEntries = 1 << pfd->cColorBits;
+ QRgb* pal = new QRgb[numEntries];
+ for (int i = 0; i < numEntries; i++) {
+ int r = qgl_rgb_palette_comp(i, pfd->cRedBits, pfd->cRedShift);
+ int g = qgl_rgb_palette_comp(i, pfd->cGreenBits, pfd->cGreenShift);
+ int b = qgl_rgb_palette_comp(i, pfd->cBlueBits, pfd->cBlueShift);
+ pal[i] = qRgb(r, g, b);
+ }
+
+ const int syscol_indices[12] = {
+ 3, 24, 27, 64, 67, 88, 173, 181, 236, 247, 164, 91
+ };
+
+ const uint syscols[20] = {
+ 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080,
+ 0x008080, 0xc0c0c0, 0xc0dcc0, 0xa6caf0, 0xfffbf0, 0xa0a0a4,
+ 0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff,
+ 0x00ffff, 0xffffff
+ }; // colors #1 - #12 are not present in pal; gets added below
+
+ if ((pfd->cColorBits == 8) &&
+ (pfd->cRedBits == 3) && (pfd->cRedShift == 0) &&
+ (pfd->cGreenBits == 3) && (pfd->cGreenShift == 3) &&
+ (pfd->cBlueBits == 2) && (pfd->cBlueShift == 6)) {
+ for (int j = 0 ; j < 12 ; j++)
+ pal[syscol_indices[j]] = QRgb(syscols[j+1]);
+ }
+
+ return pal;
+}
+
+static QGLFormat pfdToQGLFormat(const PIXELFORMATDESCRIPTOR* pfd)
+{
+ QGLFormat fmt;
+ fmt.setDoubleBuffer(pfd->dwFlags & PFD_DOUBLEBUFFER);
+ fmt.setDepth(pfd->cDepthBits);
+ if (fmt.depth())
+ fmt.setDepthBufferSize(pfd->cDepthBits);
+ fmt.setRgba(pfd->iPixelType == PFD_TYPE_RGBA);
+ fmt.setRedBufferSize(pfd->cRedBits);
+ fmt.setGreenBufferSize(pfd->cGreenBits);
+ fmt.setBlueBufferSize(pfd->cBlueBits);
+ fmt.setAlpha(pfd->cAlphaBits);
+ if (fmt.alpha())
+ fmt.setAlphaBufferSize(pfd->cAlphaBits);
+ fmt.setAccum(pfd->cAccumBits);
+ if (fmt.accum())
+ fmt.setAccumBufferSize(pfd->cAccumRedBits);
+ fmt.setStencil(pfd->cStencilBits);
+ if (fmt.stencil())
+ fmt.setStencilBufferSize(pfd->cStencilBits);
+ fmt.setStereo(pfd->dwFlags & PFD_STEREO);
+ fmt.setDirectRendering((pfd->dwFlags & PFD_GENERIC_ACCELERATED) ||
+ !(pfd->dwFlags & PFD_GENERIC_FORMAT));
+ fmt.setOverlay((pfd->bReserved & 0x0f) != 0);
+ return fmt;
+}
+
+/*
+ NB! requires a current GL context to work
+*/
+QGLFormat pfiToQGLFormat(HDC hdc, int pfi)
+{
+ QGLFormat fmt;
+ QVarLengthArray<int> iAttributes(40);
+ QVarLengthArray<int> iValues(40);
+ int i = 0;
+ bool has_sample_buffers = QGLExtensions::glExtensions & QGLExtensions::SampleBuffers;
+
+ iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; // 0
+ iAttributes[i++] = WGL_DEPTH_BITS_ARB; // 1
+ iAttributes[i++] = WGL_PIXEL_TYPE_ARB; // 2
+ iAttributes[i++] = WGL_RED_BITS_ARB; // 3
+ iAttributes[i++] = WGL_GREEN_BITS_ARB; // 4
+ iAttributes[i++] = WGL_BLUE_BITS_ARB; // 5
+ iAttributes[i++] = WGL_ALPHA_BITS_ARB; // 6
+ iAttributes[i++] = WGL_ACCUM_BITS_ARB; // 7
+ iAttributes[i++] = WGL_STENCIL_BITS_ARB; // 8
+ iAttributes[i++] = WGL_STEREO_ARB; // 9
+ iAttributes[i++] = WGL_ACCELERATION_ARB; // 10
+ iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB; // 11
+ if (has_sample_buffers) {
+ iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; // 12
+ iAttributes[i++] = WGL_SAMPLES_ARB; // 13
+ }
+ PFNWGLGETPIXELFORMATATTRIBIVARB wglGetPixelFormatAttribivARB =
+ (PFNWGLGETPIXELFORMATATTRIBIVARB) wglGetProcAddress("wglGetPixelFormatAttribivARB");
+
+ if (wglGetPixelFormatAttribivARB
+ && wglGetPixelFormatAttribivARB(hdc, pfi, 0, i,
+ iAttributes.constData(),
+ iValues.data()))
+ {
+ fmt.setDoubleBuffer(iValues[0]);
+ fmt.setDepth(iValues[1]);
+ if (fmt.depth())
+ fmt.setDepthBufferSize(iValues[1]);
+ fmt.setRgba(iValues[2] == WGL_TYPE_RGBA_ARB);
+ fmt.setRedBufferSize(iValues[3]);
+ fmt.setGreenBufferSize(iValues[4]);
+ fmt.setBlueBufferSize(iValues[5]);
+ fmt.setAlpha(iValues[6]);
+ if (fmt.alpha())
+ fmt.setAlphaBufferSize(iValues[6]);
+ fmt.setAccum(iValues[7]);
+ if (fmt.accum())
+ fmt.setAccumBufferSize(iValues[7]);
+ fmt.setStencil(iValues[8]);
+ if (fmt.stencil())
+ fmt.setStencilBufferSize(iValues[8]);
+ fmt.setStereo(iValues[9]);
+ if (iValues[10] == WGL_FULL_ACCELERATION_ARB)
+ fmt.setDirectRendering(true);
+ else
+ fmt.setDirectRendering(false);
+ fmt.setOverlay(iValues[11]);
+ if (has_sample_buffers) {
+ fmt.setSampleBuffers(iValues[12]);
+ if (fmt.sampleBuffers())
+ fmt.setSamples(iValues[13]);
+ }
+ }
+#if 0
+ qDebug() << "values for pfi:" << pfi;
+ qDebug() << "doublebuffer 0:" << fmt.doubleBuffer();
+ qDebug() << "depthbuffer 1:" << fmt.depthBufferSize();
+ qDebug() << "rgba 2:" << fmt.rgba();
+ qDebug() << "red size 3:" << fmt.redBufferSize();
+ qDebug() << "green size 4:" << fmt.greenBufferSize();
+ qDebug() << "blue size 5:" << fmt.blueBufferSize();
+ qDebug() << "alpha size 6:" << fmt.alphaBufferSize();
+ qDebug() << "accum size 7:" << fmt.accumBufferSize();
+ qDebug() << "stencil size 8:" << fmt.stencilBufferSize();
+ qDebug() << "stereo 9:" << fmt.stereo();
+ qDebug() << "direct 10:" << fmt.directRendering();
+ qDebug() << "has overlays 11:" << fmt.hasOverlay();
+ qDebug() << "sample buff 12:" << fmt.sampleBuffers();
+ qDebug() << "num samples 13:" << fmt.samples();
+#endif
+ return fmt;
+}
+
+
+/*
+ Creates a temporary GL context and makes it current
+ - cleans up when the object is destructed.
+*/
+
+Q_GUI_EXPORT const QString qt_getRegisteredWndClass();
+
+class QGLTempContext
+{
+public:
+ QGLTempContext(bool directRendering, QWidget *parent = 0)
+ {
+ QString windowClassName = qt_getRegisteredWndClass();
+ if (parent && !parent->internalWinId())
+ parent = parent->nativeParentWidget();
+ QT_WA({
+ const TCHAR *cname = (TCHAR*)windowClassName.utf16();
+ dmy_id = CreateWindow(cname, 0, 0, 0, 0, 1, 1,
+ parent ? parent->winId() : 0, 0, qWinAppInst(), 0);
+ } , {
+ dmy_id = CreateWindowA(windowClassName.toLatin1(), 0, 0, 0, 0, 1, 1,
+ parent ? parent->winId() : 0, 0, qWinAppInst(), 0);
+ });
+
+ dmy_pdc = GetDC(dmy_id);
+ PIXELFORMATDESCRIPTOR dmy_pfd;
+ memset(&dmy_pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
+ dmy_pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
+ dmy_pfd.nVersion = 1;
+ dmy_pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
+ dmy_pfd.iPixelType = PFD_TYPE_RGBA;
+ if (!directRendering)
+ dmy_pfd.dwFlags |= PFD_GENERIC_FORMAT;
+
+ int dmy_pf = ChoosePixelFormat(dmy_pdc, &dmy_pfd);
+ SetPixelFormat(dmy_pdc, dmy_pf, &dmy_pfd);
+ dmy_rc = wglCreateContext(dmy_pdc);
+ wglMakeCurrent(dmy_pdc, dmy_rc);
+ }
+
+ ~QGLTempContext() {
+ wglMakeCurrent(dmy_pdc, 0);
+ wglDeleteContext(dmy_rc);
+ ReleaseDC(dmy_id, dmy_pdc);
+ DestroyWindow(dmy_id);
+ }
+
+ HDC dmy_pdc;
+ HGLRC dmy_rc;
+ WId dmy_id;
+};
+
+bool QGLContext::chooseContext(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+ // workaround for matrox driver:
+ // make a cheap call to opengl to force loading of DLL
+ if (!opengl32dll) {
+ GLint params;
+ glGetIntegerv(GL_DEPTH_BITS, &params);
+ opengl32dll = true;
+ }
+
+ bool result = true;
+ HDC myDc;
+ QWidget *widget = 0;
+
+ if (deviceIsPixmap()) {
+ if (d->glFormat.plane())
+ return false; // Pixmaps can't have overlay
+ d->win = 0;
+ HDC display_dc = GetDC(0);
+ myDc = d->hbitmap_hdc = CreateCompatibleDC(display_dc);
+ QPixmap *px = static_cast<QPixmap *>(d->paintDevice);
+
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = px->width();
+ bmi.bmiHeader.biHeight = px->height();
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ d->hbitmap = CreateDIBSection(display_dc, &bmi, DIB_RGB_COLORS, 0, 0, 0);
+ SelectObject(myDc, d->hbitmap);
+ ReleaseDC(0, display_dc);
+ } else {
+ widget = static_cast<QWidget *>(d->paintDevice);
+ d->win = widget->winId();
+ myDc = GetDC(d->win);
+ }
+
+ // NB! the QGLTempContext object is needed for the
+ // wglGetProcAddress() calls to succeed and are absolutely
+ // necessary - don't remove!
+ QGLTempContext tmp_ctx(d->glFormat.directRendering(), widget);
+
+ if (!myDc) {
+ qWarning("QGLContext::chooseContext(): Paint device cannot be null");
+ result = false;
+ goto end;
+ }
+
+ if (d->glFormat.plane()) {
+ d->pixelFormatId = ((QGLWidget*)d->paintDevice)->context()->d_func()->pixelFormatId;
+ if (!d->pixelFormatId) { // I.e. the glwidget is invalid
+ qWarning("QGLContext::chooseContext(): Cannot create overlay context for invalid widget");
+ result = false;
+ goto end;
+ }
+
+ d->rc = wglCreateLayerContext(myDc, d->glFormat.plane());
+ if (!d->rc) {
+ qwglError("QGLContext::chooseContext()", "CreateLayerContext");
+ result = false;
+ goto end;
+ }
+
+ LAYERPLANEDESCRIPTOR lpfd;
+ wglDescribeLayerPlane(myDc, d->pixelFormatId, d->glFormat.plane(), sizeof(LAYERPLANEDESCRIPTOR), &lpfd);
+ d->glFormat.setDoubleBuffer(lpfd.dwFlags & LPD_DOUBLEBUFFER);
+ d->glFormat.setDepth(lpfd.cDepthBits);
+ d->glFormat.setRgba(lpfd.iPixelType == PFD_TYPE_RGBA);
+ if (d->glFormat.rgba()) {
+ if (d->glFormat.redBufferSize() != -1)
+ d->glFormat.setRedBufferSize(lpfd.cRedBits);
+ if (d->glFormat.greenBufferSize() != -1)
+ d->glFormat.setGreenBufferSize(lpfd.cGreenBits);
+ if (d->glFormat.blueBufferSize() != -1)
+ d->glFormat.setBlueBufferSize(lpfd.cBlueBits);
+ }
+ d->glFormat.setAlpha(lpfd.cAlphaBits);
+ d->glFormat.setAccum(lpfd.cAccumBits);
+ d->glFormat.setStencil(lpfd.cStencilBits);
+ d->glFormat.setStereo(lpfd.dwFlags & LPD_STEREO);
+ d->glFormat.setDirectRendering(false);
+ if (d->glFormat.depth())
+ d->glFormat.setDepthBufferSize(lpfd.cDepthBits);
+ if (d->glFormat.alpha())
+ d->glFormat.setAlphaBufferSize(lpfd.cAlphaBits);
+ if (d->glFormat.accum())
+ d->glFormat.setAccumBufferSize(lpfd.cAccumRedBits);
+ if (d->glFormat.stencil())
+ d->glFormat.setStencilBufferSize(lpfd.cStencilBits);
+
+ if (d->glFormat.rgba()) {
+ if (lpfd.dwFlags & LPD_TRANSPARENT)
+ d->transpColor = QColor(lpfd.crTransparent & 0xff,
+ (lpfd.crTransparent >> 8) & 0xff,
+ (lpfd.crTransparent >> 16) & 0xff);
+ else
+ d->transpColor = QColor(0, 0, 0);
+ }
+ else {
+ if (lpfd.dwFlags & LPD_TRANSPARENT)
+ d->transpColor = QColor(qRgb(1, 2, 3));//, lpfd.crTransparent);
+ else
+ d->transpColor = QColor(qRgb(1, 2, 3));//, 0);
+
+ d->cmap = new QGLCmap(1 << lpfd.cColorBits);
+ d->cmap->setEntry(lpfd.crTransparent, qRgb(1, 2, 3));//, QGLCmap::Reserved);
+ }
+
+ if (shareContext && shareContext->isValid()) {
+ QGLContext *share = const_cast<QGLContext *>(shareContext);
+ d->sharing = (wglShareLists(shareContext->d_func()->rc, d->rc) != 0);
+ share->d_func()->sharing = d->sharing;
+ }
+
+ goto end;
+ }
+ {
+ PIXELFORMATDESCRIPTOR pfd;
+ PIXELFORMATDESCRIPTOR realPfd;
+ d->pixelFormatId = choosePixelFormat(&pfd, myDc);
+ if (d->pixelFormatId == 0) {
+ qwglError("QGLContext::chooseContext()", "ChoosePixelFormat");
+ result = false;
+ goto end;
+ }
+
+ bool overlayRequested = d->glFormat.hasOverlay();
+ DescribePixelFormat(myDc, d->pixelFormatId, sizeof(PIXELFORMATDESCRIPTOR), &realPfd);
+
+ if (!deviceIsPixmap() && wglGetProcAddress("wglGetPixelFormatAttribivARB"))
+ d->glFormat = pfiToQGLFormat(myDc, d->pixelFormatId);
+ else
+ d->glFormat = pfdToQGLFormat(&realPfd);
+
+ d->glFormat.setOverlay(d->glFormat.hasOverlay() && overlayRequested);
+
+ if (deviceIsPixmap() && !(realPfd.dwFlags & PFD_DRAW_TO_BITMAP)) {
+ qWarning("QGLContext::chooseContext(): Failed to get pixmap rendering context.");
+ result = false;
+ goto end;
+ }
+
+ if (deviceIsPixmap() &&
+ (((QPixmap*)d->paintDevice)->depth() != realPfd.cColorBits)) {
+ qWarning("QGLContext::chooseContext(): Failed to get pixmap rendering context of suitable depth.");
+ result = false;
+ goto end;
+ }
+
+ if (!SetPixelFormat(myDc, d->pixelFormatId, &realPfd)) {
+ qwglError("QGLContext::chooseContext()", "SetPixelFormat");
+ result = false;
+ goto end;
+ }
+
+ if (!(d->rc = wglCreateLayerContext(myDc, 0))) {
+ qwglError("QGLContext::chooseContext()", "wglCreateContext");
+ result = false;
+ goto end;
+ }
+
+ if (shareContext && shareContext->isValid()) {
+ d->sharing = (wglShareLists(shareContext->d_func()->rc, d->rc) != 0);
+ const_cast<QGLContext *>(shareContext)->d_func()->sharing = d->sharing;
+ }
+
+ if(!deviceIsPixmap()) {
+ QRgb* pal = qgl_create_rgb_palette(&realPfd);
+ if (pal) {
+ QGLColormap cmap;
+ cmap.setEntries(256, pal);
+ ((QGLWidget*)d->paintDevice)->setColormap(cmap);
+ delete[] pal;
+ }
+ }
+ }
+
+end:
+ // vblanking
+ wglMakeCurrent(myDc, d->rc);
+ typedef BOOL (APIENTRYP PFNWGLSWAPINTERVALEXT) (int interval);
+ typedef int (APIENTRYP PFNWGLGETSWAPINTERVALEXT) (void);
+ PFNWGLSWAPINTERVALEXT wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXT) wglGetProcAddress("wglSwapIntervalEXT");
+ PFNWGLGETSWAPINTERVALEXT wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXT) wglGetProcAddress("wglGetSwapIntervalEXT");
+ if (wglSwapIntervalEXT && wglGetSwapIntervalEXT) {
+ if (d->reqFormat.swapInterval() != -1)
+ wglSwapIntervalEXT(d->reqFormat.swapInterval());
+ d->glFormat.setSwapInterval(wglGetSwapIntervalEXT());
+ }
+
+ if (d->win)
+ ReleaseDC(d->win, myDc);
+ return result;
+}
+
+
+
+static bool qLogEq(bool a, bool b)
+{
+ return (((!a) && (!b)) || (a && b));
+}
+
+/*!
+ \bold{Win32 only:} This virtual function chooses a pixel
+ format that matches the OpenGL \link setFormat() format\endlink.
+ Reimplement this function in a subclass if you need a custom
+ context.
+
+ \warning The \a dummyPfd pointer and \a pdc are used as a \c
+ PIXELFORMATDESCRIPTOR*. We use \c void to avoid using
+ Windows-specific types in our header files.
+
+ \sa chooseContext()
+*/
+
+int QGLContext::choosePixelFormat(void* dummyPfd, HDC pdc)
+{
+ Q_D(QGLContext);
+ // workaround for matrox driver:
+ // make a cheap call to opengl to force loading of DLL
+ if (!opengl32dll) {
+ GLint params;
+ glGetIntegerv(GL_DEPTH_BITS, &params);
+ opengl32dll = true;
+ }
+
+ PFNWGLCHOOSEPIXELFORMATARB wglChoosePixelFormatARB =
+ (PFNWGLCHOOSEPIXELFORMATARB) wglGetProcAddress("wglChoosePixelFormatARB");
+ int chosenPfi = 0;
+ if (!deviceIsPixmap() && wglChoosePixelFormatARB) {
+ bool valid;
+ int pixelFormat = 0;
+ uint numFormats = 0;
+ QVarLengthArray<int> iAttributes(40);
+ int i = 0;
+ iAttributes[i++] = WGL_ACCELERATION_ARB;
+ if (d->glFormat.directRendering())
+ iAttributes[i++] = WGL_FULL_ACCELERATION_ARB;
+ else
+ iAttributes[i++] = WGL_NO_ACCELERATION_ARB;
+ iAttributes[i++] = WGL_SUPPORT_OPENGL_ARB;
+ iAttributes[i++] = TRUE;
+ iAttributes[i++] = WGL_DRAW_TO_WINDOW_ARB;
+ iAttributes[i++] = TRUE;
+ iAttributes[i++] = WGL_COLOR_BITS_ARB;
+ iAttributes[i++] = 32;
+ iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB;
+ iAttributes[i++] = d->glFormat.doubleBuffer();
+ if (d->glFormat.stereo()) {
+ iAttributes[i++] = WGL_STEREO_ARB;
+ iAttributes[i++] = TRUE;
+ }
+ if (d->glFormat.depth()) {
+ iAttributes[i++] = WGL_DEPTH_BITS_ARB;
+ iAttributes[i++] = d->glFormat.depthBufferSize() == -1 ? 24 : d->glFormat.depthBufferSize();
+ }
+ iAttributes[i++] = WGL_PIXEL_TYPE_ARB;
+ if (d->glFormat.rgba()) {
+ iAttributes[i++] = WGL_TYPE_RGBA_ARB;
+ if (d->glFormat.redBufferSize() != -1) {
+ iAttributes[i++] = WGL_RED_BITS_ARB;
+ iAttributes[i++] = d->glFormat.redBufferSize();
+ }
+ if (d->glFormat.greenBufferSize() != -1) {
+ iAttributes[i++] = WGL_GREEN_BITS_ARB;
+ iAttributes[i++] = d->glFormat.greenBufferSize();
+ }
+ if (d->glFormat.blueBufferSize() != -1) {
+ iAttributes[i++] = WGL_BLUE_BITS_ARB;
+ iAttributes[i++] = d->glFormat.blueBufferSize();
+ }
+ } else {
+ iAttributes[i++] = WGL_TYPE_COLORINDEX_ARB;
+ }
+ if (d->glFormat.alpha()) {
+ iAttributes[i++] = WGL_ALPHA_BITS_ARB;
+ iAttributes[i++] = d->glFormat.alphaBufferSize() == -1 ? 8 : d->glFormat.alphaBufferSize();
+ }
+ if (d->glFormat.accum()) {
+ iAttributes[i++] = WGL_ACCUM_BITS_ARB;
+ iAttributes[i++] = d->glFormat.accumBufferSize() == -1 ? 16 : d->glFormat.accumBufferSize();
+ }
+ if (d->glFormat.stencil()) {
+ iAttributes[i++] = WGL_STENCIL_BITS_ARB;
+ iAttributes[i++] = d->glFormat.stencilBufferSize() == -1 ? 8 : d->glFormat.stencilBufferSize();
+ }
+ if (d->glFormat.hasOverlay()) {
+ iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB;
+ iAttributes[i++] = 1;
+ }
+ int si = 0;
+ bool trySampleBuffers = QGLExtensions::glExtensions & QGLExtensions::SampleBuffers;
+ if (trySampleBuffers && d->glFormat.sampleBuffers()) {
+ iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB;
+ iAttributes[i++] = TRUE;
+ iAttributes[i++] = WGL_SAMPLES_ARB;
+ si = i;
+ iAttributes[i++] = d->glFormat.samples() == -1 ? 4 : d->glFormat.samples();
+ }
+ iAttributes[i] = 0;
+
+ do {
+ valid = wglChoosePixelFormatARB(pdc, iAttributes.constData(), 0, 1,
+ &pixelFormat, &numFormats);
+ if (trySampleBuffers && (!valid || numFormats < 1) && d->glFormat.sampleBuffers())
+ iAttributes[si] /= 2; // try different no. samples - we aim for the best one
+ else
+ break;
+ } while ((!valid || numFormats < 1) && iAttributes[si] > 1);
+ chosenPfi = pixelFormat;
+ }
+
+ if (!chosenPfi) { // fallback if wglChoosePixelFormatARB() failed
+ int pmDepth = deviceIsPixmap() ? ((QPixmap*)d->paintDevice)->depth() : 0;
+ PIXELFORMATDESCRIPTOR* p = (PIXELFORMATDESCRIPTOR*)dummyPfd;
+ memset(p, 0, sizeof(PIXELFORMATDESCRIPTOR));
+ p->nSize = sizeof(PIXELFORMATDESCRIPTOR);
+ p->nVersion = 1;
+ p->dwFlags = PFD_SUPPORT_OPENGL;
+ if (deviceIsPixmap())
+ p->dwFlags |= PFD_DRAW_TO_BITMAP;
+ else
+ p->dwFlags |= PFD_DRAW_TO_WINDOW;
+ if (!d->glFormat.directRendering())
+ p->dwFlags |= PFD_GENERIC_FORMAT;
+ if (d->glFormat.doubleBuffer() && !deviceIsPixmap())
+ p->dwFlags |= PFD_DOUBLEBUFFER;
+ if (d->glFormat.stereo())
+ p->dwFlags |= PFD_STEREO;
+ if (d->glFormat.depth())
+ p->cDepthBits = d->glFormat.depthBufferSize() == -1 ? 32 : d->glFormat.depthBufferSize();
+ else
+ p->dwFlags |= PFD_DEPTH_DONTCARE;
+ if (d->glFormat.rgba()) {
+ p->iPixelType = PFD_TYPE_RGBA;
+ if (d->glFormat.redBufferSize() != -1)
+ p->cRedBits = d->glFormat.redBufferSize();
+ if (d->glFormat.greenBufferSize() != -1)
+ p->cGreenBits = d->glFormat.greenBufferSize();
+ if (d->glFormat.blueBufferSize() != -1)
+ p->cBlueBits = d->glFormat.blueBufferSize();
+ if (deviceIsPixmap())
+ p->cColorBits = pmDepth;
+ else
+ p->cColorBits = 32;
+ } else {
+ p->iPixelType = PFD_TYPE_COLORINDEX;
+ p->cColorBits = 8;
+ }
+ if (d->glFormat.alpha())
+ p->cAlphaBits = d->glFormat.alphaBufferSize() == -1 ? 8 : d->glFormat.alphaBufferSize();
+ if (d->glFormat.accum()) {
+ p->cAccumRedBits = p->cAccumGreenBits = p->cAccumBlueBits = p->cAccumAlphaBits =
+ d->glFormat.accumBufferSize() == -1 ? 16 : d->glFormat.accumBufferSize();
+ }
+ if (d->glFormat.stencil())
+ p->cStencilBits = d->glFormat.stencilBufferSize() == -1 ? 8 : d->glFormat.stencilBufferSize();
+ p->iLayerType = PFD_MAIN_PLANE;
+ chosenPfi = ChoosePixelFormat(pdc, p);
+
+ if (!chosenPfi)
+ qErrnoWarning("QGLContext: ChoosePixelFormat failed");
+
+ // Since the GDI function ChoosePixelFormat() does not handle
+ // overlay and direct-rendering requests, we must roll our own here
+
+ bool doSearch = chosenPfi <= 0;
+ PIXELFORMATDESCRIPTOR pfd;
+ QGLFormat fmt;
+ if (!doSearch) {
+ DescribePixelFormat(pdc, chosenPfi, sizeof(PIXELFORMATDESCRIPTOR),
+ &pfd);
+ fmt = pfdToQGLFormat(&pfd);
+ if (d->glFormat.hasOverlay() && !fmt.hasOverlay())
+ doSearch = true;
+ else if (!qLogEq(d->glFormat.directRendering(), fmt.directRendering()))
+ doSearch = true;
+ else if (deviceIsPixmap() && (!(pfd.dwFlags & PFD_DRAW_TO_BITMAP) ||
+ pfd.cColorBits != pmDepth))
+ doSearch = true;
+ else if (!deviceIsPixmap() && !(pfd.dwFlags & PFD_DRAW_TO_WINDOW))
+ doSearch = true;
+ else if (!qLogEq(d->glFormat.rgba(), fmt.rgba()))
+ doSearch = true;
+ }
+
+ if (doSearch) {
+ int pfiMax = DescribePixelFormat(pdc, 0, 0, NULL);
+ int bestScore = -1;
+ int bestPfi = -1;
+ for (int pfi = 1; pfi <= pfiMax; pfi++) {
+ DescribePixelFormat(pdc, pfi, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
+ if (!(pfd.dwFlags & PFD_SUPPORT_OPENGL))
+ continue;
+ if (deviceIsPixmap() && (!(pfd.dwFlags & PFD_DRAW_TO_BITMAP) ||
+ pfd.cColorBits != pmDepth))
+ continue;
+ if (!deviceIsPixmap() && !(pfd.dwFlags & PFD_DRAW_TO_WINDOW))
+ continue;
+
+ fmt = pfdToQGLFormat(&pfd);
+ if (d->glFormat.hasOverlay() && !fmt.hasOverlay())
+ continue;
+
+ int score = pfd.cColorBits;
+ if (qLogEq(d->glFormat.depth(), fmt.depth()))
+ score += pfd.cDepthBits;
+ if (qLogEq(d->glFormat.alpha(), fmt.alpha()))
+ score += pfd.cAlphaBits;
+ if (qLogEq(d->glFormat.accum(), fmt.accum()))
+ score += pfd.cAccumBits;
+ if (qLogEq(d->glFormat.stencil(), fmt.stencil()))
+ score += pfd.cStencilBits;
+ if (qLogEq(d->glFormat.doubleBuffer(), fmt.doubleBuffer()))
+ score += 1000;
+ if (qLogEq(d->glFormat.stereo(), fmt.stereo()))
+ score += 2000;
+ if (qLogEq(d->glFormat.directRendering(), fmt.directRendering()))
+ score += 4000;
+ if (qLogEq(d->glFormat.rgba(), fmt.rgba()))
+ score += 8000;
+ if (score > bestScore) {
+ bestScore = score;
+ bestPfi = pfi;
+ }
+ }
+
+ if (bestPfi > 0)
+ chosenPfi = bestPfi;
+ }
+ }
+ return chosenPfi;
+}
+
+
+
+void QGLContext::reset()
+{
+ Q_D(QGLContext);
+ // workaround for matrox driver:
+ // make a cheap call to opengl to force loading of DLL
+ if (!opengl32dll) {
+ GLint params;
+ glGetIntegerv(GL_DEPTH_BITS, &params);
+ opengl32dll = true;
+ }
+
+ if (!d->valid)
+ return;
+ d->cleanup();
+ doneCurrent();
+ if (d->rc)
+ wglDeleteContext(d->rc);
+ d->rc = 0;
+ if (d->win && d->dc)
+ ReleaseDC(d->win, d->dc);
+ if (deviceIsPixmap()) {
+ DeleteDC(d->hbitmap_hdc);
+ DeleteObject(d->hbitmap);
+ d->hbitmap_hdc = 0;
+ d->hbitmap = 0;
+ }
+ d->dc = 0;
+ d->win = 0;
+ d->pixelFormatId = 0;
+ d->sharing = false;
+ d->valid = false;
+ d->transpColor = QColor();
+ delete d->cmap;
+ d->cmap = 0;
+ d->initDone = false;
+ qgl_share_reg()->removeShare(this);
+}
+
+//
+// NOTE: In a multi-threaded environment, each thread has a current
+// context. If we want to make this code thread-safe, we probably
+// have to use TLS (thread local storage) for keeping current contexts.
+//
+
+void QGLContext::makeCurrent()
+{
+ Q_D(QGLContext);
+ if (d->rc == wglGetCurrentContext() || !d->valid) // already current
+ return;
+ if (d->win) {
+ d->dc = GetDC(d->win);
+ if (!d->dc) {
+ qwglError("QGLContext::makeCurrent()", "GetDC()");
+ return;
+ }
+ } else if (deviceIsPixmap()) {
+ d->dc = d->hbitmap_hdc;
+ }
+
+ HPALETTE hpal = QColormap::hPal();
+ if (hpal) {
+ SelectPalette(d->dc, hpal, FALSE);
+ RealizePalette(d->dc);
+ }
+ if (d->glFormat.plane()) {
+ wglRealizeLayerPalette(d->dc, d->glFormat.plane(), TRUE);
+ }
+
+ if (wglMakeCurrent(d->dc, d->rc)) {
+ if (!qgl_context_storage.hasLocalData() && QThread::currentThread())
+ qgl_context_storage.setLocalData(new QGLThreadContext);
+ if (qgl_context_storage.hasLocalData())
+ qgl_context_storage.localData()->context = this;
+ currentCtx = this;
+ } else {
+ qwglError("QGLContext::makeCurrent()", "wglMakeCurrent");
+ }
+}
+
+
+void QGLContext::doneCurrent()
+{
+ Q_D(QGLContext);
+ currentCtx = 0;
+ wglMakeCurrent(0, 0);
+ if (qgl_context_storage.hasLocalData())
+ qgl_context_storage.localData()->context = 0;
+ if (deviceIsPixmap() && d->hbitmap) {
+ QPixmap *pm = static_cast<QPixmap *>(d->paintDevice);
+ *pm = QPixmap::fromWinHBITMAP(d->hbitmap);
+ }
+ if (d->win && d->dc) {
+ ReleaseDC(d->win, d->dc);
+ d->dc = 0;
+ }
+}
+
+void QGLContext::swapBuffers() const
+{
+ Q_D(const QGLContext);
+ if (d->dc && d->glFormat.doubleBuffer() && !deviceIsPixmap()) {
+ if (d->glFormat.plane())
+ wglSwapLayerBuffers(d->dc, WGL_SWAP_OVERLAY1);
+ else {
+ if (d->glFormat.hasOverlay())
+ wglSwapLayerBuffers(d->dc, WGL_SWAP_MAIN_PLANE);
+ else
+ SwapBuffers(d->dc);
+ }
+ }
+}
+
+
+QColor QGLContext::overlayTransparentColor() const
+{
+ return d_func()->transpColor;
+}
+
+
+uint QGLContext::colorIndex(const QColor& c) const
+{
+ Q_D(const QGLContext);
+ if (!isValid())
+ return 0;
+ if (d->cmap) {
+ int idx = d->cmap->find(c.rgb());
+ if (idx >= 0)
+ return idx;
+ if (d->dc && d->glFormat.plane()) {
+ idx = d->cmap->allocate(c.rgb());
+ if (idx >= 0) {
+ COLORREF r = RGB(qRed(c.rgb()),qGreen(c.rgb()),qBlue(c.rgb()));
+ wglSetLayerPaletteEntries(d->dc, d->glFormat.plane(), idx, 1, &r);
+ wglRealizeLayerPalette(d->dc, d->glFormat.plane(), TRUE);
+ return idx;
+ }
+ }
+ return d->cmap->findNearest(c.rgb());
+ }
+ QColormap cmap = QColormap::instance();
+ return cmap.pixel(c) & 0x00ffffff; // Assumes standard palette
+}
+
+void QGLContext::generateFontDisplayLists(const QFont & fnt, int listBase)
+{
+ if (!isValid())
+ return;
+
+ HDC display_dc = GetDC(0);
+ HDC tmp_dc = CreateCompatibleDC(display_dc);
+ HGDIOBJ old_font = SelectObject(tmp_dc, fnt.handle());
+
+ ReleaseDC(0, display_dc);
+
+ if (!wglUseFontBitmaps(tmp_dc, 0, 256, listBase))
+ qWarning("QGLContext::generateFontDisplayLists: Could not generate display lists for font '%s'", fnt.family().toLatin1().data());
+
+ SelectObject(tmp_dc, old_font);
+ DeleteDC(tmp_dc);
+}
+
+void *QGLContext::getProcAddress(const QString &proc) const
+{
+ return (void *)wglGetProcAddress(proc.toLatin1());
+}
+
+/*****************************************************************************
+ QGLWidget Win32/WGL-specific code
+ *****************************************************************************/
+
+void QGLWidgetPrivate::init(QGLContext *ctx, const QGLWidget* shareWidget)
+{
+ Q_Q(QGLWidget);
+ olcx = 0;
+ initContext(ctx, shareWidget);
+
+ if (q->isValid() && q->context()->format().hasOverlay()) {
+ olcx = new QGLContext(QGLFormat::defaultOverlayFormat(), q);
+ if (!olcx->create(shareWidget ? shareWidget->overlayContext() : 0)) {
+ delete olcx;
+ olcx = 0;
+ glcx->d_func()->glFormat.setOverlay(false);
+ }
+ } else {
+ olcx = 0;
+ }
+}
+
+/*\internal
+ Store color values in the given colormap.
+*/
+static void qStoreColors(HPALETTE cmap, const QGLColormap & cols)
+{
+ QRgb color;
+ PALETTEENTRY pe;
+
+ for (int i = 0; i < cols.size(); i++) {
+ color = cols.entryRgb(i);
+ pe.peRed = qRed(color);
+ pe.peGreen = qGreen(color);
+ pe.peBlue = qBlue(color);
+ pe.peFlags = 0;
+
+ SetPaletteEntries(cmap, i, 1, &pe);
+ }
+}
+
+void QGLWidgetPrivate::updateColormap()
+{
+ Q_Q(QGLWidget);
+ if (!cmap.handle())
+ return;
+ HDC hdc = GetDC(q->winId());
+ SelectPalette(hdc, (HPALETTE) cmap.handle(), TRUE);
+ qStoreColors((HPALETTE) cmap.handle(), cmap);
+ RealizePalette(hdc);
+ ReleaseDC(q->winId(), hdc);
+}
+
+void QGLWidget::setMouseTracking(bool enable)
+{
+ QWidget::setMouseTracking(enable);
+}
+
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ resizeGL(width(), height());
+ if (d->olcx) {
+ makeOverlayCurrent();
+ resizeOverlayGL(width(), height());
+ }
+}
+
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return d_func()->olcx;
+}
+
+
+void QGLWidget::makeOverlayCurrent()
+{
+ Q_D(QGLWidget);
+ if (d->olcx) {
+ d->olcx->makeCurrent();
+ if (!d->olcx->initialized()) {
+ initializeOverlayGL();
+ d->olcx->setInitialized(true);
+ }
+ }
+}
+
+
+void QGLWidget::updateOverlayGL()
+{
+ Q_D(QGLWidget);
+ if (d->olcx) {
+ makeOverlayCurrent();
+ paintOverlayGL();
+ if (d->olcx->format().doubleBuffer()) {
+ if (d->autoSwap)
+ d->olcx->swapBuffers();
+ }
+ else {
+ glFlush();
+ }
+ }
+}
+
+
+void QGLWidget::setContext(QGLContext *context,
+ const QGLContext* shareContext,
+ bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+ if (!context->deviceIsPixmap() && context->device() != this) {
+ qWarning("QGLWidget::setContext: Context must refer to this widget");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+
+ bool doShow = false;
+ if (oldcx && oldcx->d_func()->win == winId() && !d->glcx->deviceIsPixmap()) {
+ // We already have a context and must therefore create a new
+ // window since Windows does not permit setting a new OpenGL
+ // context for a window that already has one set.
+ doShow = isVisible();
+ QWidget *pW = static_cast<QWidget *>(parent());
+ QPoint pos = geometry().topLeft();
+ setParent(pW, windowFlags());
+ move(pos);
+ }
+
+ if (!d->glcx->isValid()) {
+ bool wasSharing = shareContext || oldcx && oldcx->isSharing();
+ d->glcx->create(shareContext ? shareContext : oldcx);
+ // the above is a trick to keep disp lists etc when a
+ // QGLWidget has been reparented, so remove the sharing
+ // flag if we don't actually have a sharing context.
+ if (!wasSharing)
+ d->glcx->d_ptr->sharing = false;
+ }
+
+ if (deleteOldContext)
+ delete oldcx;
+
+ if (doShow)
+ show();
+}
+
+
+bool QGLWidgetPrivate::renderCxPm(QPixmap*)
+{
+ return false;
+}
+
+void QGLWidgetPrivate::cleanupColormaps()
+{
+ Q_Q(QGLWidget);
+ if (cmap.handle()) {
+ HDC hdc = GetDC(q->winId());
+ SelectPalette(hdc, (HPALETTE) GetStockObject(DEFAULT_PALETTE), FALSE);
+ DeleteObject((HPALETTE) cmap.handle());
+ ReleaseDC(q->winId(), hdc);
+ cmap.setHandle(0);
+ }
+ return;
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ return d_func()->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap & c)
+{
+ Q_D(QGLWidget);
+ d->cmap = c;
+
+ if (d->cmap.handle()) { // already have an allocated cmap
+ d->updateColormap();
+ } else {
+ LOGPALETTE *lpal = (LOGPALETTE *) malloc(sizeof(LOGPALETTE)
+ +c.size()*sizeof(PALETTEENTRY));
+ lpal->palVersion = 0x300;
+ lpal->palNumEntries = c.size();
+ d->cmap.setHandle(CreatePalette(lpal));
+ free(lpal);
+ d->updateColormap();
+ }
+}
+
+void QGLExtensions::init()
+{
+ static bool init_done = false;
+
+ if (init_done)
+ return;
+ init_done = true;
+ QGLTempContext temp_ctx(QGLFormat::defaultFormat().directRendering());
+ init_extensions();
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_wince.cpp b/src/opengl/qgl_wince.cpp
new file mode 100644
index 0000000000..cb51598870
--- /dev/null
+++ b/src/opengl/qgl_wince.cpp
@@ -0,0 +1,739 @@
+/****************************************************************************
+**
+** 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 <qgl.h>
+#include <qlist.h>
+#include <qmap.h>
+#include <qpixmap.h>
+#include <qevent.h>
+#include <private/qgl_p.h>
+#include <qcolormap.h>
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+#include <qapplication.h>
+#include <qdesktopwidget>
+
+#include <windows.h>
+
+#include "qegl_p.h"
+#include "qgl_egl_p.h"
+#include "qgl_cl_p.h"
+
+
+QT_BEGIN_NAMESPACE
+
+
+
+class QGLCmapPrivate
+{
+public:
+ QGLCmapPrivate() : count(1) { }
+ void ref() { ++count; }
+ bool deref() { return !--count; }
+ uint count;
+
+ enum AllocState{ UnAllocated = 0, Allocated = 0x01, Reserved = 0x02 };
+
+ int maxSize;
+ QVector<uint> colorArray;
+ QVector<quint8> allocArray;
+ QVector<quint8> contextArray;
+ QMap<uint,int> colorMap;
+};
+
+/*****************************************************************************
+ QColorMap class - temporarily here, until it is ready for prime time
+ *****************************************************************************/
+
+/****************************************************************************
+**
+** Definition of QColorMap class
+**
+****************************************************************************/
+
+#ifndef QGLCMAP_H
+#define QGLCMAP_H
+
+#include <qcolor.h>
+
+
+
+
+/*****************************************************************************
+ QGLFormat Win32/WGL-specific code
+ *****************************************************************************/
+
+void qt_egl_add_platform_config(QEglProperties& props, QPaintDevice *device)
+{
+ int devType = device->devType();
+ if (devType == QInternal::Image)
+ props.setPixelFormat(static_cast<QImage *>(device)->format());
+ else
+ props.setPixelFormat(QImage::Format_RGB16);
+}
+
+
+bool QGLFormat::hasOpenGL()
+{
+ return true;
+}
+
+static bool opengl32dll = false;
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ return false; // ###
+}
+
+
+bool QGLContext::chooseContext(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+
+ // Validate the device.
+ if (!device())
+ return false;
+ int devType = device()->devType();
+ if (devType != QInternal::Pixmap && devType != QInternal::Image && devType != QInternal::Widget) {
+ qWarning("QGLContext::chooseContext(): Cannot create QGLContext's for paint device type %d", devType);
+ return false;
+ }
+
+ // Get the display and initialize it.
+ d->eglContext = new QEglContext();
+ d->eglContext->setApi(QEglContext::OpenGL);
+ if (!d->eglContext->openDisplay(device())) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ // Construct the configuration we need for this surface.
+ QEglProperties configProps;
+ qt_egl_add_platform_config(configProps, device());
+ qt_egl_set_format(configProps, devType, d->glFormat);
+ configProps.setRenderableType(QEglContext::OpenGL);
+
+ // Search for a matching configuration, reducing the complexity
+ // each time until we get something that matches.
+ if (!d->eglContext->chooseConfig(configProps)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ // Inform the higher layers about the actual format properties.
+ qt_egl_update_format(*(d->eglContext), d->glFormat);
+
+ // Create a new context for the configuration.
+ if (!d->eglContext->createContext
+ (shareContext ? shareContext->d_func()->eglContext : 0)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+#if defined(EGL_VERSION_1_1)
+ if (d->glFormat.swapInterval() != -1 && devType == QInternal::Widget)
+ eglSwapInterval(d->eglContext->display(), d->glFormat.swapInterval());
+#endif
+
+ // Create the EGL surface to draw into.
+ if (!d->eglContext->createSurface(device())) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ return true;
+
+}
+
+
+
+static bool qLogEq(bool a, bool b)
+{
+ return (((!a) && (!b)) || (a && b));
+}
+
+int QGLContext::choosePixelFormat(void* , HDC )
+{
+
+ return 0;
+}
+
+class QGLCmapPrivate;
+
+class /*Q_EXPORT*/ QGLCmap
+{
+public:
+ enum Flags { Reserved = 0x01 };
+
+ QGLCmap(int maxSize = 256);
+ QGLCmap(const QGLCmap& map);
+ ~QGLCmap();
+
+ QGLCmap& operator=(const QGLCmap& map);
+
+ // isEmpty and/or isNull ?
+ int size() const;
+ int maxSize() const;
+
+ void resize(int newSize);
+
+ int find(QRgb color) const;
+ int findNearest(QRgb color) const;
+ int allocate(QRgb color, uint flags = 0, quint8 context = 0);
+
+ void setEntry(int idx, QRgb color, uint flags = 0, quint8 context = 0);
+
+ const QRgb* colors() const;
+
+private:
+ void detach();
+ QGLCmapPrivate* d;
+};
+
+#endif
+
+
+QGLCmap::QGLCmap(int maxSize) // add a bool prealloc?
+{
+ d = new QGLCmapPrivate;
+ d->maxSize = maxSize;
+}
+
+QGLCmap::QGLCmap(const QGLCmap& map)
+{
+ d = map.d;
+ d->ref();
+}
+
+QGLCmap::~QGLCmap()
+{
+ if (d && d->deref())
+ delete d;
+ d = 0;
+}
+
+QGLCmap& QGLCmap::operator=(const QGLCmap& map)
+{
+ map.d->ref();
+ if (d->deref())
+ delete d;
+ d = map.d;
+ return *this;
+}
+
+int QGLCmap::size() const
+{
+ return d->colorArray.size();
+}
+
+int QGLCmap::maxSize() const
+{
+ return d->maxSize;
+}
+
+void QGLCmap::detach()
+{
+ if (d->count != 1) {
+ d->deref();
+ QGLCmapPrivate* newd = new QGLCmapPrivate;
+ newd->maxSize = d->maxSize;
+ newd->colorArray = d->colorArray;
+ newd->allocArray = d->allocArray;
+ newd->contextArray = d->contextArray;
+ newd->colorArray.detach();
+ newd->allocArray.detach();
+ newd->contextArray.detach();
+ newd->colorMap = d->colorMap;
+ d = newd;
+ }
+}
+
+
+void QGLCmap::resize(int newSize)
+{
+ if (newSize < 0 || newSize > d->maxSize) {
+ qWarning("QGLCmap::resize(): size out of range");
+ return;
+ }
+ int oldSize = size();
+ detach();
+ //if shrinking; remove the lost elems from colorMap
+ d->colorArray.resize(newSize);
+ d->allocArray.resize(newSize);
+ d->contextArray.resize(newSize);
+ if (newSize > oldSize) {
+ memset(d->allocArray.data() + oldSize, 0, newSize - oldSize);
+ memset(d->contextArray.data() + oldSize, 0, newSize - oldSize);
+ }
+}
+
+
+int QGLCmap::find(QRgb color) const
+{
+ QMap<uint,int>::ConstIterator it = d->colorMap.find(color);
+ if (it != d->colorMap.end())
+ return *it;
+ return -1;
+}
+
+
+int QGLCmap::findNearest(QRgb color) const
+{
+ int idx = find(color);
+ if (idx >= 0)
+ return idx;
+ int mapSize = size();
+ int mindist = 200000;
+ int r = qRed(color);
+ int g = qGreen(color);
+ int b = qBlue(color);
+ int rx, gx, bx, dist;
+ for (int i=0; i < mapSize; i++) {
+ if (!(d->allocArray[i] & QGLCmapPrivate::Allocated))
+ continue;
+ QRgb ci = d->colorArray[i];
+ rx = r - qRed(ci);
+ gx = g - qGreen(ci);
+ bx = b - qBlue(ci);
+ dist = rx*rx + gx*gx + bx*bx; // calculate distance
+ if (dist < mindist) { // minimal?
+ mindist = dist;
+ idx = i;
+ }
+ }
+ return idx;
+}
+
+
+// Does not always allocate; returns existing c idx if found
+
+int QGLCmap::allocate(QRgb color, uint flags, quint8 context)
+{
+ int idx = find(color);
+ if (idx >= 0)
+ return idx;
+
+ int mapSize = d->colorArray.size();
+ int newIdx = d->allocArray.indexOf(QGLCmapPrivate::UnAllocated);
+
+ if (newIdx < 0) { // Must allocate more room
+ if (mapSize < d->maxSize) {
+ newIdx = mapSize;
+ mapSize++;
+ resize(mapSize);
+ }
+ else {
+ //# add a bool param that says what to do in case no more room -
+ // fail (-1) or return nearest?
+ return -1;
+ }
+ }
+
+ d->colorArray[newIdx] = color;
+ if (flags & QGLCmap::Reserved) {
+ d->allocArray[newIdx] = QGLCmapPrivate::Reserved;
+ }
+ else {
+ d->allocArray[newIdx] = QGLCmapPrivate::Allocated;
+ d->colorMap.insert(color, newIdx);
+ }
+ d->contextArray[newIdx] = context;
+ return newIdx;
+}
+
+
+void QGLCmap::setEntry(int idx, QRgb color, uint flags, quint8 context)
+{
+ if (idx < 0 || idx >= d->maxSize) {
+ qWarning("QGLCmap::set(): Index out of range");
+ return;
+ }
+ detach();
+ int mapSize = size();
+ if (idx >= mapSize) {
+ mapSize = idx + 1;
+ resize(mapSize);
+ }
+ d->colorArray[idx] = color;
+ if (flags & QGLCmap::Reserved) {
+ d->allocArray[idx] = QGLCmapPrivate::Reserved;
+ }
+ else {
+ d->allocArray[idx] = QGLCmapPrivate::Allocated;
+ d->colorMap.insert(color, idx);
+ }
+ d->contextArray[idx] = context;
+}
+
+
+const QRgb* QGLCmap::colors() const
+{
+ return d->colorArray.data();
+}
+
+
+void QGLContext::reset()
+{
+ Q_D(QGLContext);
+ if (!d->valid)
+ return;
+ d->cleanup();
+ doneCurrent();
+ if (d->eglContext) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ }
+ d->crWin = false;
+ d->sharing = false;
+ d->valid = false;
+ d->transpColor = QColor();
+ d->initDone = false;
+ qgl_share_reg()->removeShare(this);
+}
+
+
+//
+// NOTE: In a multi-threaded environment, each thread has a current
+// context. If we want to make this code thread-safe, we probably
+// have to use TLS (thread local storage) for keeping current contexts.
+//
+
+void QGLContext::makeCurrent()
+{
+
+ Q_D(QGLContext);
+ if(!d->valid || !d->eglContext) {
+ qWarning("QGLContext::makeCurrent(): Cannot make invalid context current");
+ return;
+ }
+
+ if (d->eglContext->makeCurrent()) {
+ if (!qgl_context_storage.hasLocalData() && QThread::currentThread())
+ qgl_context_storage.setLocalData(new QGLThreadContext);
+ if (qgl_context_storage.hasLocalData())
+ qgl_context_storage.localData()->context = this;
+ currentCtx = this;
+ }
+}
+
+
+void QGLContext::doneCurrent()
+{
+
+ Q_D(QGLContext);
+ if (d->eglContext)
+ d->eglContext->doneCurrent();
+
+ if (qgl_context_storage.hasLocalData())
+ qgl_context_storage.localData()->context = 0;
+ currentCtx = 0;
+}
+
+void QGLContext::swapBuffers() const
+{
+ Q_D(const QGLContext);
+ if(!d->valid || !d->eglContext)
+ return;
+
+ d->eglContext->swapBuffers();
+}
+
+
+QColor QGLContext::overlayTransparentColor() const
+{
+ return d_func()->transpColor;
+}
+
+
+void QGLContext::generateFontDisplayLists(const QFont & fnt, int listBase)
+{
+ Q_UNUSED(fnt);
+ Q_UNUSED(listBase);
+}
+
+void *QGLContext::getProcAddress(const QString &proc) const
+{
+ return (void*)eglGetProcAddress(reinterpret_cast<const char *>(proc.toLatin1().data()));
+}
+
+/*****************************************************************************
+ QGLWidget Win32/WGL-specific code
+ *****************************************************************************/
+
+void QGLWidgetPrivate::init(QGLContext *ctx, const QGLWidget* shareWidget)
+{
+ Q_Q(QGLWidget);
+ olcx = 0;
+ initContext(ctx, shareWidget);
+
+ if (q->isValid() && q->context()->format().hasOverlay()) {
+ olcx = new QGLContext(QGLFormat::defaultOverlayFormat(), q);
+ if (!olcx->create(shareWidget ? shareWidget->overlayContext() : 0)) {
+ delete olcx;
+ olcx = 0;
+ glcx->d_func()->glFormat.setOverlay(false);
+ }
+ } else {
+ olcx = 0;
+ }
+}
+
+/*\internal
+ Store color values in the given colormap.
+*/
+static void qStoreColors(HPALETTE cmap, const QGLColormap & cols)
+{
+ QRgb color;
+ PALETTEENTRY pe;
+
+ for (int i = 0; i < cols.size(); i++) {
+ color = cols.entryRgb(i);
+ pe.peRed = qRed(color);
+ pe.peGreen = qGreen(color);
+ pe.peBlue = qBlue(color);
+ pe.peFlags = 0;
+
+ SetPaletteEntries(cmap, i, 1, &pe);
+ }
+}
+
+void QGLWidgetPrivate::updateColormap()
+{
+ Q_Q(QGLWidget);
+ if (!cmap.handle())
+ return;
+ HDC hdc = GetDC(q->winId());
+ SelectPalette(hdc, (HPALETTE) cmap.handle(), TRUE);
+ qStoreColors((HPALETTE) cmap.handle(), cmap);
+ RealizePalette(hdc);
+ ReleaseDC(q->winId(), hdc);
+}
+
+/*!
+ \reimp
+\*/
+bool QGLWidget::event(QEvent *e)
+{
+ Q_D(QGLWidget);
+ if (e->type() == QEvent::ParentChange) {
+ setContext(new QGLContext(d->glcx->requestedFormat(), this));
+ // the overlay needs to be recreated as well
+ delete d->olcx;
+ if (isValid() && context()->format().hasOverlay()) {
+ d->olcx = new QGLContext(QGLFormat::defaultOverlayFormat(), this);
+ if (!d->olcx->create(isSharing() ? d->glcx : 0)) {
+ delete d->olcx;
+ d->olcx = 0;
+ d->glcx->d_func()->glFormat.setOverlay(false);
+ }
+ } else {
+ d->olcx = 0;
+ }
+ } else if (e->type() == QEvent::Show && !format().rgba()) {
+ d->updateColormap();
+ }
+
+ return QWidget::event(e);
+}
+
+
+void QGLWidget::setMouseTracking(bool enable)
+{
+ QWidget::setMouseTracking(enable);
+}
+
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ resizeGL(width(), height());
+ if (d->olcx) {
+ makeOverlayCurrent();
+ resizeOverlayGL(width(), height());
+ }
+}
+
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return d_func()->olcx;
+}
+
+
+void QGLWidget::makeOverlayCurrent()
+{
+ Q_D(QGLWidget);
+ if (d->olcx) {
+ d->olcx->makeCurrent();
+ if (!d->olcx->initialized()) {
+ initializeOverlayGL();
+ d->olcx->setInitialized(true);
+ }
+ }
+}
+
+
+void QGLWidget::updateOverlayGL()
+{
+ Q_D(QGLWidget);
+ if (d->olcx) {
+ makeOverlayCurrent();
+ paintOverlayGL();
+ if (d->olcx->format().doubleBuffer()) {
+ if (d->autoSwap)
+ d->olcx->swapBuffers();
+ }
+ else {
+ glFlush();
+ }
+ }
+}
+
+void QGLWidget::setContext(QGLContext *context,
+ const QGLContext* shareContext,
+ bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+ if (!context->deviceIsPixmap() && context->device() != this) {
+ qWarning("QGLWidget::setContext: Context must refer to this widget");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+
+ bool doShow = false;
+ if (oldcx && oldcx->d_func()->win == winId() && !d->glcx->deviceIsPixmap()) {
+ // We already have a context and must therefore create a new
+ // window since Windows does not permit setting a new OpenGL
+ // context for a window that already has one set.
+ doShow = isVisible();
+ QWidget *pW = static_cast<QWidget *>(parent());
+ QPoint pos = geometry().topLeft();
+ setParent(pW, windowFlags());
+ move(pos);
+ }
+
+ if (!d->glcx->isValid()) {
+ d->glcx->create(shareContext ? shareContext : oldcx);
+ // the above is a trick to keep disp lists etc when a
+ // QGLWidget has been reparented, so remove the sharing
+ // flag if we don't actually have a sharing context.
+ if (!shareContext)
+ d->glcx->d_ptr->sharing = false;
+ }
+
+ if (deleteOldContext)
+ delete oldcx;
+
+ if (doShow)
+ show();
+}
+
+
+bool QGLWidgetPrivate::renderCxPm(QPixmap*)
+{
+ return false;
+}
+
+void QGLWidgetPrivate::cleanupColormaps()
+{
+ Q_Q(QGLWidget);
+ if (cmap.handle()) {
+ HDC hdc = GetDC(q->winId());
+ SelectPalette(hdc, (HPALETTE) GetStockObject(DEFAULT_PALETTE), FALSE);
+ DeleteObject((HPALETTE) cmap.handle());
+ ReleaseDC(q->winId(), hdc);
+ cmap.setHandle(0);
+ }
+ return;
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ return d_func()->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap & c)
+{
+ Q_D(QGLWidget);
+ d->cmap = c;
+
+ if (d->cmap.handle()) { // already have an allocated cmap
+ d->updateColormap();
+ } else {
+ LOGPALETTE *lpal = (LOGPALETTE *) malloc(sizeof(LOGPALETTE)
+ +c.size()*sizeof(PALETTEENTRY));
+ lpal->palVersion = 0x300;
+ lpal->palNumEntries = c.size();
+ d->cmap.setHandle(CreatePalette(lpal));
+ free(lpal);
+ d->updateColormap();
+ }
+}
+
+void QGLExtensions::init()
+{
+ static bool init_done = false;
+
+ if (init_done)
+ return;
+ init_done = true;
+ init_extensions();
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_x11.cpp b/src/opengl/qgl_x11.cpp
new file mode 100644
index 0000000000..28c34deda2
--- /dev/null
+++ b/src/opengl/qgl_x11.cpp
@@ -0,0 +1,1425 @@
+/****************************************************************************
+**
+** 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 "qgl.h"
+#include "qgl_p.h"
+
+#include "qmap.h"
+#include "qapplication.h"
+#include "qcolormap.h"
+#include "qdesktopwidget.h"
+#include "qpixmap.h"
+#include "qhash.h"
+#include "qlibrary.h"
+#include "qdebug.h"
+#include <private/qfontengine_ft_p.h>
+#include <private/qt_x11_p.h>
+#ifdef Q_OS_HPUX
+// for GLXPBuffer
+#include <private/qglpixelbuffer_p.h>
+#endif
+
+#define INT8 dummy_INT8
+#define INT32 dummy_INT32
+#include <GL/glx.h>
+#undef INT8
+#undef INT32
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#include <X11/Xatom.h>
+
+#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
+#include <dlfcn.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern Drawable qt_x11Handle(const QPaintDevice *pd);
+extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
+
+#ifndef GLX_ARB_multisample
+#define GLX_SAMPLE_BUFFERS_ARB 100000
+#define GLX_SAMPLES_ARB 100001
+#endif
+
+/*
+ The qt_gl_choose_cmap function is internal and used by QGLWidget::setContext()
+ and GLX (not Windows). If the application can't find any sharable
+ colormaps, it must at least create as few colormaps as possible. The
+ dictionary solution below ensures only one colormap is created per visual.
+ Colormaps are also deleted when the application terminates.
+*/
+
+struct QCMapEntry {
+ QCMapEntry();
+ ~QCMapEntry();
+
+ Colormap cmap;
+ bool alloc;
+ XStandardColormap scmap;
+};
+
+QCMapEntry::QCMapEntry()
+{
+ cmap = 0;
+ alloc = false;
+ scmap.colormap = 0;
+}
+
+QCMapEntry::~QCMapEntry()
+{
+ if (alloc)
+ XFreeColormap(X11->display, cmap);
+}
+typedef QHash<int, QCMapEntry *> CMapEntryHash;
+typedef QHash<int, QMap<int, QRgb> > GLCMapHash;
+static bool mesa_gl = false;
+static bool first_time = true;
+
+static void cleanup_cmaps();
+
+struct QGLCMapCleanupHandler {
+ QGLCMapCleanupHandler() {
+ cmap_hash = new CMapEntryHash;
+ qglcmap_hash = new GLCMapHash;
+ }
+ ~QGLCMapCleanupHandler() {
+ delete cmap_hash;
+ delete qglcmap_hash;
+ }
+ CMapEntryHash *cmap_hash;
+ GLCMapHash *qglcmap_hash;
+};
+Q_GLOBAL_STATIC(QGLCMapCleanupHandler, cmap_handler);
+
+static void cleanup_cmaps()
+{
+ CMapEntryHash *hash = cmap_handler()->cmap_hash;
+ QHash<int, QCMapEntry *>::ConstIterator it = hash->constBegin();
+ while (it != hash->constEnd()) {
+ delete it.value();
+ ++it;
+ }
+
+ hash->clear();
+ cmap_handler()->qglcmap_hash->clear();
+}
+
+Colormap qt_gl_choose_cmap(Display *dpy, XVisualInfo *vi)
+{
+ if (first_time) {
+ const char *v = glXQueryServerString(dpy, vi->screen, GLX_VERSION);
+ if (v)
+ mesa_gl = (strstr(v, "Mesa") != 0);
+ first_time = false;
+ }
+
+ CMapEntryHash *hash = cmap_handler()->cmap_hash;
+ CMapEntryHash::ConstIterator it = hash->constFind((long) vi->visualid + (vi->screen * 256));
+ if (it != hash->constEnd())
+ return it.value()->cmap; // found colormap for visual
+
+ if (vi->visualid ==
+ XVisualIDFromVisual((Visual *) QX11Info::appVisual(vi->screen))) {
+ // qDebug("Using x11AppColormap");
+ return QX11Info::appColormap(vi->screen);
+ }
+
+ QCMapEntry *x = new QCMapEntry();
+
+ XStandardColormap *c;
+ int n, i;
+
+ // qDebug("Choosing cmap for vID %0x", vi->visualid);
+
+ if (mesa_gl) { // we're using MesaGL
+ Atom hp_cmaps = XInternAtom(dpy, "_HP_RGB_SMOOTH_MAP_LIST", true);
+ if (hp_cmaps && vi->visual->c_class == TrueColor && vi->depth == 8) {
+ if (XGetRGBColormaps(dpy,RootWindow(dpy,vi->screen),&c,&n,
+ hp_cmaps)) {
+ i = 0;
+ while (i < n && x->cmap == 0) {
+ if (c[i].visualid == vi->visual->visualid) {
+ x->cmap = c[i].colormap;
+ x->scmap = c[i];
+ //qDebug("Using HP_RGB scmap");
+
+ }
+ i++;
+ }
+ XFree((char *)c);
+ }
+ }
+ }
+ if (!x->cmap) {
+ if (XGetRGBColormaps(dpy,RootWindow(dpy,vi->screen),&c,&n,
+ XA_RGB_DEFAULT_MAP)) {
+ for (int i = 0; i < n && x->cmap == 0; ++i) {
+ if (!c[i].red_max ||
+ !c[i].green_max ||
+ !c[i].blue_max ||
+ !c[i].red_mult ||
+ !c[i].green_mult ||
+ !c[i].blue_mult)
+ continue; // invalid stdcmap
+ if (c[i].visualid == vi->visualid) {
+ x->cmap = c[i].colormap;
+ x->scmap = c[i];
+ //qDebug("Using RGB_DEFAULT scmap");
+ }
+ }
+ XFree((char *)c);
+ }
+ }
+ if (!x->cmap) { // no shared cmap found
+ x->cmap = XCreateColormap(dpy, RootWindow(dpy,vi->screen), vi->visual,
+ AllocNone);
+ x->alloc = true;
+ // qDebug("Allocating cmap");
+ }
+
+ // colormap hash should be cleanup only when the QApplication dtor is called
+ if (hash->isEmpty())
+ qAddPostRoutine(cleanup_cmaps);
+
+ // associate cmap with visualid
+ hash->insert((long) vi->visualid + (vi->screen * 256), x);
+ return x->cmap;
+}
+
+struct QTransColor
+{
+ VisualID vis;
+ int screen;
+ long color;
+};
+
+static QVector<QTransColor> trans_colors;
+static int trans_colors_init = false;
+
+static void find_trans_colors()
+{
+ struct OverlayProp {
+ long visual;
+ long type;
+ long value;
+ long layer;
+ };
+
+ trans_colors_init = true;
+
+ Display* appDisplay = X11->display;
+
+ int scr;
+ int lastsize = 0;
+ for (scr = 0; scr < ScreenCount(appDisplay); scr++) {
+ QWidget* rootWin = QApplication::desktop()->screen(scr);
+ if (!rootWin)
+ return; // Should not happen
+ Atom overlayVisualsAtom = XInternAtom(appDisplay,
+ "SERVER_OVERLAY_VISUALS", True);
+ if (overlayVisualsAtom == XNone)
+ return; // Server has no overlays
+
+ Atom actualType;
+ int actualFormat;
+ ulong nItems;
+ ulong bytesAfter;
+ unsigned char *retval = 0;
+ int res = XGetWindowProperty(appDisplay, rootWin->winId(),
+ overlayVisualsAtom, 0, 10000, False,
+ overlayVisualsAtom, &actualType,
+ &actualFormat, &nItems, &bytesAfter,
+ &retval);
+
+ if (res != Success || actualType != overlayVisualsAtom
+ || actualFormat != 32 || nItems < 4 || !retval)
+ return; // Error reading property
+
+ OverlayProp *overlayProps = (OverlayProp *)retval;
+
+ int numProps = nItems / 4;
+ trans_colors.resize(lastsize + numProps);
+ int j = lastsize;
+ for (int i = 0; i < numProps; i++) {
+ if (overlayProps[i].type == 1) {
+ trans_colors[j].vis = (VisualID)overlayProps[i].visual;
+ trans_colors[j].screen = scr;
+ trans_colors[j].color = (int)overlayProps[i].value;
+ j++;
+ }
+ }
+ XFree(overlayProps);
+ lastsize = j;
+ trans_colors.resize(lastsize);
+ }
+}
+
+/*****************************************************************************
+ QGLFormat UNIX/GLX-specific code
+ *****************************************************************************/
+
+bool QGLFormat::hasOpenGL()
+{
+ return glXQueryExtension(X11->display, 0, 0) != 0;
+}
+
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ if (!trans_colors_init)
+ find_trans_colors();
+ return trans_colors.size() > 0;
+}
+
+/*****************************************************************************
+ QGLContext UNIX/GLX-specific code
+ *****************************************************************************/
+
+bool QGLContext::chooseContext(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+ const QX11Info *xinfo = qt_x11Info(d->paintDevice);
+
+ Display* disp = xinfo->display();
+ d->vi = chooseVisual();
+ if (!d->vi)
+ return false;
+
+ if (deviceIsPixmap() &&
+ (((XVisualInfo*)d->vi)->depth != xinfo->depth() ||
+ ((XVisualInfo*)d->vi)->screen != xinfo->screen()))
+ {
+ XFree(d->vi);
+ XVisualInfo appVisInfo;
+ memset(&appVisInfo, 0, sizeof(XVisualInfo));
+ appVisInfo.visualid = XVisualIDFromVisual((Visual *) xinfo->visual());
+ appVisInfo.screen = xinfo->screen();
+ int nvis;
+ d->vi = XGetVisualInfo(disp, VisualIDMask | VisualScreenMask, &appVisInfo, &nvis);
+ if (!d->vi)
+ return false;
+
+ int useGL;
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_USE_GL, &useGL);
+ if (!useGL)
+ return false; //# Chickening out already...
+ }
+ int res;
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_LEVEL, &res);
+ d->glFormat.setPlane(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_DOUBLEBUFFER, &res);
+ d->glFormat.setDoubleBuffer(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_DEPTH_SIZE, &res);
+ d->glFormat.setDepth(res);
+ if (d->glFormat.depth())
+ d->glFormat.setDepthBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_RGBA, &res);
+ d->glFormat.setRgba(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_RED_SIZE, &res);
+ d->glFormat.setRedBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_GREEN_SIZE, &res);
+ d->glFormat.setGreenBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_BLUE_SIZE, &res);
+ d->glFormat.setBlueBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_ALPHA_SIZE, &res);
+ d->glFormat.setAlpha(res);
+ if (d->glFormat.alpha())
+ d->glFormat.setAlphaBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_ACCUM_RED_SIZE, &res);
+ d->glFormat.setAccum(res);
+ if (d->glFormat.accum())
+ d->glFormat.setAccumBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_STENCIL_SIZE, &res);
+ d->glFormat.setStencil(res);
+ if (d->glFormat.stencil())
+ d->glFormat.setStencilBufferSize(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_STEREO, &res);
+ d->glFormat.setStereo(res);
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_SAMPLE_BUFFERS_ARB, &res);
+ d->glFormat.setSampleBuffers(res);
+ if (d->glFormat.sampleBuffers()) {
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_SAMPLES_ARB, &res);
+ d->glFormat.setSamples(res);
+ }
+
+ Bool direct = format().directRendering() ? True : False;
+
+ if (shareContext &&
+ (!shareContext->isValid() || !shareContext->d_func()->cx)) {
+ qWarning("QGLContext::chooseContext(): Cannot share with invalid context");
+ shareContext = 0;
+ }
+
+ // 1. Sharing between rgba and color-index will give wrong colors.
+ // 2. Contexts cannot be shared btw. direct/non-direct renderers.
+ // 3. Pixmaps cannot share contexts that are set up for direct rendering.
+ // 4. If the contexts are not created on the same screen, they can't be shared
+
+ if (shareContext
+ && (format().rgba() != shareContext->format().rgba()
+ || (deviceIsPixmap() && glXIsDirect(disp, (GLXContext)shareContext->d_func()->cx))
+ || (shareContext->d_func()->screen != xinfo->screen())))
+ {
+ shareContext = 0;
+ }
+
+ d->cx = 0;
+ if (shareContext) {
+ d->cx = glXCreateContext(disp, (XVisualInfo *)d->vi,
+ (GLXContext)shareContext->d_func()->cx, direct);
+ d->screen = ((XVisualInfo*)d->vi)->screen;
+ if (d->cx) {
+ QGLContext *share = const_cast<QGLContext *>(shareContext);
+ d->sharing = true;
+ share->d_func()->sharing = true;
+ }
+ }
+ if (!d->cx) {
+ d->cx = glXCreateContext(disp, (XVisualInfo *)d->vi, NULL, direct);
+ d->screen = ((XVisualInfo*)d->vi)->screen;
+ }
+ if (!d->cx)
+ return false;
+ d->glFormat.setDirectRendering(glXIsDirect(disp, (GLXContext)d->cx));
+ if (deviceIsPixmap()) {
+#if defined(GLX_MESA_pixmap_colormap) && defined(QGL_USE_MESA_EXT)
+ d->gpm = glXCreateGLXPixmapMESA(disp, (XVisualInfo *)d->vi,
+ qt_x11Handle(d->paintDevice),
+ qt_gl_choose_cmap(disp, (XVisualInfo *)d->vi));
+#else
+ d->gpm = (quint32)glXCreateGLXPixmap(disp, (XVisualInfo *)d->vi,
+ qt_x11Handle(d->paintDevice));
+#endif
+ if (!d->gpm)
+ return false;
+ }
+ QString glxExt = QString(QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS)));
+ if (glxExt.contains(QLatin1String("GLX_SGI_video_sync"))) {
+ if (d->glFormat.swapInterval() == -1)
+ d->glFormat.setSwapInterval(0);
+ } else {
+ d->glFormat.setSwapInterval(-1);
+ }
+ return true;
+}
+
+
+/*!
+ \bold{X11 only:} This virtual function tries to find a
+ visual that matches the format, reducing the demands if the original
+ request cannot be met.
+
+ The algorithm for reducing the demands of the format is quite
+ simple-minded, so override this method in your subclass if your
+ application has spcific requirements on visual selection.
+
+ \sa chooseContext()
+*/
+
+void *QGLContext::chooseVisual()
+{
+ Q_D(QGLContext);
+ static const int bufDepths[] = { 8, 4, 2, 1 }; // Try 16, 12 also?
+ //todo: if pixmap, also make sure that vi->depth == pixmap->depth
+ void* vis = 0;
+ int i = 0;
+ bool fail = false;
+ QGLFormat fmt = format();
+ bool tryDouble = !fmt.doubleBuffer(); // Some GL impl's only have double
+ bool triedDouble = false;
+ bool triedSample = false;
+ if (fmt.sampleBuffers())
+ fmt.setSampleBuffers(QGLExtensions::glExtensions & QGLExtensions::SampleBuffers);
+ while(!fail && !(vis = tryVisual(fmt, bufDepths[i]))) {
+ if (!fmt.rgba() && bufDepths[i] > 1) {
+ i++;
+ continue;
+ }
+ if (tryDouble) {
+ fmt.setDoubleBuffer(true);
+ tryDouble = false;
+ triedDouble = true;
+ continue;
+ } else if (triedDouble) {
+ fmt.setDoubleBuffer(false);
+ triedDouble = false;
+ }
+ if (!triedSample && fmt.sampleBuffers()) {
+ fmt.setSampleBuffers(false);
+ triedSample = true;
+ continue;
+ }
+ if (fmt.stereo()) {
+ fmt.setStereo(false);
+ continue;
+ }
+ if (fmt.accum()) {
+ fmt.setAccum(false);
+ continue;
+ }
+ if (fmt.stencil()) {
+ fmt.setStencil(false);
+ continue;
+ }
+ if (fmt.alpha()) {
+ fmt.setAlpha(false);
+ continue;
+ }
+ if (fmt.depth()) {
+ fmt.setDepth(false);
+ continue;
+ }
+ if (fmt.doubleBuffer()) {
+ fmt.setDoubleBuffer(false);
+ continue;
+ }
+ fail = true;
+ }
+ d->glFormat = fmt;
+ return vis;
+}
+
+
+/*!
+ \internal
+
+ \bold{X11 only:} This virtual function chooses a visual
+ that matches the OpenGL \link format() format\endlink. Reimplement this
+ function in a subclass if you need a custom visual.
+
+ \sa chooseContext()
+*/
+
+void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth)
+{
+ Q_D(QGLContext);
+ int spec[40];
+ int i = 0;
+ spec[i++] = GLX_LEVEL;
+ spec[i++] = f.plane();
+ const QX11Info *xinfo = qt_x11Info(d->paintDevice);
+
+#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_info)
+ static bool useTranspExt = false;
+ static bool useTranspExtChecked = false;
+ if (f.plane() && !useTranspExtChecked && d->paintDevice) {
+ QByteArray estr(glXQueryExtensionsString(xinfo->display(), xinfo->screen()));
+ useTranspExt = estr.contains("GLX_EXT_visual_info");
+ //# (A bit simplistic; that could theoretically be a substring)
+ if (useTranspExt) {
+ QByteArray cstr(glXGetClientString(xinfo->display(), GLX_VENDOR));
+ useTranspExt = !cstr.contains("Xi Graphics"); // bug workaround
+ if (useTranspExt) {
+ // bug workaround - some systems (eg. FireGL) refuses to return an overlay
+ // visual if the GLX_TRANSPARENT_TYPE_EXT attribute is specified, even if
+ // the implementation supports transparent overlays
+ int tmpSpec[] = { GLX_LEVEL, f.plane(), GLX_TRANSPARENT_TYPE_EXT,
+ f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT,
+ XNone };
+ XVisualInfo * vinf = glXChooseVisual(xinfo->display(), xinfo->screen(), tmpSpec);
+ if (!vinf) {
+ useTranspExt = false;
+ }
+ }
+ }
+
+ useTranspExtChecked = true;
+ }
+ if (f.plane() && useTranspExt) {
+ // Required to avoid non-transparent overlay visual(!) on some systems
+ spec[i++] = GLX_TRANSPARENT_TYPE_EXT;
+ spec[i++] = f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT;
+ }
+#endif
+
+ if (f.doubleBuffer())
+ spec[i++] = GLX_DOUBLEBUFFER;
+ if (f.depth()) {
+ spec[i++] = GLX_DEPTH_SIZE;
+ spec[i++] = f.depthBufferSize() == -1 ? 1 : f.depthBufferSize();
+ }
+ if (f.stereo()) {
+ spec[i++] = GLX_STEREO;
+ }
+ if (f.stencil()) {
+ spec[i++] = GLX_STENCIL_SIZE;
+ spec[i++] = f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize();
+ }
+ if (f.rgba()) {
+ spec[i++] = GLX_RGBA;
+ spec[i++] = GLX_RED_SIZE;
+ spec[i++] = f.redBufferSize() == -1 ? 1 : f.redBufferSize();
+ spec[i++] = GLX_GREEN_SIZE;
+ spec[i++] = f.greenBufferSize() == -1 ? 1 : f.greenBufferSize();
+ spec[i++] = GLX_BLUE_SIZE;
+ spec[i++] = f.blueBufferSize() == -1 ? 1 : f.blueBufferSize();
+ if (f.alpha()) {
+ spec[i++] = GLX_ALPHA_SIZE;
+ spec[i++] = f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize();
+ }
+ if (f.accum()) {
+ spec[i++] = GLX_ACCUM_RED_SIZE;
+ spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ spec[i++] = GLX_ACCUM_GREEN_SIZE;
+ spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ spec[i++] = GLX_ACCUM_BLUE_SIZE;
+ spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ if (f.alpha()) {
+ spec[i++] = GLX_ACCUM_ALPHA_SIZE;
+ spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ }
+ }
+ } else {
+ spec[i++] = GLX_BUFFER_SIZE;
+ spec[i++] = bufDepth;
+ }
+
+ if (f.sampleBuffers()) {
+ spec[i++] = GLX_SAMPLE_BUFFERS_ARB;
+ spec[i++] = 1;
+ spec[i++] = GLX_SAMPLES_ARB;
+ spec[i++] = f.samples() == -1 ? 4 : f.samples();
+ }
+
+ spec[i] = XNone;
+ return glXChooseVisual(xinfo->display(), xinfo->screen(), spec);
+}
+
+
+void QGLContext::reset()
+{
+ Q_D(QGLContext);
+ if (!d->valid)
+ return;
+ d->cleanup();
+ const QX11Info *xinfo = qt_x11Info(d->paintDevice);
+ doneCurrent();
+ if (d->gpm)
+ glXDestroyGLXPixmap(xinfo->display(), (GLXPixmap)d->gpm);
+ d->gpm = 0;
+ glXDestroyContext(xinfo->display(), (GLXContext)d->cx);
+ if (d->vi)
+ XFree(d->vi);
+ d->vi = 0;
+ d->cx = 0;
+ d->crWin = false;
+ d->sharing = false;
+ d->valid = false;
+ d->transpColor = QColor();
+ d->initDone = false;
+ qgl_share_reg()->removeShare(this);
+}
+
+
+void QGLContext::makeCurrent()
+{
+ Q_D(QGLContext);
+ if (!d->valid) {
+ qWarning("QGLContext::makeCurrent(): Cannot make invalid context current.");
+ return;
+ }
+ const QX11Info *xinfo = qt_x11Info(d->paintDevice);
+ bool ok = true;
+ if (d->paintDevice->devType() == QInternal::Pixmap) {
+ ok = glXMakeCurrent(xinfo->display(), (GLXPixmap)d->gpm, (GLXContext)d->cx);
+ } else if (d->paintDevice->devType() == QInternal::Pbuffer) {
+ ok = glXMakeCurrent(xinfo->display(), (GLXPbuffer)d->pbuf, (GLXContext)d->cx);
+ } else if (d->paintDevice->devType() == QInternal::Widget) {
+ ok = glXMakeCurrent(xinfo->display(), ((QWidget *)d->paintDevice)->winId(), (GLXContext)d->cx);
+ }
+ if (!ok)
+ qWarning("QGLContext::makeCurrent(): Failed.");
+
+ if (ok) {
+ if (!qgl_context_storage.hasLocalData() && QThread::currentThread())
+ qgl_context_storage.setLocalData(new QGLThreadContext);
+ if (qgl_context_storage.hasLocalData())
+ qgl_context_storage.localData()->context = this;
+ currentCtx = this;
+ }
+}
+
+void QGLContext::doneCurrent()
+{
+ Q_D(QGLContext);
+ glXMakeCurrent(qt_x11Info(d->paintDevice)->display(), 0, 0);
+ if (qgl_context_storage.hasLocalData())
+ qgl_context_storage.localData()->context = 0;
+ currentCtx = 0;
+}
+
+
+void QGLContext::swapBuffers() const
+{
+ Q_D(const QGLContext);
+ if (!d->valid)
+ return;
+ if (!deviceIsPixmap()) {
+ int interval = d->glFormat.swapInterval();
+ if (interval > 0) {
+ typedef int (*qt_glXGetVideoSyncSGI)(uint *);
+ typedef int (*qt_glXWaitVideoSyncSGI)(int, int, uint *);
+ static qt_glXGetVideoSyncSGI glXGetVideoSyncSGI = 0;
+ static qt_glXWaitVideoSyncSGI glXWaitVideoSyncSGI = 0;
+ static bool resolved = false;
+ if (!resolved) {
+ QString glxExt = QString(QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS)));
+ if (glxExt.contains(QLatin1String("GLX_SGI_video_sync"))) {
+#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
+ void *handle = dlopen(NULL, RTLD_LAZY);
+ if (handle) {
+ glXGetVideoSyncSGI = (qt_glXGetVideoSyncSGI) dlsym(handle, "glXGetVideoSyncSGI");
+ glXWaitVideoSyncSGI = (qt_glXWaitVideoSyncSGI) dlsym(handle, "glXWaitVideoSyncSGI");
+ dlclose(handle);
+ }
+ if (!glXGetVideoSyncSGI)
+#endif
+ {
+ extern const QString qt_gl_library_name();
+ QLibrary lib(qt_gl_library_name());
+ glXGetVideoSyncSGI = (qt_glXGetVideoSyncSGI) lib.resolve("glXGetVideoSyncSGI");
+ glXWaitVideoSyncSGI = (qt_glXWaitVideoSyncSGI) lib.resolve("glXWaitVideoSyncSGI");
+ }
+ }
+ resolved = true;
+ }
+ if (glXGetVideoSyncSGI && glXWaitVideoSyncSGI) {
+ uint counter;
+ if (!glXGetVideoSyncSGI(&counter))
+ glXWaitVideoSyncSGI(interval + 1, (counter + interval) % (interval + 1), &counter);
+ }
+ }
+ glXSwapBuffers(qt_x11Info(d->paintDevice)->display(),
+ static_cast<QWidget *>(d->paintDevice)->winId());
+ }
+}
+
+QColor QGLContext::overlayTransparentColor() const
+{
+ if (isValid())
+ return Qt::transparent;
+ return QColor(); // Invalid color
+}
+
+static uint qt_transparent_pixel(VisualID id, int screen)
+{
+ for (int i = 0; i < trans_colors.size(); i++) {
+ if (trans_colors[i].vis == id && trans_colors[i].screen == screen)
+ return trans_colors[i].color;
+ }
+ return 0;
+}
+
+uint QGLContext::colorIndex(const QColor& c) const
+{
+ Q_D(const QGLContext);
+ int screen = ((XVisualInfo *)d->vi)->screen;
+ QColormap colmap = QColormap::instance(screen);
+ if (isValid()) {
+ if (format().plane() && c == Qt::transparent) {
+ return qt_transparent_pixel(((XVisualInfo *)d->vi)->visualid,
+ ((XVisualInfo *)d->vi)->screen);
+ }
+ if (((XVisualInfo*)d->vi)->visualid ==
+ XVisualIDFromVisual((Visual *) QX11Info::appVisual(screen)))
+ return colmap.pixel(c); // We're using QColor's cmap
+
+ XVisualInfo *info = (XVisualInfo *) d->vi;
+ CMapEntryHash *hash = cmap_handler()->cmap_hash;
+ CMapEntryHash::ConstIterator it = hash->constFind(long(info->visualid)
+ + (info->screen * 256));
+ QCMapEntry *x = 0;
+ if (it != hash->constEnd())
+ x = it.value();
+ if (x && !x->alloc) { // It's a standard colormap
+ int rf = (int)(((float)c.red() * (x->scmap.red_max+1))/256.0);
+ int gf = (int)(((float)c.green() * (x->scmap.green_max+1))/256.0);
+ int bf = (int)(((float)c.blue() * (x->scmap.blue_max+1))/256.0);
+ uint p = x->scmap.base_pixel
+ + (rf * x->scmap.red_mult)
+ + (gf * x->scmap.green_mult)
+ + (bf * x->scmap.blue_mult);
+ return p;
+ } else {
+ QMap<int, QRgb> &cmap = (*cmap_handler()->qglcmap_hash)[(long)info->visualid];
+
+ // already in the map?
+ QRgb target = c.rgb();
+ QMap<int, QRgb>::Iterator it = cmap.begin();
+ for (; it != cmap.end(); ++it) {
+ if ((*it) == target)
+ return it.key();
+ }
+
+ // need to alloc color
+ unsigned long plane_mask[2];
+ unsigned long color_map_entry;
+ if (!XAllocColorCells (QX11Info::display(), x->cmap, true, plane_mask, 0,
+ &color_map_entry, 1))
+ return colmap.pixel(c);
+
+ XColor col;
+ col.flags = DoRed | DoGreen | DoBlue;
+ col.pixel = color_map_entry;
+ col.red = (ushort)((qRed(c.rgb()) / 255.0) * 65535.0 + 0.5);
+ col.green = (ushort)((qGreen(c.rgb()) / 255.0) * 65535.0 + 0.5);
+ col.blue = (ushort)((qBlue(c.rgb()) / 255.0) * 65535.0 + 0.5);
+ XStoreColor(QX11Info::display(), x->cmap, &col);
+
+ cmap.insert(color_map_entry, target);
+ return color_map_entry;
+ }
+ }
+ return 0;
+}
+
+#ifndef QT_NO_FONTCONFIG
+/*! \internal
+ This is basically a substitute for glxUseXFont() which can only
+ handle XLFD fonts. This version relies on freetype to render the
+ glyphs, but it works with all fonts that fontconfig provides - both
+ antialiased and aliased bitmap and outline fonts.
+*/
+static void qgl_use_font(QFontEngineFT *engine, int first, int count, int listBase)
+{
+ GLfloat color[4];
+ glGetFloatv(GL_CURRENT_COLOR, color);
+
+ // save the pixel unpack state
+ GLint gl_swapbytes, gl_lsbfirst, gl_rowlength, gl_skiprows, gl_skippixels, gl_alignment;
+ glGetIntegerv (GL_UNPACK_SWAP_BYTES, &gl_swapbytes);
+ glGetIntegerv (GL_UNPACK_LSB_FIRST, &gl_lsbfirst);
+ glGetIntegerv (GL_UNPACK_ROW_LENGTH, &gl_rowlength);
+ glGetIntegerv (GL_UNPACK_SKIP_ROWS, &gl_skiprows);
+ glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &gl_skippixels);
+ glGetIntegerv (GL_UNPACK_ALIGNMENT, &gl_alignment);
+
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ const bool antialiased = engine->drawAntialiased();
+ FT_Face face = engine->lockFace();
+
+ // start generating font glyphs
+ for (int i = first; i < count; ++i) {
+ int list = listBase + i;
+ GLfloat x0, y0, dx, dy;
+
+ FT_Error err;
+
+ err = FT_Load_Glyph(face, FT_Get_Char_Index(face, i), FT_LOAD_DEFAULT);
+ if (err) {
+ qDebug("failed loading glyph %d from font", i);
+ Q_ASSERT(!err);
+ }
+ err = FT_Render_Glyph(face->glyph, (antialiased ? FT_RENDER_MODE_NORMAL
+ : FT_RENDER_MODE_MONO));
+ if (err) {
+ qDebug("failed rendering glyph %d from font", i);
+ Q_ASSERT(!err);
+ }
+
+ FT_Bitmap bm = face->glyph->bitmap;
+ x0 = face->glyph->metrics.horiBearingX >> 6;
+ y0 = (face->glyph->metrics.height - face->glyph->metrics.horiBearingY) >> 6;
+ dx = face->glyph->metrics.horiAdvance >> 6;
+ dy = 0;
+ int sz = bm.pitch * bm.rows;
+ uint *aa_glyph = 0;
+ uchar *ua_glyph = 0;
+
+ if (antialiased)
+ aa_glyph = new uint[sz];
+ else
+ ua_glyph = new uchar[sz];
+
+ // convert to GL format
+ for (int y = 0; y < bm.rows; ++y) {
+ for (int x = 0; x < bm.pitch; ++x) {
+ int c1 = y*bm.pitch + x;
+ int c2 = (bm.rows - y - 1) > 0 ? (bm.rows-y-1)*bm.pitch + x : x;
+ if (antialiased) {
+ aa_glyph[c1] = (int(color[0]*255) << 24)
+ | (int(color[1]*255) << 16)
+ | (int(color[2]*255) << 8) | bm.buffer[c2];
+ } else {
+ ua_glyph[c1] = bm.buffer[c2];
+ }
+ }
+ }
+
+ glNewList(list, GL_COMPILE);
+ if (antialiased) {
+ // calling glBitmap() is just a trick to move the current
+ // raster pos, since glGet*() won't work in display lists
+ glBitmap(0, 0, 0, 0, x0, -y0, 0);
+ glDrawPixels(bm.pitch, bm.rows, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, aa_glyph);
+ glBitmap(0, 0, 0, 0, dx-x0, y0, 0);
+ } else {
+ glBitmap(bm.pitch*8, bm.rows, -x0, y0, dx, dy, ua_glyph);
+ }
+ glEndList();
+ antialiased ? delete[] aa_glyph : delete[] ua_glyph;
+ }
+
+ engine->unlockFace();
+
+ // restore pixel unpack settings
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, gl_swapbytes);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, gl_lsbfirst);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, gl_rowlength);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS, gl_skiprows);
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, gl_skippixels);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, gl_alignment);
+}
+#endif
+
+#undef d
+void QGLContext::generateFontDisplayLists(const QFont & fnt, int listBase)
+{
+ QFont f(fnt);
+ QFontEngine *engine = f.d->engineForScript(QUnicodeTables::Common);
+
+ if (engine->type() == QFontEngine::Multi)
+ engine = static_cast<QFontEngineMulti *>(engine)->engine(0);
+#ifndef QT_NO_FONTCONFIG
+ if(engine->type() == QFontEngine::Freetype) {
+ qgl_use_font(static_cast<QFontEngineFT *>(engine), 0, 256, listBase);
+ return;
+ }
+#endif
+ // glXUseXFont() only works with XLFD font structures and a few GL
+ // drivers crash if 0 is passed as the font handle
+ f.setStyleStrategy(QFont::OpenGLCompatible);
+ if (f.handle() && engine->type() == QFontEngine::XLFD)
+ glXUseXFont(static_cast<Font>(f.handle()), 0, 256, listBase);
+}
+
+void *QGLContext::getProcAddress(const QString &proc) const
+{
+ typedef void *(*qt_glXGetProcAddressARB)(const GLubyte *);
+ static qt_glXGetProcAddressARB glXGetProcAddressARB = 0;
+ static bool resolved = false;
+
+ if (resolved && !glXGetProcAddressARB)
+ return 0;
+ if (!glXGetProcAddressARB) {
+ QString glxExt = QString(QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS)));
+ if (glxExt.contains(QLatin1String("GLX_ARB_get_proc_address"))) {
+#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
+ void *handle = dlopen(NULL, RTLD_LAZY);
+ if (handle) {
+ glXGetProcAddressARB = (qt_glXGetProcAddressARB) dlsym(handle, "glXGetProcAddressARB");
+ dlclose(handle);
+ }
+ if (!glXGetProcAddressARB)
+#endif
+ {
+ extern const QString qt_gl_library_name();
+ QLibrary lib(qt_gl_library_name());
+ glXGetProcAddressARB = (qt_glXGetProcAddressARB) lib.resolve("glXGetProcAddressARB");
+ }
+ }
+ resolved = true;
+ }
+ if (!glXGetProcAddressARB)
+ return 0;
+ return glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(proc.toLatin1().data()));
+}
+
+/*****************************************************************************
+ QGLOverlayWidget (Internal overlay class for X11)
+ *****************************************************************************/
+
+class QGLOverlayWidget : public QGLWidget
+{
+ Q_OBJECT
+public:
+ QGLOverlayWidget(const QGLFormat& format, QGLWidget* parent, const QGLWidget* shareWidget=0);
+
+protected:
+ void initializeGL();
+ void paintGL();
+ void resizeGL(int w, int h);
+ bool x11Event(XEvent *e) { return realWidget->x11Event(e); }
+
+private:
+ QGLWidget* realWidget;
+
+private:
+ Q_DISABLE_COPY(QGLOverlayWidget)
+};
+
+
+QGLOverlayWidget::QGLOverlayWidget(const QGLFormat& format, QGLWidget* parent,
+ const QGLWidget* shareWidget)
+ : QGLWidget(format, parent, shareWidget ? shareWidget->d_func()->olw : 0)
+{
+ setAttribute(Qt::WA_X11OpenGLOverlay);
+ realWidget = parent;
+}
+
+
+
+void QGLOverlayWidget::initializeGL()
+{
+ QColor transparentColor = context()->overlayTransparentColor();
+ if (transparentColor.isValid())
+ qglClearColor(transparentColor);
+ else
+ qWarning("QGLOverlayWidget::initializeGL(): Could not get transparent color");
+ realWidget->initializeOverlayGL();
+}
+
+
+void QGLOverlayWidget::resizeGL(int w, int h)
+{
+ glViewport(0, 0, w, h);
+ realWidget->resizeOverlayGL(w, h);
+}
+
+
+void QGLOverlayWidget::paintGL()
+{
+ realWidget->paintOverlayGL();
+}
+
+#undef Bool
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qgl_x11.moc"
+QT_END_INCLUDE_NAMESPACE
+
+/*****************************************************************************
+ QGLWidget UNIX/GLX-specific code
+ *****************************************************************************/
+void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget *shareWidget)
+{
+ Q_Q(QGLWidget);
+ initContext(context, shareWidget);
+ olw = 0;
+
+ if (q->isValid() && context->format().hasOverlay()) {
+ QString olwName = q->objectName();
+ olwName += QLatin1String("-QGL_internal_overlay_widget");
+ olw = new QGLOverlayWidget(QGLFormat::defaultOverlayFormat(), q, shareWidget);
+ olw->setObjectName(olwName);
+ if (olw->isValid()) {
+ olw->setAutoBufferSwap(false);
+ olw->setFocusProxy(q);
+ }
+ else {
+ delete olw;
+ olw = 0;
+ glcx->d_func()->glFormat.setOverlay(false);
+ }
+ }
+}
+
+bool QGLWidgetPrivate::renderCxPm(QPixmap* pm)
+{
+ Q_Q(QGLWidget);
+ if (((XVisualInfo*)glcx->d_func()->vi)->depth != pm->depth())
+ return false;
+
+ GLXPixmap glPm;
+#if defined(GLX_MESA_pixmap_colormap) && defined(QGL_USE_MESA_EXT)
+ glPm = glXCreateGLXPixmapMESA(X11->display,
+ (XVisualInfo*)glcx->vi,
+ (Pixmap)pm->handle(),
+ qt_gl_choose_cmap(pm->X11->display,
+ (XVisualInfo*)glcx->vi));
+#else
+ glPm = (quint32)glXCreateGLXPixmap(X11->display,
+ (XVisualInfo*)glcx->d_func()->vi,
+ (Pixmap)pm->handle());
+#endif
+
+ if (!glXMakeCurrent(X11->display, glPm, (GLXContext)glcx->d_func()->cx)) {
+ glXDestroyGLXPixmap(X11->display, glPm);
+ return false;
+ }
+
+ glDrawBuffer(GL_FRONT);
+ if (!glcx->initialized())
+ q->glInit();
+ q->resizeGL(pm->width(), pm->height());
+ q->paintGL();
+ glFlush();
+ q->makeCurrent();
+ glXDestroyGLXPixmap(X11->display, glPm);
+ q->resizeGL(q->width(), q->height());
+ return true;
+}
+
+/*! \internal
+ Free up any allocated colormaps. This fn is only called for
+ top-level widgets.
+*/
+void QGLWidgetPrivate::cleanupColormaps()
+{
+ if (!cmap.handle()) {
+ return;
+ } else {
+ XFreeColormap(X11->display, (Colormap) cmap.handle());
+ cmap.setHandle(0);
+ }
+}
+
+void QGLWidget::setMouseTracking(bool enable)
+{
+ Q_D(QGLWidget);
+ if (d->olw)
+ d->olw->setMouseTracking(enable);
+ QWidget::setMouseTracking(enable);
+}
+
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ glXWaitX();
+ resizeGL(width(), height());
+ if (d->olw)
+ d->olw->setGeometry(rect());
+}
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ Q_D(const QGLWidget);
+ if (d->olw)
+ return d->olw->context();
+ else
+ return 0;
+}
+
+
+void QGLWidget::makeOverlayCurrent()
+{
+ Q_D(QGLWidget);
+ if (d->olw)
+ d->olw->makeCurrent();
+}
+
+
+void QGLWidget::updateOverlayGL()
+{
+ Q_D(QGLWidget);
+ if (d->olw)
+ d->olw->updateGL();
+}
+
+/*!
+ \internal
+
+ Sets a new QGLContext, \a context, for this QGLWidget, using the
+ shared context, \a shareContext. If \a deleteOldContext is true,
+ the original context is deleted; otherwise it is overridden.
+*/
+void QGLWidget::setContext(QGLContext *context,
+ const QGLContext* shareContext,
+ bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+ if (!context->deviceIsPixmap() && context->device() != this) {
+ qWarning("QGLWidget::setContext: Context must refer to this widget");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+
+
+ if (parentWidget()) {
+ // force creation of delay-created widgets
+ parentWidget()->winId();
+ if (parentWidget()->x11Info().screen() != x11Info().screen())
+ d_func()->xinfo = parentWidget()->d_func()->xinfo;
+ }
+
+ bool createFailed = false;
+ if (!d->glcx->isValid()) {
+ if (!d->glcx->create(shareContext ? shareContext : oldcx))
+ createFailed = true;
+ }
+ if (createFailed) {
+ if (deleteOldContext)
+ delete oldcx;
+ return;
+ }
+
+ if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) {
+ if (deleteOldContext)
+ delete oldcx;
+ return;
+ }
+
+ bool visible = isVisible();
+ if (visible)
+ hide();
+
+ XVisualInfo *vi = (XVisualInfo*)d->glcx->d_func()->vi;
+ XSetWindowAttributes a;
+
+ QColormap colmap = QColormap::instance(vi->screen);
+ a.colormap = qt_gl_choose_cmap(QX11Info::display(), vi); // find best colormap
+ a.background_pixel = colmap.pixel(palette().color(backgroundRole()));
+ a.border_pixel = colmap.pixel(Qt::black);
+ Window p = RootWindow(X11->display, vi->screen);
+ if (parentWidget())
+ p = parentWidget()->winId();
+
+ Window w = XCreateWindow(X11->display, p, x(), y(), width(), height(),
+ 0, vi->depth, InputOutput, vi->visual,
+ CWBackPixel|CWBorderPixel|CWColormap, &a);
+ Window *cmw;
+ Window *cmwret;
+ int count;
+ if (XGetWMColormapWindows(X11->display, window()->winId(),
+ &cmwret, &count)) {
+ cmw = new Window[count+1];
+ memcpy((char *)cmw, (char *)cmwret, sizeof(Window)*count);
+ XFree((char *)cmwret);
+ int i;
+ for (i=0; i<count; i++) {
+ if (cmw[i] == winId()) { // replace old window
+ cmw[i] = w;
+ break;
+ }
+ }
+ if (i >= count) // append new window
+ cmw[count++] = w;
+ } else {
+ count = 1;
+ cmw = new Window[count];
+ cmw[0] = w;
+ }
+
+#if defined(GLX_MESA_release_buffers) && defined(QGL_USE_MESA_EXT)
+ if (oldcx && oldcx->windowCreated())
+ glXReleaseBuffersMESA(X11->display, winId());
+#endif
+ if (deleteOldContext)
+ delete oldcx;
+ oldcx = 0;
+
+ if (testAttribute(Qt::WA_WState_Created))
+ create(w);
+ else
+ d->createWinId(w);
+ XSetWMColormapWindows(X11->display, window()->winId(), cmw, count);
+ delete [] cmw;
+
+ // calling QWidget::create() will always result in a new paint
+ // engine being created - get rid of it and replace it with our
+ // own
+
+ if (visible)
+ show();
+ XFlush(X11->display);
+ d->glcx->setWindowCreated(true);
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ Q_D(const QGLWidget);
+ return d->cmap;
+}
+
+/*\internal
+ Store color values in the given colormap.
+*/
+static void qStoreColors(QWidget * tlw, Colormap cmap,
+ const QGLColormap & cols)
+{
+ Q_UNUSED(tlw);
+ XColor c;
+ QRgb color;
+
+ for (int i = 0; i < cols.size(); i++) {
+ color = cols.entryRgb(i);
+ c.pixel = i;
+ c.red = (ushort)((qRed(color) / 255.0) * 65535.0 + 0.5);
+ c.green = (ushort)((qGreen(color) / 255.0) * 65535.0 + 0.5);
+ c.blue = (ushort)((qBlue(color) / 255.0) * 65535.0 + 0.5);
+ c.flags = DoRed | DoGreen | DoBlue;
+ XStoreColor(X11->display, cmap, &c);
+ }
+}
+
+/*\internal
+ Check whether the given visual supports dynamic colormaps or not.
+*/
+static bool qCanAllocColors(QWidget * w)
+{
+ bool validVisual = false;
+ int numVisuals;
+ long mask;
+ XVisualInfo templ;
+ XVisualInfo * visuals;
+ VisualID id = XVisualIDFromVisual((Visual *) w->window()->x11Info().visual());
+
+ mask = VisualScreenMask;
+ templ.screen = w->x11Info().screen();
+ visuals = XGetVisualInfo(X11->display, mask, &templ, &numVisuals);
+
+ for (int i = 0; i < numVisuals; i++) {
+ if (visuals[i].visualid == id) {
+ switch (visuals[i].c_class) {
+ case TrueColor:
+ case StaticColor:
+ case StaticGray:
+ case XGrayScale:
+ validVisual = false;
+ break;
+ case DirectColor:
+ case PseudoColor:
+ validVisual = true;
+ break;
+ }
+ break;
+ }
+ }
+ XFree(visuals);
+
+ if (!validVisual)
+ return false;
+ return true;
+}
+
+
+void QGLWidget::setColormap(const QGLColormap & c)
+{
+ Q_D(QGLWidget);
+ QWidget * tlw = window(); // must return a valid widget
+
+ d->cmap = c;
+ if (!d->cmap.handle())
+ return;
+
+ if (!qCanAllocColors(this)) {
+ qWarning("QGLWidget::setColormap: Cannot create a read/write "
+ "colormap for this visual");
+ return;
+ }
+
+ // If the child GL widget is not of the same visual class as the
+ // toplevel widget we will get in trouble..
+ Window wid = tlw->winId();
+ Visual * vis = (Visual *) tlw->x11Info().visual();;
+ VisualID cvId = XVisualIDFromVisual((Visual *) x11Info().visual());
+ VisualID tvId = XVisualIDFromVisual((Visual *) tlw->x11Info().visual());
+ if (cvId != tvId) {
+ wid = winId();
+ vis = (Visual *) x11Info().visual();
+ }
+
+ if (!d->cmap.handle()) // allocate a cmap if necessary
+ d->cmap.setHandle(XCreateColormap(X11->display, wid, vis, AllocAll));
+
+ qStoreColors(this, (Colormap) d->cmap.handle(), c);
+ XSetWindowColormap(X11->display, wid, (Colormap) d->cmap.handle());
+
+ // tell the wm that this window has a special colormap
+ Window * cmw;
+ Window * cmwret;
+ int count;
+ if (XGetWMColormapWindows(X11->display, tlw->winId(), &cmwret, &count))
+ {
+ cmw = new Window[count+1];
+ memcpy((char *) cmw, (char *) cmwret, sizeof(Window) * count);
+ XFree((char *) cmwret);
+ int i;
+ for (i = 0; i < count; i++) {
+ if (cmw[i] == winId()) {
+ break;
+ }
+ }
+ if (i >= count) // append new window only if not in the list
+ cmw[count++] = winId();
+ } else {
+ count = 1;
+ cmw = new Window[count];
+ cmw[0] = winId();
+ }
+ XSetWMColormapWindows(X11->display, tlw->winId(), cmw, count);
+ delete [] cmw;
+}
+
+void QGLExtensions::init()
+{
+ static bool init_done = false;
+
+ if (init_done)
+ return;
+ init_done = true;
+
+ QGLWidget dmy;
+ dmy.makeCurrent();
+ init_extensions();
+
+ // nvidia 9x.xx unix drivers contain a bug which requires us to call glFinish before releasing an fbo
+ // to avoid painting artifacts
+ const QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
+ const int pos = versionString.indexOf("NVIDIA");
+ if (pos >= 0) {
+ const float nvidiaDriverVersion = versionString.mid(pos + strlen("NVIDIA")).toFloat();
+ nvidiaFboNeedsFinish = nvidiaDriverVersion >= 90.0 && nvidiaDriverVersion < 100.0;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp
new file mode 100644
index 0000000000..480a2dc504
--- /dev/null
+++ b/src/opengl/qgl_x11egl.cpp
@@ -0,0 +1,378 @@
+/****************************************************************************
+**
+** 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 "qgl.h"
+#include <private/qt_x11_p.h>
+#include <private/qgl_p.h>
+#include <private/qpaintengine_opengl_p.h>
+#include "qgl_egl_p.h"
+#include "qcolormap.h"
+
+
+QT_BEGIN_NAMESPACE
+
+
+bool QGLFormat::hasOpenGL()
+{
+ return true;
+}
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ return false;
+}
+
+void qt_egl_add_platform_config(QEglProperties& props, QPaintDevice *device)
+{
+ if (device->devType() == QInternal::Image)
+ props.setPixelFormat(static_cast<QImage *>(device)->format());
+}
+
+bool QGLContext::chooseContext(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+
+ if (!device())
+ return false;
+
+ int devType = device()->devType();
+
+ // Get the display and initialize it.
+ d->eglContext = new QEglContext();
+ d->eglContext->setApi(QEglContext::OpenGL);
+ if (!d->eglContext->openDisplay(device())) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ // Construct the configuration we need for this surface.
+ QEglProperties configProps;
+ qt_egl_set_format(configProps, devType, d->glFormat);
+ qt_egl_add_platform_config(configProps, device());
+ configProps.setRenderableType(QEglContext::OpenGL);
+
+ // Search for a matching configuration, reducing the complexity
+ // each time until we get something that matches.
+ if (!d->eglContext->chooseConfig(configProps, QEglContext::BestPixelFormat)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ // Inform the higher layers about the actual format properties.
+ qt_egl_update_format(*(d->eglContext), d->glFormat);
+
+ // Create a new context for the configuration.
+ if (!d->eglContext->createContext
+ (shareContext ? shareContext->d_func()->eglContext : 0)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+#if defined(EGL_VERSION_1_1)
+ if (d->glFormat.swapInterval() != -1 && devType == QInternal::Widget)
+ eglSwapInterval(d->eglContext->display(), d->glFormat.swapInterval());
+#endif
+
+ // Create the EGL surface to draw into.
+ if (!d->eglContext->createSurface(device())) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ return true;
+}
+
+
+void QGLContext::reset()
+{
+ Q_D(QGLContext);
+ if (!d->valid)
+ return;
+ d->cleanup();
+ doneCurrent();
+ if (d->eglContext) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ }
+ d->crWin = false;
+ d->sharing = false;
+ d->valid = false;
+ d->transpColor = QColor();
+ d->initDone = false;
+ qgl_share_reg()->removeShare(this);
+}
+
+void QGLContext::makeCurrent()
+{
+ Q_D(QGLContext);
+ if(!d->valid || !d->eglContext) {
+ qWarning("QGLContext::makeCurrent(): Cannot make invalid context current");
+ return;
+ }
+
+ if (d->eglContext->makeCurrent()) {
+ if (!qgl_context_storage.hasLocalData() && QThread::currentThread())
+ qgl_context_storage.setLocalData(new QGLThreadContext);
+ if (qgl_context_storage.hasLocalData())
+ qgl_context_storage.localData()->context = this;
+ currentCtx = this;
+ }
+}
+
+void QGLContext::doneCurrent()
+{
+ Q_D(QGLContext);
+ if (d->eglContext)
+ d->eglContext->doneCurrent();
+
+ if (qgl_context_storage.hasLocalData())
+ qgl_context_storage.localData()->context = 0;
+ currentCtx = 0;
+}
+
+
+void QGLContext::swapBuffers() const
+{
+ Q_D(const QGLContext);
+ if(!d->valid || !d->eglContext)
+ return;
+
+ d->eglContext->swapBuffers();
+}
+
+QColor QGLContext::overlayTransparentColor() const
+{
+ return QColor(0, 0, 0); // Invalid color
+}
+
+uint QGLContext::colorIndex(const QColor &c) const
+{
+ //### color index doesn't work on egl
+ Q_UNUSED(c);
+ return 0;
+}
+
+void QGLContext::generateFontDisplayLists(const QFont & fnt, int listBase)
+{
+ Q_UNUSED(fnt);
+ Q_UNUSED(listBase);
+}
+
+void *QGLContext::getProcAddress(const QString &proc) const
+{
+ return (void*)eglGetProcAddress(reinterpret_cast<const char *>(proc.toLatin1().data()));
+}
+
+void QGLWidget::setMouseTracking(bool enable)
+{
+ QWidget::setMouseTracking(enable);
+}
+
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ resizeGL(width(), height());
+ //handle overlay
+}
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return 0;
+}
+
+void QGLWidget::makeOverlayCurrent()
+{
+ //handle overlay
+}
+
+void QGLWidget::updateOverlayGL()
+{
+ //handle overlay
+}
+
+void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+ if (!context->deviceIsPixmap() && context->device() != this) {
+ qWarning("QGLWidget::setContext: Context must refer to this widget");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+
+ if (parentWidget()) {
+ // force creation of delay-created widgets
+ parentWidget()->winId();
+ if (parentWidget()->x11Info().screen() != x11Info().screen())
+ d_func()->xinfo = parentWidget()->d_func()->xinfo;
+ }
+
+ bool visible = isVisible();
+ if (visible)
+ hide();
+
+ XVisualInfo vi;
+
+ int err = XMatchVisualInfo(x11Info().display(), x11Info().screen(), x11Info().depth(), TrueColor, &vi);
+ if (err == 0) {
+ qWarning("Error: Couldn't get a matching X visual for format");
+ return;
+ }
+
+ XSetWindowAttributes a;
+
+ Window p = RootWindow(X11->display, vi.screen);
+ if (parentWidget())
+ p = parentWidget()->winId();
+
+ QColormap colmap = QColormap::instance(vi.screen);
+ a.background_pixel = colmap.pixel(palette().color(backgroundRole()));
+ a.border_pixel = colmap.pixel(Qt::black);
+
+ Window w = XCreateWindow(X11->display, p, x(), y(), width(), height(),
+ 0, vi.depth, InputOutput, vi.visual,
+ CWBackPixel|CWBorderPixel, &a);
+
+ if (deleteOldContext)
+ delete oldcx;
+ oldcx = 0;
+
+ create(w); // Create with the ID of the window we've just created
+
+ d->eglSurfaceWindowId = w; // Remember the window id we created the surface for
+
+ if (visible)
+ show();
+
+ bool createFailed = false;
+ if (!d->glcx->isValid()) {
+ if (!d->glcx->create(shareContext ? shareContext : oldcx))
+ createFailed = true;
+ }
+ if (createFailed) {
+ if (deleteOldContext)
+ delete oldcx;
+ return;
+ }
+
+ if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) {
+ if (deleteOldContext)
+ delete oldcx;
+ return;
+ }
+
+ d->glcx->setWindowCreated(true);
+}
+
+void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget* shareWidget)
+{
+ Q_Q(QGLWidget);
+
+ initContext(context, shareWidget);
+
+ if(q->isValid() && glcx->format().hasOverlay()) {
+ //no overlay
+ qWarning("QtOpenGL ES doesn't currently support overlays");
+ }
+}
+
+bool QGLWidgetPrivate::renderCxPm(QPixmap*)
+{
+ return false;
+}
+
+void QGLWidgetPrivate::cleanupColormaps()
+{
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ return d_func()->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap &)
+{
+}
+
+void QGLExtensions::init()
+{
+ static bool init_done = false;
+
+ if (init_done)
+ return;
+ init_done = true;
+ init_extensions();
+}
+
+// Re-creates the EGL surface if the window ID has changed or if force is true
+void QGLWidgetPrivate::recreateEglSurface(bool force)
+{
+ Q_Q(QGLWidget);
+
+ Window currentId = q->winId();
+
+ if ( force || (currentId != eglSurfaceWindowId) ) {
+ // The window id has changed so we need to re-create the EGL surface
+ if (!glcx->d_func()->eglContext->recreateSurface(q))
+ qWarning("Error creating EGL window surface: 0x%x", eglGetError());
+
+ eglSurfaceWindowId = currentId;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglcolormap.cpp b/src/opengl/qglcolormap.cpp
new file mode 100644
index 0000000000..02a2c13d89
--- /dev/null
+++ b/src/opengl/qglcolormap.cpp
@@ -0,0 +1,287 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \class QGLColormap
+ \brief The QGLColormap class is used for installing custom colormaps into
+ QGLWidgets.
+
+ \module OpenGL
+ \ingroup multimedia
+ \ingroup shared
+
+ QGLColormap provides a platform independent way of specifying and
+ installing indexed colormaps into QGLWidgets. QGLColormap is
+ especially useful when using the OpenGL color-index mode.
+
+ Under X11 you must use an X server that supports either a \c
+ PseudoColor or \c DirectColor visual class. If your X server
+ currently only provides a \c GrayScale, \c TrueColor, \c
+ StaticColor or \c StaticGray visual, you will not be able to
+ allocate colorcells for writing. If this is the case, try setting
+ your X server to 8 bit mode. It should then provide you with at
+ least a \c PseudoColor visual. Note that you may experience
+ colormap flashing if your X server is running in 8 bit mode.
+
+ Under Windows the size of the colormap is always set to 256
+ colors. Note that under Windows you can also install colormaps
+ in child widgets.
+
+ This class uses \l{implicit sharing} as a memory and speed
+ optimization.
+
+ Example of use:
+ \snippet doc/src/snippets/code/src_opengl_qglcolormap.cpp 0
+
+ \sa QGLWidget::setColormap(), QGLWidget::colormap()
+*/
+
+/*!
+ \fn Qt::HANDLE QGLColormap::handle()
+
+ \internal
+
+ Returns the handle for this color map.
+*/
+
+/*!
+ \fn void QGLColormap::setHandle(Qt::HANDLE handle)
+
+ \internal
+
+ Sets the handle for this color map to \a handle.
+*/
+
+#include "qglcolormap.h"
+
+QT_BEGIN_NAMESPACE
+
+QGLColormap::QGLColormapData QGLColormap::shared_null = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0 };
+
+/*!
+ Construct a QGLColormap.
+*/
+QGLColormap::QGLColormap()
+ : d(&shared_null)
+{
+ d->ref.ref();
+}
+
+
+/*!
+ Construct a shallow copy of \a map.
+*/
+QGLColormap::QGLColormap(const QGLColormap &map)
+ : d(map.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ Dereferences the QGLColormap and deletes it if this was the last
+ reference to it.
+*/
+QGLColormap::~QGLColormap()
+{
+ if (!d->ref.deref())
+ cleanup(d);
+}
+
+void QGLColormap::cleanup(QGLColormap::QGLColormapData *x)
+{
+ delete x->cells;
+ x->cells = 0;
+ delete x;
+}
+
+/*!
+ Assign a shallow copy of \a map to this QGLColormap.
+*/
+QGLColormap & QGLColormap::operator=(const QGLColormap &map)
+{
+ map.d->ref.ref();
+ if (!d->ref.deref())
+ cleanup(d);
+ d = map.d;
+ return *this;
+}
+
+/*!
+ \fn void QGLColormap::detach()
+ \internal
+
+ Detaches this QGLColormap from the shared block.
+*/
+
+void QGLColormap::detach_helper()
+{
+ QGLColormapData *x = new QGLColormapData;
+ x->ref = 1;
+ x->cmapHandle = 0;
+ x->cells = 0;
+ if (d->cells) {
+ x->cells = new QVector<QRgb>(256);
+ *x->cells = *d->cells;
+ }
+ if (!d->ref.deref())
+ cleanup(d);
+ d = x;
+}
+
+/*!
+ Set cell at index \a idx in the colormap to color \a color.
+*/
+void QGLColormap::setEntry(int idx, QRgb color)
+{
+ detach();
+ if (!d->cells)
+ d->cells = new QVector<QRgb>(256);
+ d->cells->insert(idx, color);
+}
+
+/*!
+ Set an array of cells in this colormap. \a count is the number of
+ colors that should be set, \a colors is the array of colors, and
+ \a base is the starting index.
+*/
+void QGLColormap::setEntries(int count, const QRgb *colors, int base)
+{
+ detach();
+ if (!d->cells)
+ d->cells = new QVector<QRgb>(256);
+
+ Q_ASSERT_X(!colors || base >= 0 || base + count < d->cells->size(), "QGLColormap::setEntries",
+ "preconditions not met");
+ for (int i = base; i < base + count; ++i)
+ setEntry(i, colors[i]);
+}
+
+/*!
+ Returns the QRgb value in the colorcell with index \a idx.
+*/
+QRgb QGLColormap::entryRgb(int idx) const
+{
+ if (d == &shared_null || !d->cells)
+ return 0;
+ else
+ return d->cells->at(idx);
+}
+
+/*!
+ \overload
+
+ Set the cell with index \a idx in the colormap to color \a color.
+*/
+void QGLColormap::setEntry(int idx, const QColor &color)
+{
+ setEntry(idx, color.rgb());
+}
+
+/*!
+ Returns the QRgb value in the colorcell with index \a idx.
+*/
+QColor QGLColormap::entryColor(int idx) const
+{
+ if (d == &shared_null || !d->cells)
+ return QColor();
+ else
+ return QColor(d->cells->at(idx));
+}
+
+/*!
+ Returns true if the colormap is empty; otherwise returns false. A
+ colormap with no color values set is considered to be empty.
+*/
+bool QGLColormap::isEmpty() const
+{
+ return d == &shared_null || d->cells == 0 || d->cells->size() == 0 || d->cmapHandle == 0;
+}
+
+
+/*!
+ Returns the number of colorcells in the colormap.
+*/
+int QGLColormap::size() const
+{
+ return d->cells ? d->cells->size() : 0;
+}
+
+/*!
+ Returns the index of the color \a color. If \a color is not in the
+ map, -1 is returned.
+*/
+int QGLColormap::find(QRgb color) const
+{
+ if (d->cells)
+ return d->cells->indexOf(color);
+ return -1;
+}
+
+/*!
+ Returns the index of the color that is the closest match to color
+ \a color.
+*/
+int QGLColormap::findNearest(QRgb color) const
+{
+ int idx = find(color);
+ if (idx >= 0)
+ return idx;
+ int mapSize = size();
+ int mindist = 200000;
+ int r = qRed(color);
+ int g = qGreen(color);
+ int b = qBlue(color);
+ int rx, gx, bx, dist;
+ for (int i = 0; i < mapSize; ++i) {
+ QRgb ci = d->cells->at(i);
+ rx = r - qRed(ci);
+ gx = g - qGreen(ci);
+ bx = b - qBlue(ci);
+ dist = rx * rx + gx * gx + bx * bx; // calculate distance
+ if (dist < mindist) { // minimal?
+ mindist = dist;
+ idx = i;
+ }
+ }
+ return idx;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglcolormap.h b/src/opengl/qglcolormap.h
new file mode 100644
index 0000000000..6bdb0c491c
--- /dev/null
+++ b/src/opengl/qglcolormap.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** 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 QGLCOLORMAP_H
+#define QGLCOLORMAP_H
+
+#include <QtGui/qcolor.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class Q_OPENGL_EXPORT QGLColormap
+{
+public:
+ QGLColormap();
+ QGLColormap(const QGLColormap &);
+ ~QGLColormap();
+
+ QGLColormap &operator=(const QGLColormap &);
+
+ bool isEmpty() const;
+ int size() const;
+ void detach();
+
+ void setEntries(int count, const QRgb * colors, int base = 0);
+ void setEntry(int idx, QRgb color);
+ void setEntry(int idx, const QColor & color);
+ QRgb entryRgb(int idx) const;
+ QColor entryColor(int idx) const;
+ int find(QRgb color) const;
+ int findNearest(QRgb color) const;
+
+protected:
+ Qt::HANDLE handle() { return d ? d->cmapHandle : 0; }
+ void setHandle(Qt::HANDLE ahandle) { d->cmapHandle = ahandle; }
+
+private:
+ struct QGLColormapData {
+ QBasicAtomicInt ref;
+ QVector<QRgb> *cells;
+ Qt::HANDLE cmapHandle;
+ };
+
+ QGLColormapData *d;
+ static struct QGLColormapData shared_null;
+ static void cleanup(QGLColormapData *x);
+ void detach_helper();
+
+ friend class QGLWidget;
+ friend class QGLWidgetPrivate;
+};
+
+inline void QGLColormap::detach()
+{
+ if (d->ref != 1)
+ detach_helper();
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGLCOLORMAP_H
diff --git a/src/opengl/qglextensions.cpp b/src/opengl/qglextensions.cpp
new file mode 100644
index 0000000000..8357cf996f
--- /dev/null
+++ b/src/opengl/qglextensions.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 "qgl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+bool qt_resolve_framebufferobject_extensions(QGLContext *ctx)
+{
+#if !defined(QT_OPENGL_ES_2)
+ if (glIsRenderbufferEXT != 0)
+ return true;
+
+ if (ctx == 0) {
+ qWarning("QGLFramebufferObject: Unable to resolve framebuffer object extensions -"
+ " make sure there is a current context when creating the framebuffer object.");
+ return false;
+ }
+
+ glIsRenderbufferEXT = (_glIsRenderbufferEXT) ctx->getProcAddress(QLatin1String("glIsRenderbufferEXT"));
+ glBindRenderbufferEXT = (_glBindRenderbufferEXT) ctx->getProcAddress(QLatin1String("glBindRenderbufferEXT"));
+ glDeleteRenderbuffersEXT = (_glDeleteRenderbuffersEXT) ctx->getProcAddress(QLatin1String("glDeleteRenderbuffersEXT"));
+ glGenRenderbuffersEXT = (_glGenRenderbuffersEXT) ctx->getProcAddress(QLatin1String("glGenRenderbuffersEXT"));
+ glRenderbufferStorageEXT = (_glRenderbufferStorageEXT) ctx->getProcAddress(QLatin1String("glRenderbufferStorageEXT"));
+ glGetRenderbufferParameterivEXT =
+ (_glGetRenderbufferParameterivEXT) ctx->getProcAddress(QLatin1String("glGetRenderbufferParameterivEXT"));
+ glIsFramebufferEXT = (_glIsFramebufferEXT) ctx->getProcAddress(QLatin1String("glIsFramebufferEXT"));
+ glBindFramebufferEXT = (_glBindFramebufferEXT) ctx->getProcAddress(QLatin1String("glBindFramebufferEXT"));
+ glDeleteFramebuffersEXT = (_glDeleteFramebuffersEXT) ctx->getProcAddress(QLatin1String("glDeleteFramebuffersEXT"));
+ glGenFramebuffersEXT = (_glGenFramebuffersEXT) ctx->getProcAddress(QLatin1String("glGenFramebuffersEXT"));
+ glCheckFramebufferStatusEXT = (_glCheckFramebufferStatusEXT) ctx->getProcAddress(QLatin1String("glCheckFramebufferStatusEXT"));
+ glFramebufferTexture1DEXT = (_glFramebufferTexture1DEXT) ctx->getProcAddress(QLatin1String("glFramebufferTexture1DEXT"));
+ glFramebufferTexture2DEXT = (_glFramebufferTexture2DEXT) ctx->getProcAddress(QLatin1String("glFramebufferTexture2DEXT"));
+ glFramebufferTexture3DEXT = (_glFramebufferTexture3DEXT) ctx->getProcAddress(QLatin1String("glFramebufferTexture3DEXT"));
+ glFramebufferRenderbufferEXT = (_glFramebufferRenderbufferEXT) ctx->getProcAddress(QLatin1String("glFramebufferRenderbufferEXT"));
+ glGetFramebufferAttachmentParameterivEXT =
+ (_glGetFramebufferAttachmentParameterivEXT) ctx->getProcAddress(QLatin1String("glGetFramebufferAttachmentParameterivEXT"));
+ glGenerateMipmapEXT = (_glGenerateMipmapEXT) ctx->getProcAddress(QLatin1String("glGenerateMipmapEXT"));
+ return glIsRenderbufferEXT;
+#else
+ Q_UNUSED(ctx);
+ return true;
+#endif
+}
+
+bool qt_resolve_version_1_3_functions(QGLContext *ctx)
+{
+ if (glMultiTexCoord4f != 0)
+ return true;
+
+ QGLContext cx(QGLFormat::defaultFormat());
+ glMultiTexCoord4f = (_glMultiTexCoord4f) ctx->getProcAddress(QLatin1String("glMultiTexCoord4f"));
+
+#if defined(QT_OPENGL_ES_2)
+ return glMultiTexCoord4f;
+#else
+ glActiveTexture = (_glActiveTexture) ctx->getProcAddress(QLatin1String("glActiveTexture"));
+ return glMultiTexCoord4f && glActiveTexture;
+#endif
+}
+
+bool qt_resolve_stencil_face_extension(QGLContext *ctx)
+{
+ if (glActiveStencilFaceEXT != 0)
+ return true;
+
+ QGLContext cx(QGLFormat::defaultFormat());
+ glActiveStencilFaceEXT = (_glActiveStencilFaceEXT) ctx->getProcAddress(QLatin1String("glActiveStencilFaceEXT"));
+
+ return glActiveStencilFaceEXT;
+}
+
+bool qt_resolve_frag_program_extensions(QGLContext *ctx)
+{
+ if (glProgramStringARB != 0)
+ return true;
+
+ // ARB_fragment_program
+ glProgramStringARB = (_glProgramStringARB) ctx->getProcAddress(QLatin1String("glProgramStringARB"));
+ glBindProgramARB = (_glBindProgramARB) ctx->getProcAddress(QLatin1String("glBindProgramARB"));
+ glDeleteProgramsARB = (_glDeleteProgramsARB) ctx->getProcAddress(QLatin1String("glDeleteProgramsARB"));
+ glGenProgramsARB = (_glGenProgramsARB) ctx->getProcAddress(QLatin1String("glGenProgramsARB"));
+ glProgramLocalParameter4fvARB = (_glProgramLocalParameter4fvARB) ctx->getProcAddress(QLatin1String("glProgramLocalParameter4fvARB"));
+
+ return glProgramStringARB
+ && glBindProgramARB
+ && glDeleteProgramsARB
+ && glGenProgramsARB
+ && glProgramLocalParameter4fvARB;
+}
+
+bool qt_resolve_buffer_extensions(QGLContext *ctx)
+{
+ if (glBindBufferARB && glDeleteBuffersARB && glGenBuffersARB && glBufferDataARB
+ && glMapBufferARB && glUnmapBufferARB)
+ return true;
+
+ glBindBufferARB = (_glBindBufferARB) ctx->getProcAddress(QLatin1String("glBindBufferARB"));
+ glDeleteBuffersARB = (_glDeleteBuffersARB) ctx->getProcAddress(QLatin1String("glDeleteBuffersARB"));
+ glGenBuffersARB = (_glGenBuffersARB) ctx->getProcAddress(QLatin1String("glGenBuffersARB"));
+ glBufferDataARB = (_glBufferDataARB) ctx->getProcAddress(QLatin1String("glBufferDataARB"));
+ glMapBufferARB = (_glMapBufferARB) ctx->getProcAddress(QLatin1String("glMapBufferARB"));
+ glUnmapBufferARB = (_glUnmapBufferARB) ctx->getProcAddress(QLatin1String("glUnmapBufferARB"));
+
+ return glBindBufferARB
+ && glDeleteBuffersARB
+ && glGenBuffersARB
+ && glBufferDataARB
+ && glMapBufferARB
+ && glUnmapBufferARB;
+}
+
+bool qt_resolve_glsl_extensions(QGLContext *ctx)
+{
+ if (glCreateShader)
+ return true;
+
+ glCreateShader = (_glCreateShader) ctx->getProcAddress(QLatin1String("glCreateShader"));
+ glShaderSource = (_glShaderSource) ctx->getProcAddress(QLatin1String("glShaderSource"));
+ glCompileShader = (_glCompileShader) ctx->getProcAddress(QLatin1String("glCompileShader"));
+ glDeleteShader = (_glDeleteShader) ctx->getProcAddress(QLatin1String("glDeleteShader"));
+
+ glCreateProgram = (_glCreateProgram) ctx->getProcAddress(QLatin1String("glCreateProgram"));
+ glAttachShader = (_glAttachShader) ctx->getProcAddress(QLatin1String("glAttachShader"));
+ glDetachShader = (_glDetachShader) ctx->getProcAddress(QLatin1String("glDetachShader"));
+ glLinkProgram = (_glLinkProgram) ctx->getProcAddress(QLatin1String("glLinkProgram"));
+ glUseProgram = (_glUseProgram) ctx->getProcAddress(QLatin1String("glUseProgram"));
+ glDeleteProgram = (_glDeleteProgram) ctx->getProcAddress(QLatin1String("glDeleteProgram"));
+
+ glGetShaderInfoLog = (_glGetShaderInfoLog) ctx->getProcAddress(QLatin1String("glGetShaderInfoLog"));
+ glGetShaderiv = (_glGetShaderiv) ctx->getProcAddress(QLatin1String("glGetShaderiv"));
+ glGetProgramiv = (_glGetProgramiv) ctx->getProcAddress(QLatin1String("glGetProgramiv"));
+
+ glGetUniformLocation = (_glGetUniformLocation) ctx->getProcAddress(QLatin1String("glGetUniformLocation"));
+ glUniform4fv = (_glUniform4fv) ctx->getProcAddress(QLatin1String("glUniform4fv"));
+ glUniform3fv = (_glUniform3fv) ctx->getProcAddress(QLatin1String("glUniform3fv"));
+ glUniform2fv = (_glUniform2fv) ctx->getProcAddress(QLatin1String("glUniform2fv"));
+ glUniform1fv = (_glUniform1fv) ctx->getProcAddress(QLatin1String("glUniform1fv"));
+ glUniform1i = (_glUniform1i) ctx->getProcAddress(QLatin1String("glUniform1i"));
+
+ return glCreateShader && glShaderSource && glCompileShader && glDeleteProgram &&
+ glCreateProgram && glAttachShader && glDetachShader && glLinkProgram && glUseProgram &&
+ glDeleteProgram && glGetShaderInfoLog && glGetShaderiv && glGetProgramiv && glGetUniformLocation &&
+ glUniform1i && glUniform1fv && glUniform2fv && glUniform3fv && glUniform4fv;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglextensions_p.h b/src/opengl/qglextensions_p.h
new file mode 100644
index 0000000000..a0517f505e
--- /dev/null
+++ b/src/opengl/qglextensions_p.h
@@ -0,0 +1,516 @@
+/****************************************************************************
+**
+** 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 QGL_EXTENSIONS_P_H
+#define QGL_EXTENSIONS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Qt OpenGL classes. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// extension prototypes
+#ifndef Q_WS_MAC
+# ifndef APIENTRYP
+# ifdef APIENTRY
+# define APIENTRYP APIENTRY *
+# else
+# define APIENTRY
+# define APIENTRYP *
+# endif
+# endif
+#else
+# define APIENTRY
+# define APIENTRYP *
+#endif
+
+#include <QtCore/qglobal.h>
+
+#ifndef GL_ARB_vertex_buffer_object
+typedef ptrdiff_t GLsizeiptrARB;
+#endif
+
+// ARB_pixel_buffer_object
+typedef void (APIENTRY *_glBindBufferARB) (GLenum, GLuint);
+typedef void (APIENTRY *_glDeleteBuffersARB) (GLsizei, const GLuint *);
+typedef void (APIENTRY *_glGenBuffersARB) (GLsizei, GLuint *);
+typedef void (APIENTRY *_glBufferDataARB) (GLenum, GLsizeiptrARB, const GLvoid *, GLenum);
+typedef GLvoid* (APIENTRY *_glMapBufferARB) (GLenum, GLenum);
+typedef GLboolean (APIENTRY *_glUnmapBufferARB) (GLenum);
+
+// ARB_fragment_program
+typedef void (APIENTRY *_glProgramStringARB) (GLenum, GLenum, GLsizei, const GLvoid *);
+typedef void (APIENTRY *_glBindProgramARB) (GLenum, GLuint);
+typedef void (APIENTRY *_glDeleteProgramsARB) (GLsizei, const GLuint *);
+typedef void (APIENTRY *_glGenProgramsARB) (GLsizei, GLuint *);
+typedef void (APIENTRY *_glProgramLocalParameter4fvARB) (GLenum, GLuint, const GLfloat *);
+
+// GLSL
+typedef GLuint (APIENTRY *_glCreateShader) (GLenum);
+typedef void (APIENTRY *_glShaderSource) (GLuint, GLsizei, const char **, const GLint *);
+typedef void (APIENTRY *_glCompileShader) (GLuint);
+typedef void (APIENTRY *_glDeleteShader) (GLuint);
+
+typedef GLuint (APIENTRY *_glCreateProgram) ();
+typedef void (APIENTRY *_glAttachShader) (GLuint, GLuint);
+typedef void (APIENTRY *_glDetachShader) (GLuint, GLuint);
+typedef void (APIENTRY *_glLinkProgram) (GLuint);
+typedef void (APIENTRY *_glUseProgram) (GLuint);
+typedef void (APIENTRY *_glDeleteProgram) (GLuint);
+
+typedef void (APIENTRY *_glGetShaderInfoLog) (GLuint, GLsizei, GLsizei *, char *);
+typedef void (APIENTRY *_glGetShaderiv) (GLuint, GLenum, GLint *);
+typedef void (APIENTRY *_glGetProgramiv) (GLuint, GLenum, GLint *);
+
+typedef GLuint (APIENTRY *_glGetUniformLocation) (GLuint, const char*);
+typedef void (APIENTRY *_glUniform4fv) (GLint, GLsizei, GLfloat *);
+typedef void (APIENTRY *_glUniform3fv) (GLint, GLsizei, GLfloat *);
+typedef void (APIENTRY *_glUniform2fv) (GLint, GLsizei, GLfloat *);
+typedef void (APIENTRY *_glUniform1fv) (GLint, GLsizei, GLfloat *);
+typedef void (APIENTRY *_glUniform1i) (GLint, GLint);
+
+typedef void (APIENTRY *_glActiveStencilFaceEXT) (GLenum );
+
+typedef void (APIENTRY *_glMultiTexCoord4f) (GLenum, GLfloat, GLfloat, GLfloat, GLfloat);
+typedef void (APIENTRY *_glActiveTexture) (GLenum);
+
+// EXT_GL_framebuffer_object
+typedef GLboolean (APIENTRY *_glIsRenderbufferEXT) (GLuint renderbuffer);
+typedef void (APIENTRY *_glBindRenderbufferEXT) (GLenum target, GLuint renderbuffer);
+typedef void (APIENTRY *_glDeleteRenderbuffersEXT) (GLsizei n, const GLuint *renderbuffers);
+typedef void (APIENTRY *_glGenRenderbuffersEXT) (GLsizei n, GLuint *renderbuffers);
+typedef void (APIENTRY *_glRenderbufferStorageEXT) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (APIENTRY *_glGetRenderbufferParameterivEXT) (GLenum target, GLenum pname, GLint *params);
+typedef GLboolean (APIENTRY *_glIsFramebufferEXT) (GLuint framebuffer);
+typedef void (APIENTRY *_glBindFramebufferEXT) (GLenum target, GLuint framebuffer);
+typedef void (APIENTRY *_glDeleteFramebuffersEXT) (GLsizei n, const GLuint *framebuffers);
+typedef void (APIENTRY *_glGenFramebuffersEXT) (GLsizei n, GLuint *framebuffers);
+typedef GLenum (APIENTRY *_glCheckFramebufferStatusEXT) (GLenum target);
+typedef void (APIENTRY *_glFramebufferTexture1DEXT) (GLenum target, GLenum attachment, GLenum textarget,
+ GLuint texture, GLint level);
+typedef void (APIENTRY *_glFramebufferTexture2DEXT) (GLenum target, GLenum attachment, GLenum textarget,
+ GLuint texture, GLint level);
+typedef void (APIENTRY *_glFramebufferTexture3DEXT) (GLenum target, GLenum attachment, GLenum textarget,
+ GLuint texture, GLint level, GLint zoffset);
+typedef void (APIENTRY *_glFramebufferRenderbufferEXT) (GLenum target, GLenum attachment, GLenum renderbuffertarget,
+ GLuint renderbuffer);
+typedef void (APIENTRY *_glGetFramebufferAttachmentParameterivEXT) (GLenum target, GLenum attachment, GLenum pname,
+ GLint *params);
+typedef void (APIENTRY *_glGenerateMipmapEXT) (GLenum target);
+
+QT_BEGIN_NAMESPACE
+
+struct QGLExtensionFuncs
+{
+ QGLExtensionFuncs() {
+ qt_glProgramStringARB = 0;
+ qt_glBindProgramARB = 0;
+ qt_glDeleteProgramsARB = 0;
+ qt_glGenProgramsARB = 0;
+ qt_glProgramLocalParameter4fvARB = 0;
+
+ qt_glCreateShader = 0;
+ qt_glShaderSource = 0;
+ qt_glCompileShader = 0;
+ qt_glDeleteShader = 0;
+
+ qt_glCreateProgram = 0;
+ qt_glAttachShader = 0;
+ qt_glDetachShader = 0;
+ qt_glLinkProgram = 0;
+ qt_glUseProgram = 0;
+ qt_glDeleteProgram = 0;
+
+ qt_glGetShaderInfoLog = 0;
+ qt_glGetShaderiv = 0;
+ qt_glGetProgramiv = 0;
+
+ qt_glGetUniformLocation = 0;
+ qt_glUniform4fv = 0;
+ qt_glUniform3fv = 0;
+ qt_glUniform2fv = 0;
+ qt_glUniform1fv = 0;
+ qt_glUniform1i = 0;
+
+ qt_glActiveStencilFaceEXT = 0;
+
+ qt_glMultiTexCoord4f = 0;
+ qt_glActiveTexture = 0;
+
+#if !defined(QT_OPENGL_ES_2)
+ qt_glIsRenderbufferEXT = 0;
+ qt_glBindRenderbufferEXT = 0;
+ qt_glDeleteRenderbuffersEXT = 0;
+ qt_glGenRenderbuffersEXT = 0;
+ qt_glRenderbufferStorageEXT = 0;
+ qt_glGetRenderbufferParameterivEXT = 0;
+ qt_glIsFramebufferEXT = 0;
+ qt_glBindFramebufferEXT = 0;
+ qt_glDeleteFramebuffersEXT = 0;
+ qt_glGenFramebuffersEXT = 0;
+ qt_glCheckFramebufferStatusEXT = 0;
+ qt_glFramebufferTexture1DEXT = 0;
+ qt_glFramebufferTexture2DEXT = 0;
+ qt_glFramebufferTexture3DEXT = 0;
+ qt_glFramebufferRenderbufferEXT = 0;
+ qt_glGetFramebufferAttachmentParameterivEXT = 0;
+ qt_glGenerateMipmapEXT = 0;
+#endif
+
+ qt_glBindBufferARB = 0;
+ qt_glDeleteBuffersARB = 0;
+ qt_glGenBuffersARB = 0;
+ qt_glBufferDataARB = 0;
+ qt_glMapBufferARB = 0;
+ qt_glUnmapBufferARB = 0;
+ }
+
+ _glProgramStringARB qt_glProgramStringARB;
+ _glBindProgramARB qt_glBindProgramARB;
+ _glDeleteProgramsARB qt_glDeleteProgramsARB;
+ _glGenProgramsARB qt_glGenProgramsARB;
+ _glProgramLocalParameter4fvARB qt_glProgramLocalParameter4fvARB;
+
+ // GLSL definitions
+ _glCreateShader qt_glCreateShader;
+ _glShaderSource qt_glShaderSource;
+ _glCompileShader qt_glCompileShader;
+ _glDeleteShader qt_glDeleteShader;
+
+ _glCreateProgram qt_glCreateProgram;
+ _glAttachShader qt_glAttachShader;
+ _glDetachShader qt_glDetachShader;
+ _glLinkProgram qt_glLinkProgram;
+ _glUseProgram qt_glUseProgram;
+ _glDeleteProgram qt_glDeleteProgram;
+
+ _glGetShaderInfoLog qt_glGetShaderInfoLog;
+ _glGetShaderiv qt_glGetShaderiv;
+ _glGetProgramiv qt_glGetProgramiv;
+
+ _glGetUniformLocation qt_glGetUniformLocation;
+ _glUniform4fv qt_glUniform4fv;
+ _glUniform3fv qt_glUniform3fv;
+ _glUniform2fv qt_glUniform2fv;
+ _glUniform1fv qt_glUniform1fv;
+ _glUniform1i qt_glUniform1i;
+
+ _glActiveStencilFaceEXT qt_glActiveStencilFaceEXT;
+
+ _glMultiTexCoord4f qt_glMultiTexCoord4f;
+ _glActiveTexture qt_glActiveTexture;
+
+#if !defined(QT_OPENGL_ES_2)
+ _glIsRenderbufferEXT qt_glIsRenderbufferEXT;
+ _glBindRenderbufferEXT qt_glBindRenderbufferEXT;
+ _glDeleteRenderbuffersEXT qt_glDeleteRenderbuffersEXT;
+ _glGenRenderbuffersEXT qt_glGenRenderbuffersEXT;
+ _glRenderbufferStorageEXT qt_glRenderbufferStorageEXT;
+ _glGetRenderbufferParameterivEXT qt_glGetRenderbufferParameterivEXT;
+ _glIsFramebufferEXT qt_glIsFramebufferEXT;
+ _glBindFramebufferEXT qt_glBindFramebufferEXT;
+ _glDeleteFramebuffersEXT qt_glDeleteFramebuffersEXT;
+ _glGenFramebuffersEXT qt_glGenFramebuffersEXT;
+ _glCheckFramebufferStatusEXT qt_glCheckFramebufferStatusEXT;
+ _glFramebufferTexture1DEXT qt_glFramebufferTexture1DEXT;
+ _glFramebufferTexture2DEXT qt_glFramebufferTexture2DEXT;
+ _glFramebufferTexture3DEXT qt_glFramebufferTexture3DEXT;
+ _glFramebufferRenderbufferEXT qt_glFramebufferRenderbufferEXT;
+ _glGetFramebufferAttachmentParameterivEXT qt_glGetFramebufferAttachmentParameterivEXT;
+ _glGenerateMipmapEXT qt_glGenerateMipmapEXT;
+#endif
+
+ _glBindBufferARB qt_glBindBufferARB;
+ _glDeleteBuffersARB qt_glDeleteBuffersARB;
+ _glGenBuffersARB qt_glGenBuffersARB;
+ _glBufferDataARB qt_glBufferDataARB;
+ _glMapBufferARB qt_glMapBufferARB;
+ _glUnmapBufferARB qt_glUnmapBufferARB;
+};
+
+
+// OpenGL constants
+
+/* NV_texture_rectangle */
+#ifndef GL_NV_texture_rectangle
+#define GL_TEXTURE_RECTANGLE_NV 0x84F5
+#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6
+#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7
+#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8
+#endif
+
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+
+#ifndef GL_MULTISAMPLE
+#define GL_MULTISAMPLE 0x809D
+#endif
+
+#ifndef GL_CLAMP_TO_EDGE
+#define GL_CLAMP_TO_EDGE 0x812F
+#endif
+
+#ifndef GL_IBM_texture_mirrored_repeat
+#define GL_MIRRORED_REPEAT_IBM 0x8370
+#endif
+
+#ifndef GL_SGIS_generate_mipmap
+#define GL_GENERATE_MIPMAP_SGIS 0x8191
+#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192
+#endif
+
+// ARB_fragment_program extension protos
+#ifndef GL_FRAGMENT_PROGRAM_ARB
+#define GL_FRAGMENT_PROGRAM_ARB 0x8804
+#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875
+#endif
+
+#ifndef GL_PIXEL_UNPACK_BUFFER_ARB
+#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC
+#endif
+
+#ifndef GL_WRITE_ONLY_ARB
+#define GL_WRITE_ONLY_ARB 0x88B9
+#endif
+
+#ifndef GL_STREAM_DRAW_ARB
+#define GL_STREAM_DRAW_ARB 0x88E0
+#endif
+
+// Stencil wrap and two-side defines
+#ifndef GL_STENCIL_TEST_TWO_SIDE_EXT
+#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910
+#endif
+#ifndef GL_INCR_WRAP_EXT
+#define GL_INCR_WRAP_EXT 0x8507
+#endif
+#ifndef GL_DECR_WRAP_EXT
+#define GL_DECR_WRAP_EXT 0x8508
+#endif
+
+#ifndef GL_TEXTURE0
+#define GL_TEXTURE0 0x84C0
+#endif
+
+#ifndef GL_TEXTURE1
+#define GL_TEXTURE1 0x84C1
+#endif
+
+#ifndef GL_EXT_framebuffer_object
+#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506
+#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8
+#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6
+#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4
+#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7
+#define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT 0x8CD8
+#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9
+#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA
+#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB
+#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC
+#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD
+#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF
+#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0
+#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1
+#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2
+#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3
+#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4
+#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5
+#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6
+#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7
+#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8
+#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9
+#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA
+#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB
+#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC
+#define GL_COLOR_ATTACHMENT13_EXT 0x8CED
+#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE
+#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF
+#define GL_DEPTH_ATTACHMENT_EXT 0x8D00
+#define GL_STENCIL_ATTACHMENT_EXT 0x8D20
+#define GL_FRAMEBUFFER_EXT 0x8D40
+#define GL_RENDERBUFFER_EXT 0x8D41
+#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42
+#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43
+#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44
+#define GL_STENCIL_INDEX_EXT 0x8D45
+#define GL_STENCIL_INDEX1_EXT 0x8D46
+#define GL_STENCIL_INDEX4_EXT 0x8D47
+#define GL_STENCIL_INDEX8_EXT 0x8D48
+#define GL_STENCIL_INDEX16_EXT 0x8D49
+#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50
+#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51
+#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52
+#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53
+#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54
+#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55
+#endif
+
+#ifndef GL_EXT_packed_depth_stencil
+#define GL_DEPTH_STENCIL_EXT 0x84F9
+#define GL_UNSIGNED_INT_24_8_EXT 0x84FA
+#define GL_DEPTH24_STENCIL8_EXT 0x88F0
+#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1
+#endif
+
+// ### hm. should be part of the GL 1.2 spec..
+#ifndef GL_CLAMP_TO_EDGE
+#define GL_CLAMP_TO_EDGE 0x812F
+#endif
+
+#ifndef GL_VERSION_1_2
+#define GL_PACK_SKIP_IMAGES 0x806B
+#define GL_PACK_IMAGE_HEIGHT 0x806C
+#define GL_UNPACK_SKIP_IMAGES 0x806D
+#define GL_UNPACK_IMAGE_HEIGHT 0x806E
+#endif
+
+#define glProgramStringARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glProgramStringARB
+#define glBindProgramARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBindProgramARB
+#define glDeleteProgramsARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteProgramsARB
+#define glGenProgramsARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenProgramsARB
+#define glProgramLocalParameter4fvARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glProgramLocalParameter4fvARB
+
+#define glActiveStencilFaceEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glActiveStencilFaceEXT
+
+#define glMultiTexCoord4f QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glMultiTexCoord4f
+
+#if !defined(QT_OPENGL_ES_2)
+#define glActiveTexture QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glActiveTexture
+#endif
+
+#if !defined(QT_OPENGL_ES_2)
+
+#define glIsRenderbufferEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glIsRenderbufferEXT
+#define glBindRenderbufferEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBindRenderbufferEXT
+#define glDeleteRenderbuffersEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteRenderbuffersEXT
+#define glGenRenderbuffersEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenRenderbuffersEXT
+#define glRenderbufferStorageEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glRenderbufferStorageEXT
+#define glGetRenderbufferParameterivEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetRenderbufferParameterivEXT
+#define glIsFramebufferEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glIsFramebufferEXT
+#define glBindFramebufferEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBindFramebufferEXT
+#define glDeleteFramebuffersEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteFramebuffersEXT
+#define glGenFramebuffersEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenFramebuffersEXT
+#define glCheckFramebufferStatusEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glCheckFramebufferStatusEXT
+#define glFramebufferTexture1DEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glFramebufferTexture1DEXT
+#define glFramebufferTexture2DEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glFramebufferTexture2DEXT
+#define glFramebufferTexture3DEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glFramebufferTexture3DEXT
+#define glFramebufferRenderbufferEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glFramebufferRenderbufferEXT
+#define glGetFramebufferAttachmentParameterivEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetFramebufferAttachmentParameterivEXT
+#define glGenerateMipmapEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenerateMipmapEXT
+
+#else // QT_OPENGL_ES_2
+
+#define glIsRenderbufferEXT glIsRenderbuffer
+#define glBindRenderbufferEXT glBindRenderbuffer
+#define glDeleteRenderbuffersEXT glDeleteRenderbuffers
+#define glGenRenderbuffersEXT glGenRenderbuffers
+#define glRenderbufferStorageEXT glRenderbufferStorage
+#define glGetRenderbufferParameterivEXT glGetRenderbufferParameteriv
+#define glIsFramebufferEXT glIsFramebuffer
+#define glBindFramebufferEXT glBindFramebuffer
+#define glDeleteFramebuffersEXT glDeleteFramebuffers
+#define glGenFramebuffersEXT glGenFramebuffers
+#define glCheckFramebufferStatusEXT glCheckFramebufferStatus
+#define glFramebufferTexture1DEXT glFramebufferTexture1D
+#define glFramebufferTexture2DEXT glFramebufferTexture2D
+#define glFramebufferTexture3DEXT glFramebufferTexture3D
+#define glFramebufferRenderbufferEXT glFramebufferRenderbuffer
+#define glGetFramebufferAttachmentParameterivEXT glGetFramebufferAttachmentParameteriv
+#define glGenerateMipmapEXT glGenerateMipmap
+
+#endif // QT_OPENGL_ES_2
+
+#define glBindBufferARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBindBufferARB
+#define glDeleteBuffersARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteBuffersARB
+#define glGenBuffersARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenBuffersARB
+#define glBufferDataARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBufferDataARB
+#define glMapBufferARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glMapBufferARB
+#define glUnmapBufferARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUnmapBufferARB
+
+#define glCreateShader QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glCreateShader
+#define glShaderSource QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glShaderSource
+#define glCompileShader QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glCompileShader
+#define glDeleteShader QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteShader
+
+#define glCreateProgram QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glCreateProgram
+#define glAttachShader QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glAttachShader
+#define glDetachShader QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDetachShader
+#define glLinkProgram QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glLinkProgram
+#define glUseProgram QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUseProgram
+#define glDeleteProgram QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteProgram
+
+#define glGetShaderInfoLog QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetShaderInfoLog
+#define glGetShaderiv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetShaderiv
+#define glGetProgramiv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetProgramiv
+
+#define glGetUniformLocation QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetUniformLocation
+#define glUniform4fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniform4fv
+#define glUniform3fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniform3fv
+#define glUniform2fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniform2fv
+#define glUniform1fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniform1fv
+#define glUniform1i QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniform1i
+
+extern bool qt_resolve_framebufferobject_extensions(QGLContext *ctx);
+bool qt_resolve_buffer_extensions(QGLContext *ctx);
+
+bool qt_resolve_version_1_3_functions(QGLContext *ctx);
+bool qt_resolve_stencil_face_extension(QGLContext *ctx);
+bool qt_resolve_frag_program_extensions(QGLContext *ctx);
+
+bool qt_resolve_glsl_extensions(QGLContext *ctx);
+
+QT_END_NAMESPACE
+
+#endif // QGL_EXTENSIONS_P_H
diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp
new file mode 100644
index 0000000000..fb2227280a
--- /dev/null
+++ b/src/opengl/qglframebufferobject.cpp
@@ -0,0 +1,719 @@
+/****************************************************************************
+**
+** 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 "qglframebufferobject.h"
+
+#include <qdebug.h>
+#include <private/qgl_p.h>
+#include <private/qpaintengine_opengl_p.h>
+#include <qglframebufferobject.h>
+#include <qlibrary.h>
+#include <qimage.h>
+
+QT_BEGIN_NAMESPACE
+
+extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool);
+
+#define QGL_FUNC_CONTEXT QGLContext *ctx = d_ptr->ctx;
+
+#define QT_CHECK_GLERROR() \
+{ \
+ GLenum err = glGetError(); \
+ if (err != GL_NO_ERROR) { \
+ qDebug("[%s line %d] GL Error: %d", \
+ __FILE__, __LINE__, (int)err); \
+ } \
+}
+
+class QGLFramebufferObjectPrivate
+{
+public:
+ QGLFramebufferObjectPrivate() : depth_stencil_buffer(0), valid(false), bound(false), ctx(0) {}
+ ~QGLFramebufferObjectPrivate() {}
+
+ void init(const QSize& sz, QGLFramebufferObject::Attachment attachment,
+ GLenum internal_format, GLenum texture_target);
+ bool checkFramebufferStatus() const;
+ GLuint texture;
+ GLuint fbo;
+ GLuint depth_stencil_buffer;
+ GLenum target;
+ QSize size;
+ uint valid : 1;
+ uint bound : 1;
+ QGLFramebufferObject::Attachment fbo_attachment;
+ QGLContext *ctx; // for Windows extension ptrs
+};
+
+bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const
+{
+ GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ switch(status) {
+ case GL_NO_ERROR:
+ case GL_FRAMEBUFFER_COMPLETE_EXT:
+ return true;
+ break;
+ case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
+ qDebug("QGLFramebufferObject: Unsupported framebuffer format.");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete attachment.");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, missing attachment.");
+ break;
+#ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT
+ case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, duplicate attachment.");
+ break;
+#endif
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions.");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, attached images must have same format.");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, missing draw buffer.");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
+ qDebug("QGLFramebufferObject: Framebuffer incomplete, missing read buffer.");
+ break;
+ default:
+ qDebug() <<"QGLFramebufferObject: An undefined error has occurred: "<< status;
+ break;
+ }
+ return false;
+}
+
+void QGLFramebufferObjectPrivate::init(const QSize &sz, QGLFramebufferObject::Attachment attachment,
+ GLenum texture_target, GLenum internal_format)
+{
+ ctx = const_cast<QGLContext *>(QGLContext::currentContext());
+ bool ext_detected = (QGLExtensions::glExtensions & QGLExtensions::FramebufferObject);
+ if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx)))
+ return;
+
+ size = sz;
+ target = texture_target;
+ // texture dimensions
+
+ while (glGetError() != GL_NO_ERROR) {} // reset error state
+ glGenFramebuffersEXT(1, &fbo);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
+
+ QT_CHECK_GLERROR();
+ // init texture
+ glGenTextures(1, &texture);
+ glBindTexture(target, texture);
+ glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+#ifndef QT_OPENGL_ES
+ glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+#else
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+#endif
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ target, texture, 0);
+
+ QT_CHECK_GLERROR();
+ valid = checkFramebufferStatus();
+
+ if (attachment == QGLFramebufferObject::CombinedDepthStencil
+ && (QGLExtensions::glExtensions & QGLExtensions::PackedDepthStencil)) {
+ // depth and stencil buffer needs another extension
+ glGenRenderbuffersEXT(1, &depth_stencil_buffer);
+ Q_ASSERT(!glIsRenderbufferEXT(depth_stencil_buffer));
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_stencil_buffer);
+ Q_ASSERT(glIsRenderbufferEXT(depth_stencil_buffer));
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, size.width(), size.height());
+ GLint i = 0;
+ glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i);
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, depth_stencil_buffer);
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, depth_stencil_buffer);
+ fbo_attachment = QGLFramebufferObject::CombinedDepthStencil;
+ valid = checkFramebufferStatus();
+ if (!valid)
+ glDeleteRenderbuffersEXT(1, &depth_stencil_buffer);
+ } else if (attachment == QGLFramebufferObject::Depth
+ || attachment == QGLFramebufferObject::CombinedDepthStencil)
+ {
+ glGenRenderbuffersEXT(1, &depth_stencil_buffer);
+ Q_ASSERT(!glIsRenderbufferEXT(depth_stencil_buffer));
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_stencil_buffer);
+ Q_ASSERT(glIsRenderbufferEXT(depth_stencil_buffer));
+#ifdef QT_OPENGL_ES
+#define GL_DEPTH_COMPONENT16 0x81A5
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, size.width(), size.height());
+#else
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, size.width(), size.height());
+#endif
+ GLint i = 0;
+ glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i);
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, depth_stencil_buffer);
+ fbo_attachment = QGLFramebufferObject::Depth;
+ valid = checkFramebufferStatus();
+ if (!valid)
+ glDeleteRenderbuffersEXT(1, &depth_stencil_buffer);
+ } else {
+ fbo_attachment = QGLFramebufferObject::NoAttachment;
+ }
+
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ if (!valid) {
+ glDeleteTextures(1, &texture);
+ glDeleteFramebuffersEXT(1, &fbo);
+ }
+ QT_CHECK_GLERROR();
+}
+
+/*!
+ \class QGLFramebufferObject
+ \brief The QGLFramebufferObject class encapsulates an OpenGL framebuffer object.
+ \since 4.2
+
+ \ingroup multimedia
+
+ The QGLFramebufferObject class encapsulates an OpenGL framebuffer
+ object, defined by the \c{GL_EXT_framebuffer_object} extension. In
+ addition it provides a rendering surface that can be painted on
+ with a QPainter, rendered to using native GL calls, or both. This
+ surface can be bound and used as a regular texture in your own GL
+ drawing code. By default, the QGLFramebufferObject class
+ generates a 2D GL texture (using the \c{GL_TEXTURE_2D} target),
+ which is used as the internal rendering target.
+
+ \bold{It is important to have a current GL context when creating a
+ QGLFramebufferObject, otherwise initialization will fail.}
+
+ OpenGL framebuffer objects and pbuffers (see
+ \l{QGLPixelBuffer}{QGLPixelBuffer}) can both be used to render to
+ offscreen surfaces, but there are a number of advantages with
+ using framebuffer objects instead of pbuffers:
+
+ \list 1
+ \o A framebuffer object does not require a separate rendering
+ context, so no context switching will occur when switching
+ rendering targets. There is an overhead involved in switching
+ targets, but in general it is cheaper than a context switch to a
+ pbuffer.
+
+ \o Rendering to dynamic textures (i.e. render-to-texture
+ functionality) works on all platforms. No need to do explicit copy
+ calls from a render buffer into a texture, as was necessary on
+ systems that did not support the \c{render_texture} extension.
+
+ \o It is possible to attach several rendering buffers (or texture
+ objects) to the same framebuffer object, and render to all of them
+ without doing a context switch.
+
+ \o The OpenGL framebuffer extension is a pure GL extension with no
+ system dependant WGL, CGL, or GLX parts. This makes using
+ framebuffer objects more portable.
+ \endlist
+
+ Note that primitives drawn to a QGLFramebufferObject with QPainter
+ will only be antialiased if the QPainter::HighQualityAntialiasing
+ render hint is set. This is because there is currently no support
+ for the \c{GL_EXT_framebuffer_multisample} extension, which is
+ required to do multisample based antialiasing. Also note that the
+ QPainter::HighQualityAntialiasing render hint requires the
+ \c{GL_ARB_fragment_program} extension to work in OpenGL.
+
+ \sa {Framebuffer Object Example}
+*/
+
+
+/*!
+ \enum QGLFramebufferObject::Attachment
+ \since 4.3
+
+ This enum type is used to configure the depth and stencil buffers
+ attached to the framebuffer object when it is created.
+
+ \value NoAttachment No attachment is added to the framebuffer object. Note that the
+ OpenGL depth and stencil tests won't work when rendering to a
+ framebuffer object without any depth or stencil buffers.
+ This is the default value.
+
+ \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present,
+ a combined depth and stencil buffer is attached.
+ If the extension is not present, only a depth buffer is attached.
+
+ \value Depth A depth buffer is attached to the framebuffer object.
+
+ \sa attachment()
+*/
+
+
+/*! \fn QGLFramebufferObject::QGLFramebufferObject(const QSize &size, GLenum target)
+
+ Constructs an OpenGL framebuffer object and binds a 2D GL texture
+ to the buffer of the size \a size. The texture is bound to the
+ \c GL_COLOR_ATTACHMENT0 target in the framebuffer object.
+
+ The \a target parameter is used to specify the GL texture
+ target. The default target is \c GL_TEXTURE_2D. Keep in mind that
+ \c GL_TEXTURE_2D textures must have a power of 2 width and height
+ (e.g. 256x512), unless you are using OpenGL 2.0 or higher.
+
+ By default, no depth and stencil buffers are attached. This behavior
+ can be toggled using one of the overloaded constructors.
+
+ The default internal texture format is \c GL_RGBA8.
+
+ It is important that you have a current GL context set when
+ creating the QGLFramebufferObject, otherwise the initialization
+ will fail.
+
+ \sa size(), texture(), attachment()
+*/
+
+#ifndef QT_OPENGL_ES
+#define DEFAULT_FORMAT GL_RGBA8
+#else
+#define DEFAULT_FORMAT GL_RGBA
+#endif
+
+QGLFramebufferObject::QGLFramebufferObject(const QSize &size, GLenum target)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(size, NoAttachment, target, DEFAULT_FORMAT);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+QGLFramebufferObject::QGLFramebufferObject(const QSize &size, QMacCompatGLenum target)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(size, NoAttachment, target, DEFAULT_FORMAT);
+}
+#endif
+
+/*! \overload
+
+ Constructs an OpenGL framebuffer object and binds a 2D GL texture
+ to the buffer of the given \a width and \a height.
+
+ \sa size(), texture()
+*/
+QGLFramebufferObject::QGLFramebufferObject(int width, int height, GLenum target)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(QSize(width, height), NoAttachment, target, DEFAULT_FORMAT);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+QGLFramebufferObject::QGLFramebufferObject(int width, int height, QMacCompatGLenum target)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(QSize(width, height), NoAttachment, target, DEFAULT_FORMAT);
+}
+#endif
+
+/*! \overload
+
+ Constructs an OpenGL framebuffer object and binds a texture to the
+ buffer of the given \a width and \a height.
+
+ The \a attachment parameter describes the depth/stencil buffer
+ configuration, \a target the texture target and \a internal_format
+ the internal texture format. The default texture target is \c
+ GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8.
+
+ \sa size(), texture(), attachment()
+*/
+QGLFramebufferObject::QGLFramebufferObject(int width, int height, Attachment attachment,
+ GLenum target, GLenum internal_format)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(QSize(width, height), attachment, target, internal_format);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+QGLFramebufferObject::QGLFramebufferObject(int width, int height, Attachment attachment,
+ QMacCompatGLenum target, QMacCompatGLenum internal_format)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(QSize(width, height), attachment, target, internal_format);
+}
+#endif
+
+/*! \overload
+
+ Constructs an OpenGL framebuffer object and binds a texture to the
+ buffer of the given \a size.
+
+ The \a attachment parameter describes the depth/stencil buffer
+ configuration, \a target the texture target and \a internal_format
+ the internal texture format. The default texture target is \c
+ GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8.
+
+ \sa size(), texture(), attachment()
+*/
+QGLFramebufferObject::QGLFramebufferObject(const QSize &size, Attachment attachment,
+ GLenum target, GLenum internal_format)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(size, attachment, target, internal_format);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+QGLFramebufferObject::QGLFramebufferObject(const QSize &size, Attachment attachment,
+ QMacCompatGLenum target, QMacCompatGLenum internal_format)
+ : d_ptr(new QGLFramebufferObjectPrivate)
+{
+ Q_D(QGLFramebufferObject);
+ d->init(size, attachment, target, internal_format);
+}
+#endif
+
+/*!
+ \fn QGLFramebufferObject::~QGLFramebufferObject()
+
+ Destroys the framebuffer object and frees any allocated resources.
+*/
+QGLFramebufferObject::~QGLFramebufferObject()
+{
+ Q_D(QGLFramebufferObject);
+ QGL_FUNC_CONTEXT;
+
+ if (isValid()
+ && (d->ctx == QGLContext::currentContext()
+ || qgl_share_reg()->checkSharing(d->ctx, QGLContext::currentContext())))
+ {
+ glDeleteTextures(1, &d->texture);
+ if (d->depth_stencil_buffer)
+ glDeleteRenderbuffersEXT(1, &d->depth_stencil_buffer);
+ glDeleteFramebuffersEXT(1, &d->fbo);
+ }
+ delete d_ptr;
+}
+
+/*!
+ \fn bool QGLFramebufferObject::isValid() const
+
+ Returns true if the framebuffer object is valid.
+
+ The framebuffer can become invalid if the initialization process
+ fails, the user attaches an invalid buffer to the framebuffer
+ object, or a non-power of 2 width/height is specified as the
+ texture size if the texture target is \c{GL_TEXTURE_2D}.
+*/
+bool QGLFramebufferObject::isValid() const
+{
+ Q_D(const QGLFramebufferObject);
+ return d->valid;
+}
+
+/*!
+ \fn bool QGLFramebufferObject::bind()
+
+ Switches rendering from the default, windowing system provided
+ framebuffer to this framebuffer object.
+ Returns true upon success, false otherwise.
+*/
+bool QGLFramebufferObject::bind()
+{
+ if (!isValid())
+ return false;
+ Q_D(QGLFramebufferObject);
+ QGL_FUNC_CONTEXT;
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, d->fbo);
+ d->bound = d->valid = d->checkFramebufferStatus();
+ return d->valid;
+}
+
+/*!
+ \fn bool QGLFramebufferObject::release()
+
+ Switches rendering back to the default, windowing system provided
+ framebuffer.
+ Returns true upon success, false otherwise.
+*/
+bool QGLFramebufferObject::release()
+{
+ if (!isValid())
+ return false;
+ Q_D(QGLFramebufferObject);
+ QGL_FUNC_CONTEXT;
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ d->valid = d->checkFramebufferStatus();
+ d->bound = false;
+ return d->valid;
+}
+
+/*!
+ \fn GLuint QGLFramebufferObject::texture() const
+
+ Returns the texture id for the texture attached as the default
+ rendering target in this framebuffer object. This texture id can
+ be bound as a normal texture in your own GL code.
+*/
+GLuint QGLFramebufferObject::texture() const
+{
+ Q_D(const QGLFramebufferObject);
+ return d->texture;
+}
+
+/*!
+ \fn QSize QGLFramebufferObject::size() const
+
+ Returns the size of the texture attached to this framebuffer
+ object.
+*/
+QSize QGLFramebufferObject::size() const
+{
+ Q_D(const QGLFramebufferObject);
+ return d->size;
+}
+
+/*!
+ \fn QImage QGLFramebufferObject::toImage() const
+
+ Returns the contents of this framebuffer object as a QImage.
+*/
+QImage QGLFramebufferObject::toImage() const
+{
+ Q_D(const QGLFramebufferObject);
+ if (!d->valid)
+ return QImage();
+
+ const_cast<QGLFramebufferObject *>(this)->bind();
+ QImage image = qt_gl_read_framebuffer(d->size, d->ctx->format().alpha(), true);
+ const_cast<QGLFramebufferObject *>(this)->release();
+
+ return image;
+}
+
+#if !defined(QT_OPENGL_ES_2)
+Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_paintengine)
+#endif
+
+/*! \reimp */
+QPaintEngine *QGLFramebufferObject::paintEngine() const
+{
+#if !defined(QT_OPENGL_ES_2)
+ return qt_buffer_paintengine();
+#else
+ return 0;
+#endif
+}
+
+/*!
+ \fn bool QGLFramebufferObject::hasOpenGLFramebufferObjects()
+
+ Returns true if the OpenGL \c{GL_EXT_framebuffer_object} extension
+ is present on this system; otherwise returns false.
+*/
+bool QGLFramebufferObject::hasOpenGLFramebufferObjects()
+{
+ QGLWidget dmy; // needed to detect and init the QGLExtensions object
+ return (QGLExtensions::glExtensions & QGLExtensions::FramebufferObject);
+}
+
+/*!
+ \since 4.4
+
+ Draws the given texture, \a textureId, to the given target rectangle,
+ \a target, in OpenGL model space. The \a textureTarget should be a 2D
+ texture target.
+
+ The framebuffer object should be bound when calling this function.
+
+ Equivalent to the corresponding QGLContext::drawTexture().
+*/
+void QGLFramebufferObject::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
+{
+ Q_D(QGLFramebufferObject);
+ d->ctx->drawTexture(target, textureId, textureTarget);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLFramebufferObject::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ Q_D(QGLFramebufferObject);
+ d->ctx->drawTexture(target, textureId, textureTarget);
+}
+#endif
+
+/*!
+ \since 4.4
+
+ Draws the given texture, \a textureId, at the given \a point in OpenGL
+ model space. The \a textureTarget should be a 2D texture target.
+
+ The framebuffer object should be bound when calling this function.
+
+ Equivalent to the corresponding QGLContext::drawTexture().
+*/
+void QGLFramebufferObject::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
+{
+ Q_D(QGLFramebufferObject);
+ d->ctx->drawTexture(point, textureId, textureTarget);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLFramebufferObject::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ Q_D(QGLFramebufferObject);
+ d->ctx->drawTexture(point, textureId, textureTarget);
+}
+#endif
+
+extern int qt_defaultDpi();
+
+/*! \reimp */
+int QGLFramebufferObject::metric(PaintDeviceMetric metric) const
+{
+ Q_D(const QGLFramebufferObject);
+
+ float dpmx = qt_defaultDpi()*100./2.54;
+ float dpmy = qt_defaultDpi()*100./2.54;
+ int w = d->size.width();
+ int h = d->size.height();
+ switch (metric) {
+ case PdmWidth:
+ return w;
+
+ case PdmHeight:
+ return h;
+
+ case PdmWidthMM:
+ return qRound(w * 1000 / dpmx);
+
+ case PdmHeightMM:
+ return qRound(h * 1000 / dpmy);
+
+ case PdmNumColors:
+ return 0;
+
+ case PdmDepth:
+ return 32;//d->depth;
+
+ case PdmDpiX:
+ return (int)(dpmx * 0.0254);
+
+ case PdmDpiY:
+ return (int)(dpmy * 0.0254);
+
+ case PdmPhysicalDpiX:
+ return (int)(dpmx * 0.0254);
+
+ case PdmPhysicalDpiY:
+ return (int)(dpmy * 0.0254);
+
+ default:
+ qWarning("QGLFramebufferObject::metric(), Unhandled metric type: %d.\n", metric);
+ break;
+ }
+ return 0;
+}
+
+/*!
+ \fn GLuint QGLFramebufferObject::handle() const
+
+ Returns the GL framebuffer object handle for this framebuffer
+ object (returned by the \c{glGenFrameBuffersEXT()} function). This
+ handle can be used to attach new images or buffers to the
+ framebuffer. The user is responsible for cleaning up and
+ destroying these objects.
+*/
+GLuint QGLFramebufferObject::handle() const
+{
+ Q_D(const QGLFramebufferObject);
+ return d->fbo;
+}
+
+/*! \fn int QGLFramebufferObject::devType() const
+
+ \reimp
+*/
+
+
+/*!
+ Returns the status of the depth and stencil buffers attached to
+ this framebuffer object.
+*/
+
+QGLFramebufferObject::Attachment QGLFramebufferObject::attachment() const
+{
+ Q_D(const QGLFramebufferObject);
+ if (d->valid)
+ return d->fbo_attachment;
+ return NoAttachment;
+}
+
+/*!
+ \since 4.5
+
+ Returns true if the framebuffer object is currently bound to a context,
+ otherwise false is returned.
+*/
+
+bool QGLFramebufferObject::isBound() const
+{
+ Q_D(const QGLFramebufferObject);
+ return d->bound;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglframebufferobject.h b/src/opengl/qglframebufferobject.h
new file mode 100644
index 0000000000..a9e1b2fd92
--- /dev/null
+++ b/src/opengl/qglframebufferobject.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** 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 QGLFRAMEBUFFEROBJECT_H
+#define QGLFRAMEBUFFEROBJECT_H
+
+#include <QtOpenGL/qgl.h>
+#include <QtGui/qpaintdevice.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLFramebufferObjectPrivate;
+
+class Q_OPENGL_EXPORT QGLFramebufferObject : public QPaintDevice
+{
+ Q_DECLARE_PRIVATE(QGLFramebufferObject)
+public:
+ enum Attachment {
+ NoAttachment,
+ CombinedDepthStencil,
+ Depth
+ };
+
+ QGLFramebufferObject(const QSize &size, GLenum target = GL_TEXTURE_2D);
+ QGLFramebufferObject(int width, int height, GLenum target = GL_TEXTURE_2D);
+#if !defined(QT_OPENGL_ES) || defined(Q_QDOC)
+ QGLFramebufferObject(const QSize &size, Attachment attachment,
+ GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA8);
+ QGLFramebufferObject(int width, int height, Attachment attachment,
+ GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA8);
+#else
+ QGLFramebufferObject(const QSize &size, Attachment attachment,
+ GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA);
+ QGLFramebufferObject(int width, int height, Attachment attachment,
+ GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA);
+#endif
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ QGLFramebufferObject(const QSize &size, QMacCompatGLenum target = GL_TEXTURE_2D);
+ QGLFramebufferObject(int width, int height, QMacCompatGLenum target = GL_TEXTURE_2D);
+
+ QGLFramebufferObject(const QSize &size, Attachment attachment,
+ QMacCompatGLenum target = GL_TEXTURE_2D, QMacCompatGLenum internal_format = GL_RGBA8);
+ QGLFramebufferObject(int width, int height, Attachment attachment,
+ QMacCompatGLenum target = GL_TEXTURE_2D, QMacCompatGLenum internal_format = GL_RGBA8);
+#endif
+
+ virtual ~QGLFramebufferObject();
+
+ bool isValid() const;
+ bool isBound() const;
+ bool bind();
+ bool release();
+ GLuint texture() const;
+ QSize size() const;
+ QImage toImage() const;
+ Attachment attachment() const;
+
+ QPaintEngine *paintEngine() const;
+ GLuint handle() const;
+
+ static bool hasOpenGLFramebufferObjects();
+
+ void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ void drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+#endif
+
+protected:
+ int metric(PaintDeviceMetric metric) const;
+ int devType() const { return QInternal::FramebufferObject; }
+
+private:
+ Q_DISABLE_COPY(QGLFramebufferObject)
+ QGLFramebufferObjectPrivate *d_ptr;
+ friend class QGLDrawable;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+#endif // QGLFRAMEBUFFEROBJECT_H
diff --git a/src/opengl/qglpaintdevice_qws.cpp b/src/opengl/qglpaintdevice_qws.cpp
new file mode 100644
index 0000000000..18905ab49d
--- /dev/null
+++ b/src/opengl/qglpaintdevice_qws.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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/qglpaintdevice_qws_p.h>
+#include <private/qgl_p.h>
+#include <private/qpaintengine_opengl_p.h>
+#include <private/qglwindowsurface_qws_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWSGLPaintDevicePrivate
+{
+public:
+ QWidget *widget;
+};
+
+class QMetricAccessor : public QWidget {
+public:
+ int metric(PaintDeviceMetric m) {
+ return QWidget::metric(m);
+ }
+};
+
+QWSGLPaintDevice::QWSGLPaintDevice(QWidget *widget) :
+ d_ptr(new QWSGLPaintDevicePrivate)
+{
+ Q_D(QWSGLPaintDevice);
+ d->widget = widget;
+}
+
+QWSGLPaintDevice::~QWSGLPaintDevice()
+{
+ Q_D(QWSGLPaintDevice);
+ delete d;
+}
+
+QPaintEngine* QWSGLPaintDevice::paintEngine() const
+{
+#if !defined(QT_OPENGL_ES_2)
+ return qt_qgl_paint_engine();
+#else
+ return 0; // XXX
+#endif
+}
+
+int QWSGLPaintDevice::metric(PaintDeviceMetric m) const
+{
+ Q_D(const QWSGLPaintDevice);
+ Q_ASSERT(d->widget);
+
+ return ((QMetricAccessor *) d->widget)->metric(m);
+}
+
+QWSGLWindowSurface* QWSGLPaintDevice::windowSurface() const
+{
+ Q_D(const QWSGLPaintDevice);
+ return static_cast<QWSGLWindowSurface*>(d->widget->windowSurface());
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpaintdevice_qws_p.h b/src/opengl/qglpaintdevice_qws_p.h
new file mode 100644
index 0000000000..369de7f408
--- /dev/null
+++ b/src/opengl/qglpaintdevice_qws_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** 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 QWSGLPAINTDEVICE_GL_P_H
+#define QWSGLPAINTDEVICE_GL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QGLWindowSurface class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QPaintDevice>
+
+QT_BEGIN_NAMESPACE
+
+class QWidget;
+class QWSGLWindowSurface;
+class QWSGLPaintDevicePrivate;
+
+class Q_OPENGL_EXPORT QWSGLPaintDevice : public QPaintDevice
+{
+ Q_DECLARE_PRIVATE(QWSGLPaintDevice)
+public:
+ QWSGLPaintDevice(QWidget *widget);
+ ~QWSGLPaintDevice();
+
+ QPaintEngine *paintEngine() const;
+
+ int metric(PaintDeviceMetric m) const;
+
+ QWSGLWindowSurface *windowSurface() const;
+
+private:
+ friend class QWSGLWindowSurface;
+ QWSGLPaintDevicePrivate *d_ptr;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QWSGLPAINTDEVICE_GL_P_H
diff --git a/src/opengl/qglpixelbuffer.cpp b/src/opengl/qglpixelbuffer.cpp
new file mode 100644
index 0000000000..5f74f26c91
--- /dev/null
+++ b/src/opengl/qglpixelbuffer.cpp
@@ -0,0 +1,582 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \class QGLPixelBuffer
+ \brief The QGLPixelBuffer class encapsulates an OpenGL pbuffer.
+ \since 4.1
+
+ \ingroup multimedia
+
+ Rendering into a pbuffer is normally done using full hardware
+ acceleration. This can be significantly faster than rendering
+ into a QPixmap.
+
+ There are three approaches to using this class:
+
+ \list 1
+ \o \bold{We can draw into the pbuffer and convert it to a QImage
+ using toImage().} This is normally much faster than calling
+ QGLWidget::renderPixmap().
+
+ \o \bold{We can draw into the pbuffer and copy the contents into
+ an OpenGL texture using updateDynamicTexture().} This allows
+ us to create dynamic textures and works on all systems
+ with pbuffer support.
+
+ \o \bold{On systems that support it, we can bind the pbuffer to
+ an OpenGL texture.} The texture is then updated automatically
+ when the pbuffer contents change, eliminating the need for
+ additional copy operations. This is supported only on Windows
+ and Mac OS X systems that provide the \c render_texture
+ extension.
+ \endlist
+
+ Pbuffers are provided by the OpenGL \c pbuffer extension; call
+ hasOpenGLPbuffer() to find out if the system provides pbuffers.
+
+ \sa {opengl/pbuffers}{Pbuffers Example}
+*/
+
+
+#include <qglpixelbuffer.h>
+#include <private/qglpixelbuffer_p.h>
+#include <qimage.h>
+
+#if !defined(QT_OPENGL_ES_2)
+#include <private/qpaintengine_opengl_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_OPENGL_ES_2)
+extern void qgl_cleanup_glyph_cache(QGLContext *);
+#else
+void qgl_cleanup_glyph_cache(QGLContext *) {}
+#endif
+
+extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool);
+
+void QGLPixelBufferPrivate::common_init(const QSize &size, const QGLFormat &format, QGLWidget *shareWidget)
+{
+ Q_Q(QGLPixelBuffer);
+ if(init(size, format, shareWidget)) {
+ req_size = size;
+ req_format = format;
+ req_shareWidget = shareWidget;
+ invalid = false;
+ qctx = new QGLContext(format);
+ qctx->d_func()->sharing = (shareWidget != 0);
+ if (shareWidget != 0 && shareWidget->d_func()->glcx)
+ qgl_share_reg()->addShare(qctx, shareWidget->d_func()->glcx);
+
+ qctx->d_func()->paintDevice = q;
+ qctx->d_func()->valid = true;
+#if defined(Q_WS_WIN) && !defined(QT_OPENGL_ES)
+ qctx->d_func()->dc = dc;
+ qctx->d_func()->rc = ctx;
+#elif (defined(Q_WS_X11) && !defined(QT_OPENGL_ES))
+ qctx->d_func()->cx = ctx;
+ qctx->d_func()->pbuf = (void *) pbuf;
+ qctx->d_func()->vi = 0;
+#elif defined(Q_WS_MAC)
+ qctx->d_func()->cx = ctx;
+ qctx->d_func()->vi = 0;
+#elif defined(QT_OPENGL_ES)
+ qctx->d_func()->eglContext = ctx;
+#endif
+ }
+}
+
+/*!
+ Constructs an OpenGL pbuffer of the given \a size. If no \a
+ format is specified, the \l{QGLFormat::defaultFormat()}{default
+ format} is used. If the \a shareWidget parameter points to a
+ valid QGLWidget, the pbuffer will share its context with \a
+ shareWidget.
+
+ If you intend to bind this pbuffer as a dynamic texture, the width
+ and height components of \c size must be powers of two (e.g., 512
+ x 128).
+
+ \sa size(), format()
+*/
+QGLPixelBuffer::QGLPixelBuffer(const QSize &size, const QGLFormat &format, QGLWidget *shareWidget)
+ : d_ptr(new QGLPixelBufferPrivate(this))
+{
+ Q_D(QGLPixelBuffer);
+ d->common_init(size, format, shareWidget);
+}
+
+
+/*! \overload
+
+ Constructs an OpenGL pbuffer with the \a width and \a height. If
+ no \a format is specified, the
+ \l{QGLFormat::defaultFormat()}{default format} is used. If the \a
+ shareWidget parameter points to a valid QGLWidget, the pbuffer
+ will share its context with \a shareWidget.
+
+ If you intend to bind this pbuffer as a dynamic texture, the width
+ and height components of \c size must be powers of two (e.g., 512
+ x 128).
+
+ \sa size(), format()
+*/
+QGLPixelBuffer::QGLPixelBuffer(int width, int height, const QGLFormat &format, QGLWidget *shareWidget)
+ : d_ptr(new QGLPixelBufferPrivate(this))
+{
+ Q_D(QGLPixelBuffer);
+ d->common_init(QSize(width, height), format, shareWidget);
+}
+
+
+/*! \fn QGLPixelBuffer::~QGLPixelBuffer()
+
+ Destroys the pbuffer and frees any allocated resources.
+*/
+QGLPixelBuffer::~QGLPixelBuffer()
+{
+ Q_D(QGLPixelBuffer);
+
+ // defined in qpaintengine_opengl.cpp
+ QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext());
+ if (current != d->qctx)
+ makeCurrent();
+ qgl_cleanup_glyph_cache(d->qctx);
+ d->cleanup();
+ delete d->qctx;
+ if (current && current != d->qctx)
+ current->makeCurrent();
+ delete d_ptr;
+}
+
+/*! \fn bool QGLPixelBuffer::makeCurrent()
+
+ Makes this pbuffer the current OpenGL rendering context. Returns
+ true on success; otherwise returns false.
+
+ \sa QGLContext::makeCurrent(), doneCurrent()
+*/
+
+bool QGLPixelBuffer::makeCurrent()
+{
+ Q_D(QGLPixelBuffer);
+ if (d->invalid)
+ return false;
+ d->qctx->makeCurrent();
+ return true;
+}
+
+/*! \fn bool QGLPixelBuffer::doneCurrent()
+
+ Makes no context the current OpenGL context. Returns true on
+ success; otherwise returns false.
+*/
+
+bool QGLPixelBuffer::doneCurrent()
+{
+ Q_D(QGLPixelBuffer);
+ if (d->invalid)
+ return false;
+ d->qctx->doneCurrent();
+ return true;
+}
+
+/*!
+ Generates and binds a 2D GL texture that is the same size as the
+ pbuffer, and returns the texture's ID. This can be used in
+ conjunction with bindToDynamicTexture() and
+ updateDynamicTexture().
+
+ \sa size()
+*/
+
+#if (defined(Q_WS_X11) || defined(Q_WS_WIN)) && !defined(QT_OPENGL_ES)
+GLuint QGLPixelBuffer::generateDynamicTexture() const
+{
+ Q_D(const QGLPixelBuffer);
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, d->req_size.width(), d->req_size.height(), 0, GL_RGBA, GL_FLOAT, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ return texture;
+}
+#endif
+
+/*! \fn bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id)
+
+ Binds the texture specified by \a texture_id to this pbuffer.
+ Returns true on success; otherwise returns false.
+
+ The texture must be of the same size and format as the pbuffer.
+
+ To unbind the texture, call releaseFromDynamicTexture(). While
+ the texture is bound, it is updated automatically when the
+ pbuffer contents change, eliminating the need for additional copy
+ operations.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_opengl_qglpixelbuffer.cpp 0
+
+ \warning This function uses the \c {render_texture} extension,
+ which is currently not supported under X11. An alternative that
+ works on all systems (including X11) is to manually copy the
+ pbuffer contents to a texture using updateDynamicTexture().
+
+ \warning For the bindToDynamicTexture() call to succeed on the
+ Mac OS X, the pbuffer needs a shared context, i.e. the
+ QGLPixelBuffer must be created with a share widget.
+
+ \sa generateDynamicTexture(), releaseFromDynamicTexture()
+*/
+
+/*! \fn void QGLPixelBuffer::releaseFromDynamicTexture()
+
+ Releases the pbuffer from any previously bound texture.
+
+ \sa bindToDynamicTexture()
+*/
+
+/*! \fn bool QGLPixelBuffer::hasOpenGLPbuffers()
+
+ Returns true if the OpenGL \c pbuffer extension is present on
+ this system; otherwise returns false.
+*/
+
+/*!
+ Copies the pbuffer contents into the texture specified with \a
+ texture_id.
+
+ The texture must be of the same size and format as the pbuffer.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_opengl_qglpixelbuffer.cpp 1
+
+ An alternative on Windows and Mac OS X systems that support the
+ \c render_texture extension is to use bindToDynamicTexture() to
+ get dynamic updates of the texture.
+
+ \sa generateDynamicTexture(), bindToDynamicTexture()
+*/
+void QGLPixelBuffer::updateDynamicTexture(GLuint texture_id) const
+{
+ Q_D(const QGLPixelBuffer);
+ if (d->invalid)
+ return;
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+#ifndef QT_OPENGL_ES
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, d->req_size.width(), d->req_size.height(), 0);
+#else
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, d->req_size.width(), d->req_size.height(), 0);
+#endif
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+void QGLPixelBuffer::updateDynamicTexture(QMacCompatGLuint texture_id) const
+{
+ updateDynamicTexture(GLuint(texture_id));
+}
+#endif
+
+/*!
+ Returns the size of the pbuffer.
+*/
+QSize QGLPixelBuffer::size() const
+{
+ Q_D(const QGLPixelBuffer);
+ return d->req_size;
+}
+
+/*!
+ Returns the contents of the pbuffer as a QImage.
+*/
+QImage QGLPixelBuffer::toImage() const
+{
+ Q_D(const QGLPixelBuffer);
+ if (d->invalid)
+ return QImage();
+
+ const_cast<QGLPixelBuffer *>(this)->makeCurrent();
+ return qt_gl_read_framebuffer(d->req_size, d->format.alpha(), true);
+}
+
+/*!
+ Returns the native pbuffer handle.
+*/
+Qt::HANDLE QGLPixelBuffer::handle() const
+{
+ Q_D(const QGLPixelBuffer);
+ if (d->invalid)
+ return 0;
+ return (Qt::HANDLE) d->pbuf;
+}
+
+/*!
+ Returns true if this pbuffer is valid; otherwise returns false.
+*/
+bool QGLPixelBuffer::isValid() const
+{
+ Q_D(const QGLPixelBuffer);
+ return !d->invalid;
+}
+
+#if !defined(QT_OPENGL_ES_2)
+Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_paintengine)
+#endif
+
+/*! \reimp */
+QPaintEngine *QGLPixelBuffer::paintEngine() const
+{
+#if !defined(QT_OPENGL_ES_2)
+ return qt_buffer_paintengine();
+#else
+ return 0;
+#endif
+}
+
+extern int qt_defaultDpi();
+
+/*! \reimp */
+int QGLPixelBuffer::metric(PaintDeviceMetric metric) const
+{
+ Q_D(const QGLPixelBuffer);
+
+ float dpmx = qt_defaultDpi()*100./2.54;
+ float dpmy = qt_defaultDpi()*100./2.54;
+ int w = d->req_size.width();
+ int h = d->req_size.height();
+ switch (metric) {
+ case PdmWidth:
+ return w;
+
+ case PdmHeight:
+ return h;
+
+ case PdmWidthMM:
+ return qRound(w * 1000 / dpmx);
+
+ case PdmHeightMM:
+ return qRound(h * 1000 / dpmy);
+
+ case PdmNumColors:
+ return 0;
+
+ case PdmDepth:
+ return 32;//d->depth;
+
+ case PdmDpiX:
+ return (int)(dpmx * 0.0254);
+
+ case PdmDpiY:
+ return (int)(dpmy * 0.0254);
+
+ case PdmPhysicalDpiX:
+ return (int)(dpmx * 0.0254);
+
+ case PdmPhysicalDpiY:
+ return (int)(dpmy * 0.0254);
+
+ default:
+ qWarning("QGLPixelBuffer::metric(), Unhandled metric type: %d\n", metric);
+ break;
+ }
+ return 0;
+}
+
+/*!
+ Generates and binds a 2D GL texture to the current context, based
+ on \a image. The generated texture id is returned and can be used
+ in later glBindTexture() calls.
+
+ The \a target parameter specifies the texture target.
+
+ Equivalent to calling QGLContext::bindTexture().
+
+ \sa deleteTexture()
+*/
+GLuint QGLPixelBuffer::bindTexture(const QImage &image, GLenum target)
+{
+ Q_D(QGLPixelBuffer);
+#ifndef QT_OPENGL_ES
+ return d->qctx->bindTexture(image, target, GLint(GL_RGBA8));
+#else
+ return d->qctx->bindTexture(image, target, GL_RGBA);
+#endif
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+GLuint QGLPixelBuffer::bindTexture(const QImage &image, QMacCompatGLenum target)
+{
+ Q_D(QGLPixelBuffer);
+ return d->qctx->bindTexture(image, target, QMacCompatGLint(GL_RGBA8));
+}
+#endif
+
+/*! \overload
+
+ Generates and binds a 2D GL texture based on \a pixmap.
+
+ Equivalent to calling QGLContext::bindTexture().
+
+ \sa deleteTexture()
+*/
+GLuint QGLPixelBuffer::bindTexture(const QPixmap &pixmap, GLenum target)
+{
+ Q_D(QGLPixelBuffer);
+#ifndef QT_OPENGL_ES
+ return d->qctx->bindTexture(pixmap, target, GLint(GL_RGBA8));
+#else
+ return d->qctx->bindTexture(pixmap, target, GL_RGBA);
+#endif
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+GLuint QGLPixelBuffer::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target)
+{
+ Q_D(QGLPixelBuffer);
+ return d->qctx->bindTexture(pixmap, target, QMacCompatGLint(GL_RGBA8));
+}
+#endif
+
+/*! \overload
+
+ Reads the DirectDrawSurface (DDS) compressed file \a fileName and
+ generates a 2D GL texture from it.
+
+ Equivalent to calling QGLContext::bindTexture().
+
+ \sa deleteTexture()
+*/
+GLuint QGLPixelBuffer::bindTexture(const QString &fileName)
+{
+ Q_D(QGLPixelBuffer);
+ return d->qctx->bindTexture(fileName);
+}
+
+/*!
+ Removes the texture identified by \a texture_id from the texture cache.
+
+ Equivalent to calling QGLContext::deleteTexture().
+ */
+void QGLPixelBuffer::deleteTexture(GLuint texture_id)
+{
+ Q_D(QGLPixelBuffer);
+ d->qctx->deleteTexture(texture_id);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLPixelBuffer::deleteTexture(QMacCompatGLuint texture_id)
+{
+ Q_D(QGLPixelBuffer);
+ d->qctx->deleteTexture(texture_id);
+}
+#endif
+
+/*!
+ \since 4.4
+
+ Draws the given texture, \a textureId, to the given target rectangle,
+ \a target, in OpenGL model space. The \a textureTarget should be a 2D
+ texture target.
+
+ Equivalent to the corresponding QGLContext::drawTexture().
+*/
+void QGLPixelBuffer::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
+{
+ Q_D(QGLPixelBuffer);
+ d->qctx->drawTexture(target, textureId, textureTarget);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLPixelBuffer::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ Q_D(QGLPixelBuffer);
+ d->qctx->drawTexture(target, textureId, textureTarget);
+}
+#endif
+
+/*!
+ \since 4.4
+
+ Draws the given texture, \a textureId, at the given \a point in OpenGL model
+ space. The textureTarget parameter should be a 2D texture target.
+
+ Equivalent to the corresponding QGLContext::drawTexture().
+*/
+void QGLPixelBuffer::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
+{
+ Q_D(QGLPixelBuffer);
+ d->qctx->drawTexture(point, textureId, textureTarget);
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+/*! \internal */
+void QGLPixelBuffer::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
+{
+ Q_D(QGLPixelBuffer);
+ d->qctx->drawTexture(point, textureId, textureTarget);
+}
+#endif
+
+/*!
+ Returns the format of the pbuffer. The format may be different
+ from the one that was requested.
+*/
+QGLFormat QGLPixelBuffer::format() const
+{
+ Q_D(const QGLPixelBuffer);
+ return d->format;
+}
+
+/*! \fn int QGLPixelBuffer::devType() const
+ \reimp
+*/
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixelbuffer.h b/src/opengl/qglpixelbuffer.h
new file mode 100644
index 0000000000..0131570320
--- /dev/null
+++ b/src/opengl/qglpixelbuffer.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** 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 QGLPIXELBUFFER_H
+#define QGLPIXELBUFFER_H
+
+#include <QtOpenGL/qgl.h>
+#include <QtGui/qpaintdevice.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLPixelBufferPrivate;
+
+class Q_OPENGL_EXPORT QGLPixelBuffer : public QPaintDevice
+{
+ Q_DECLARE_PRIVATE(QGLPixelBuffer)
+public:
+ QGLPixelBuffer(const QSize &size, const QGLFormat &format = QGLFormat::defaultFormat(),
+ QGLWidget *shareWidget = 0);
+ QGLPixelBuffer(int width, int height, const QGLFormat &format = QGLFormat::defaultFormat(),
+ QGLWidget *shareWidget = 0);
+ virtual ~QGLPixelBuffer();
+
+ bool isValid() const;
+ bool makeCurrent();
+ bool doneCurrent();
+
+ GLuint generateDynamicTexture() const;
+ bool bindToDynamicTexture(GLuint texture);
+ void releaseFromDynamicTexture();
+ void updateDynamicTexture(GLuint texture_id) const;
+
+ GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D);
+ GLuint bindTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D);
+ GLuint bindTexture(const QString &fileName);
+ void deleteTexture(GLuint texture_id);
+
+ void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ bool bindToDynamicTexture(QMacCompatGLuint texture);
+ void updateDynamicTexture(QMacCompatGLuint texture_id) const;
+ GLuint bindTexture(const QImage &image, QMacCompatGLenum target = GL_TEXTURE_2D);
+ GLuint bindTexture(const QPixmap &pixmap, QMacCompatGLenum target = GL_TEXTURE_2D);
+
+ void drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+ void drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D);
+
+ void deleteTexture(QMacCompatGLuint texture_id);
+#endif
+
+ QSize size() const;
+ Qt::HANDLE handle() const;
+ QImage toImage() const;
+
+ QPaintEngine *paintEngine() const;
+ QGLFormat format() const;
+
+ static bool hasOpenGLPbuffers();
+
+protected:
+ int metric(PaintDeviceMetric metric) const;
+ int devType() const { return QInternal::Pbuffer; }
+
+private:
+ Q_DISABLE_COPY(QGLPixelBuffer)
+ QGLPixelBufferPrivate *d_ptr;
+ friend class QGLDrawable;
+ friend class QGLWindowSurface;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGLPIXELBUFFER_H
diff --git a/src/opengl/qglpixelbuffer_egl.cpp b/src/opengl/qglpixelbuffer_egl.cpp
new file mode 100644
index 0000000000..964efa2cf0
--- /dev/null
+++ b/src/opengl/qglpixelbuffer_egl.cpp
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** 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 <qdebug.h>
+#include "qglpixelbuffer.h"
+#include "qglpixelbuffer_p.h"
+#include "qgl_egl_p.h"
+
+#include <qimage.h>
+#include <private/qgl_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef EGL_BIND_TO_TEXTURE_RGBA
+#define QGL_RENDER_TEXTURE 1
+#else
+#define QGL_RENDER_TEXTURE 0
+#endif
+
+bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget)
+{
+ // Create the EGL context.
+ ctx = new QEglContext();
+ ctx->setApi(QEglContext::OpenGL);
+
+ // Open the EGL display.
+ if (!ctx->openDisplay(0)) {
+ delete ctx;
+ ctx = 0;
+ return false;
+ }
+
+ // Choose an appropriate configuration. We use the best format
+ // we can find, even if it is greater than the requested format.
+ // We try for a pbuffer that is capable of texture rendering if possible.
+ QEglProperties configProps;
+ qt_egl_set_format(configProps, QInternal::Pbuffer, f);
+ configProps.setRenderableType(ctx->api());
+ bool ok = false;
+#if QGL_RENDER_TEXTURE
+ textureFormat = EGL_TEXTURE_RGBA;
+ configProps.setValue(EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE);
+ ok = ctx->chooseConfig(configProps, QEglContext::BestPixelFormat);
+ if (!ok) {
+ // Try again with RGB texture rendering.
+ textureFormat = EGL_TEXTURE_RGB;
+ configProps.removeValue(EGL_BIND_TO_TEXTURE_RGBA);
+ configProps.setValue(EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE);
+ ok = ctx->chooseConfig(configProps, QEglContext::BestPixelFormat);
+ if (!ok) {
+ // One last try for a pbuffer with no texture rendering.
+ configProps.removeValue(EGL_BIND_TO_TEXTURE_RGB);
+ textureFormat = EGL_NONE;
+ }
+ }
+#else
+ textureFormat = EGL_NONE;
+#endif
+ if (!ok) {
+ if (!ctx->chooseConfig(configProps, QEglContext::BestPixelFormat)) {
+ delete ctx;
+ ctx = 0;
+ return false;
+ }
+ }
+
+ // Retrieve the actual format properties.
+ qt_egl_update_format(*ctx, format);
+
+ // Create the attributes needed for the pbuffer.
+ QEglProperties attribs;
+ attribs.setValue(EGL_WIDTH, size.width());
+ attribs.setValue(EGL_HEIGHT, size.height());
+#if QGL_RENDER_TEXTURE
+ if (textureFormat != EGL_NONE) {
+ attribs.setValue(EGL_TEXTURE_FORMAT, textureFormat);
+ attribs.setValue(EGL_TEXTURE_TARGET, EGL_TEXTURE_2D);
+ }
+#endif
+
+ // Create the pbuffer surface.
+ pbuf = eglCreatePbufferSurface(ctx->display(), ctx->config(), attribs.properties());
+#if QGL_RENDER_TEXTURE
+ if (pbuf == EGL_NO_SURFACE && textureFormat != EGL_NONE) {
+ // Try again with texture rendering disabled.
+ textureFormat = EGL_NONE;
+ attribs.removeValue(EGL_TEXTURE_FORMAT);
+ attribs.removeValue(EGL_TEXTURE_TARGET);
+ pbuf = eglCreatePbufferSurface(ctx->display(), ctx->config(), attribs.properties());
+ }
+#endif
+ if (pbuf == EGL_NO_SURFACE) {
+ qWarning() << "QGLPixelBufferPrivate::init(): Unable to create EGL pbuffer surface:" << QEglContext::errorString(eglGetError());
+ return false;
+ }
+ ctx->setSurface(pbuf);
+
+ // Create a new context for the configuration.
+ QEglContext *shareContext = 0;
+ if (shareWidget && shareWidget->d_func()->glcx)
+ shareContext = shareWidget->d_func()->glcx->d_func()->eglContext;
+ if (!ctx->createContext(shareContext)) {
+ delete ctx;
+ ctx = 0;
+ return false;
+ }
+
+ return true;
+}
+
+bool QGLPixelBufferPrivate::cleanup()
+{
+ eglDestroySurface(QEglContext::defaultDisplay(0), pbuf);
+ return true;
+}
+
+bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id)
+{
+#if QGL_RENDER_TEXTURE
+ Q_D(QGLPixelBuffer);
+ if (d->invalid || d->textureFormat == EGL_NONE || !d->ctx)
+ return false;
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ return eglBindTexImage(d->ctx->display(), d->ctx->surface(), EGL_BACK_BUFFER);
+#else
+ Q_UNUSED(texture_id);
+ return false;
+#endif
+}
+
+void QGLPixelBuffer::releaseFromDynamicTexture()
+{
+#if QGL_RENDER_TEXTURE
+ Q_D(QGLPixelBuffer);
+ if (d->invalid || d->textureFormat == EGL_NONE || !d->ctx)
+ return;
+ eglReleaseTexImage(d->ctx->display(), d->ctx->surface(), EGL_BACK_BUFFER);
+#endif
+}
+
+
+GLuint QGLPixelBuffer::generateDynamicTexture() const
+{
+#if QGL_RENDER_TEXTURE
+ Q_D(const QGLPixelBuffer);
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ if (d->textureFormat == EGL_TEXTURE_RGB)
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, d->req_size.width(), d->req_size.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
+ else
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, d->req_size.width(), d->req_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ return texture;
+#else
+ return 0;
+#endif
+}
+
+bool QGLPixelBuffer::hasOpenGLPbuffers()
+{
+ // See if we have at least 1 configuration that matches the default format.
+ QEglContext ctx;
+ if (!ctx.openDisplay(0))
+ return false;
+ QEglProperties configProps;
+ qt_egl_set_format(configProps, QInternal::Pbuffer, QGLFormat::defaultFormat());
+ configProps.setRenderableType(QEglContext::OpenGL);
+ return ctx.chooseConfig(configProps);
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixelbuffer_mac.mm b/src/opengl/qglpixelbuffer_mac.mm
new file mode 100644
index 0000000000..14941ab25e
--- /dev/null
+++ b/src/opengl/qglpixelbuffer_mac.mm
@@ -0,0 +1,338 @@
+/****************************************************************************
+**
+** 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 "qglpixelbuffer.h"
+#include "qglpixelbuffer_p.h"
+
+#ifndef QT_MAC_USE_COCOA
+#include <AGL/agl.h>
+#endif
+
+#include <qimage.h>
+#include <private/qgl_p.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef GL_TEXTURE_RECTANGLE_EXT
+#define GL_TEXTURE_RECTANGLE_EXT 0x84F5
+#endif
+
+static int nearest_gl_texture_size(int v)
+{
+ int n = 0, last = 0;
+ for (int s = 0; s < 32; ++s) {
+ if (((v>>s) & 1) == 1) {
+ ++n;
+ last = s;
+ }
+ }
+ if (n > 1)
+ return 1 << (last+1);
+ return 1 << last;
+}
+
+bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget)
+{
+#ifdef QT_MAC_USE_COCOA
+ Q_Q(QGLPixelBuffer);
+ // create a dummy context
+ QGLContext context(f, q);
+ context.create(shareWidget ? shareWidget->context() : 0);
+
+ if (context.isSharing())
+ share_ctx = shareWidget->context()->d_func()->cx;
+
+ // steal the NSOpenGLContext and update the format
+ ctx = context.d_func()->cx;
+ context.d_func()->cx = 0;
+ // d->cx will be set to ctx later in
+ // QGLPixelBufferPrivate::common_init, so we need to retain it:
+ [static_cast<NSOpenGLContext *>(ctx) retain];
+
+ format = context.format();
+
+ GLenum target = GL_TEXTURE_2D;
+
+ if ((QGLExtensions::glExtensions & QGLExtensions::TextureRectangle)
+ && (size.width() != nearest_gl_texture_size(size.width())
+ || size.height() != nearest_gl_texture_size(size.height())))
+ {
+ target = GL_TEXTURE_RECTANGLE_EXT;
+ }
+
+ pbuf = [[NSOpenGLPixelBuffer alloc] initWithTextureTarget:target
+ textureInternalFormat:GL_RGBA
+ textureMaxMipMapLevel:0
+ pixelsWide:size.width()
+ pixelsHigh:size.height()];
+ if (!pbuf) {
+ qWarning("QGLPixelBuffer: Cannot create a pbuffer");
+ return false;
+ }
+
+ [static_cast<NSOpenGLContext *>(ctx) setPixelBuffer:static_cast<NSOpenGLPixelBuffer *>(pbuf)
+ cubeMapFace:0
+ mipMapLevel:0
+ currentVirtualScreen:0];
+ return true;
+#else
+ GLint attribs[40], i=0;
+ attribs[i++] = AGL_RGBA;
+ attribs[i++] = AGL_BUFFER_SIZE;
+ attribs[i++] = 32;
+ attribs[i++] = AGL_LEVEL;
+ attribs[i++] = f.plane();
+ if (f.redBufferSize() != -1) {
+ attribs[i++] = AGL_RED_SIZE;
+ attribs[i++] = f.redBufferSize();
+ }
+ if (f.greenBufferSize() != -1) {
+ attribs[i++] = AGL_GREEN_SIZE;
+ attribs[i++] = f.greenBufferSize();
+ }
+ if (f.blueBufferSize() != -1) {
+ attribs[i++] = AGL_BLUE_SIZE;
+ attribs[i++] = f.blueBufferSize();
+ }
+ if (f.stereo())
+ attribs[i++] = AGL_STEREO;
+ if (f.alpha()) {
+ attribs[i++] = AGL_ALPHA_SIZE;
+ attribs[i++] = f.alphaBufferSize() == -1 ? 8 : f.alphaBufferSize();
+ }
+ if (f.stencil()) {
+ attribs[i++] = AGL_STENCIL_SIZE;
+ attribs[i++] = f.stencilBufferSize() == -1 ? 8 : f.stencilBufferSize();
+ }
+ if (f.depth()) {
+ attribs[i++] = AGL_DEPTH_SIZE;
+ attribs[i++] = f.depthBufferSize() == -1 ? 32 : f.depthBufferSize();
+ }
+ if (f.accum()) {
+ attribs[i++] = AGL_ACCUM_RED_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 16 : f.accumBufferSize();
+ attribs[i++] = AGL_ACCUM_BLUE_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 16 : f.accumBufferSize();
+ attribs[i++] = AGL_ACCUM_GREEN_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 16 : f.accumBufferSize();
+ attribs[i++] = AGL_ACCUM_ALPHA_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 16 : f.accumBufferSize();
+ }
+
+ if (f.sampleBuffers()) {
+ attribs[i++] = AGL_SAMPLE_BUFFERS_ARB;
+ attribs[i++] = 1;
+ attribs[i++] = AGL_SAMPLES_ARB;
+ attribs[i++] = f.samples() == -1 ? 4 : f.samples();
+ }
+ attribs[i] = AGL_NONE;
+
+ AGLPixelFormat format = aglChoosePixelFormat(0, 0, attribs);
+ if (!format) {
+ qWarning("QGLPixelBuffer: Unable to find a pixel format (AGL error %d).",
+ (int) aglGetError());
+ return false;
+ }
+
+ GLint res;
+ aglDescribePixelFormat(format, AGL_LEVEL, &res);
+ this->format.setPlane(res);
+ aglDescribePixelFormat(format, AGL_DOUBLEBUFFER, &res);
+ this->format.setDoubleBuffer(res);
+ aglDescribePixelFormat(format, AGL_DEPTH_SIZE, &res);
+ this->format.setDepth(res);
+ if (this->format.depth())
+ this->format.setDepthBufferSize(res);
+ aglDescribePixelFormat(format, AGL_RGBA, &res);
+ this->format.setRgba(res);
+ aglDescribePixelFormat(format, AGL_RED_SIZE, &res);
+ this->format.setRedBufferSize(res);
+ aglDescribePixelFormat(format, AGL_GREEN_SIZE, &res);
+ this->format.setGreenBufferSize(res);
+ aglDescribePixelFormat(format, AGL_BLUE_SIZE, &res);
+ this->format.setBlueBufferSize(res);
+ aglDescribePixelFormat(format, AGL_ALPHA_SIZE, &res);
+ this->format.setAlpha(res);
+ if (this->format.alpha())
+ this->format.setAlphaBufferSize(res);
+ aglDescribePixelFormat(format, AGL_ACCUM_RED_SIZE, &res);
+ this->format.setAccum(res);
+ if (this->format.accum())
+ this->format.setAccumBufferSize(res);
+ aglDescribePixelFormat(format, AGL_STENCIL_SIZE, &res);
+ this->format.setStencil(res);
+ if (this->format.stencil())
+ this->format.setStencilBufferSize(res);
+ aglDescribePixelFormat(format, AGL_STEREO, &res);
+ this->format.setStereo(res);
+ aglDescribePixelFormat(format, AGL_SAMPLE_BUFFERS_ARB, &res);
+ this->format.setSampleBuffers(res);
+ if (this->format.sampleBuffers()) {
+ aglDescribePixelFormat(format, AGL_SAMPLES_ARB, &res);
+ this->format.setSamples(res);
+ }
+
+ AGLContext share = 0;
+ if (shareWidget)
+ share = share_ctx = static_cast<AGLContext>(shareWidget->d_func()->glcx->d_func()->cx);
+ ctx = aglCreateContext(format, share);
+ if (!ctx) {
+ qWarning("QGLPixelBuffer: Unable to create a context (AGL error %d).",
+ (int) aglGetError());
+ return false;
+ }
+
+ GLenum target = GL_TEXTURE_2D;
+
+ if ((QGLExtensions::glExtensions & QGLExtensions::TextureRectangle)
+ && (size.width() != nearest_gl_texture_size(size.width())
+ || size.height() != nearest_gl_texture_size(size.height())))
+ {
+ target = GL_TEXTURE_RECTANGLE_EXT;
+ }
+
+ if (!aglCreatePBuffer(size.width(), size.height(), target, GL_RGBA, 0, &pbuf)) {
+ qWarning("QGLPixelBuffer: Unable to create a pbuffer (AGL error %d).",
+ (int) aglGetError());
+ return false;
+ }
+
+ if (!aglSetPBuffer(ctx, pbuf, 0, 0, 0)) {
+ qWarning("QGLPixelBuffer: Unable to set pbuffer (AGL error %d).",
+ (int) aglGetError());
+ return false;
+ }
+
+ aglDestroyPixelFormat(format);
+ return true;
+
+#endif
+}
+
+bool QGLPixelBufferPrivate::cleanup()
+{
+#ifdef QT_MAC_USE_COCOA
+ [static_cast<NSOpenGLPixelBuffer *>(pbuf) release];
+ pbuf = 0;
+ [static_cast<NSOpenGLContext *>(ctx) release];
+ ctx = 0;
+#else
+ aglDestroyPBuffer(pbuf);
+#endif
+ return true;
+}
+
+bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id)
+{
+ Q_D(QGLPixelBuffer);
+ if (d->invalid || !d->share_ctx)
+ return false;
+
+#ifdef QT_MAC_USE_COCOA
+ NSOpenGLContext *oldContext = [NSOpenGLContext currentContext];
+ if (d->share_ctx != oldContext)
+ [static_cast<NSOpenGLContext *>(d->share_ctx) makeCurrentContext];
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ [static_cast<NSOpenGLContext *>(d->share_ctx)
+ setTextureImageToPixelBuffer:static_cast<NSOpenGLPixelBuffer *>(d->pbuf)
+ colorBuffer:GL_FRONT];
+ if (oldContext && oldContext != d->share_ctx)
+ [oldContext makeCurrentContext];
+ return true;
+#else
+ aglSetCurrentContext(d->share_ctx);
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ aglTexImagePBuffer(d->share_ctx, d->pbuf, GL_FRONT);
+ return true;
+#endif
+}
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+bool QGLPixelBuffer::bindToDynamicTexture(QMacCompatGLuint texture_id)
+{
+ return bindToDynamicTexture(GLuint(texture_id));
+}
+#endif
+
+void QGLPixelBuffer::releaseFromDynamicTexture()
+{
+}
+
+GLuint QGLPixelBuffer::generateDynamicTexture() const
+{
+ Q_D(const QGLPixelBuffer);
+
+#ifdef QT_MAC_USE_COCOA
+ NSOpenGLContext *oldContext = [NSOpenGLContext currentContext];
+ if (d->share_ctx != oldContext)
+ [static_cast<NSOpenGLContext *>(d->share_ctx) makeCurrentContext];
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ [static_cast<NSOpenGLContext *>(d->share_ctx)
+ setTextureImageToPixelBuffer:static_cast<NSOpenGLPixelBuffer *>(d->pbuf)
+ colorBuffer:GL_FRONT];
+ glBindTexture(GL_TEXTURE_2D, texture); // updates texture target
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ if (oldContext && oldContext != d->share_ctx)
+ [oldContext makeCurrentContext];
+ return texture;
+#else
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ aglTexImagePBuffer(d->share_ctx, d->pbuf, GL_FRONT);
+ glBindTexture(GL_TEXTURE_2D, texture); // updates texture target
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ return texture;
+#endif
+}
+
+bool QGLPixelBuffer::hasOpenGLPbuffers()
+{
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixelbuffer_p.h b/src/opengl/qglpixelbuffer_p.h
new file mode 100644
index 0000000000..abdf838fcf
--- /dev/null
+++ b/src/opengl/qglpixelbuffer_p.h
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** 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 QGLPIXELBUFFER_P_H
+#define QGLPIXELBUFFER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "QtOpenGL/qglpixelbuffer.h"
+#include <private/qgl_p.h>
+
+#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
+#include <GL/glx.h>
+
+// The below is needed to for compilation on HPUX, due to broken GLX
+// headers. Some of the systems define GLX_VERSION_1_3 without
+// defining the GLXFBConfig structure, which is wrong.
+#if defined (Q_OS_HPUX) && defined(QT_DEFINE_GLXFBCONFIG_STRUCT)
+typedef unsigned long GLXPbuffer;
+
+struct GLXFBConfig {
+ int visualType;
+ int transparentType;
+ /* colors are floats scaled to ints */
+ int transparentRed, transparentGreen, transparentBlue, transparentAlpha;
+ int transparentIndex;
+
+ int visualCaveat;
+
+ int associatedVisualId;
+ int screen;
+
+ int drawableType;
+ int renderType;
+
+ int maxPbufferWidth, maxPbufferHeight, maxPbufferPixels;
+ int optimalPbufferWidth, optimalPbufferHeight; /* for SGIX_pbuffer */
+
+ int visualSelectGroup; /* visuals grouped by select priority */
+
+ unsigned int id;
+
+ GLboolean rgbMode;
+ GLboolean colorIndexMode;
+ GLboolean doubleBufferMode;
+ GLboolean stereoMode;
+ GLboolean haveAccumBuffer;
+ GLboolean haveDepthBuffer;
+ GLboolean haveStencilBuffer;
+
+ /* The number of bits present in various buffers */
+ GLint accumRedBits, accumGreenBits, accumBlueBits, accumAlphaBits;
+ GLint depthBits;
+ GLint stencilBits;
+ GLint indexBits;
+ GLint redBits, greenBits, blueBits, alphaBits;
+ GLuint redMask, greenMask, blueMask, alphaMask;
+
+ GLuint multiSampleSize; /* Number of samples per pixel (0 if no ms) */
+
+ GLuint nMultiSampleBuffers; /* Number of available ms buffers */
+ GLint maxAuxBuffers;
+
+ /* frame buffer level */
+ GLint level;
+
+ /* color ranges (for SGI_color_range) */
+ GLboolean extendedRange;
+ GLdouble minRed, maxRed;
+ GLdouble minGreen, maxGreen;
+ GLdouble minBlue, maxBlue;
+ GLdouble minAlpha, maxAlpha;
+};
+
+#endif // Q_OS_HPUX
+
+#elif defined(Q_WS_WIN)
+DECLARE_HANDLE(HPBUFFERARB);
+#elif defined(QT_OPENGL_ES_2)
+#include <EGL/egl.h>
+#elif defined(QT_OPENGL_ES)
+#include <GLES/egl.h>
+#endif
+QT_END_INCLUDE_NAMESPACE
+
+class QEglContext;
+
+class QGLPixelBufferPrivate {
+ Q_DECLARE_PUBLIC(QGLPixelBuffer)
+public:
+ QGLPixelBufferPrivate(QGLPixelBuffer *q) : q_ptr(q), invalid(true), qctx(0), pbuf(0), ctx(0)
+ {
+ QGLExtensions::init();
+#ifdef Q_WS_WIN
+ dc = 0;
+#elif defined(Q_WS_MACX)
+ share_ctx = 0;
+#endif
+ }
+ bool init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget);
+ void common_init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget);
+ bool cleanup();
+
+ QGLPixelBuffer *q_ptr;
+ bool invalid;
+ QGLContext *qctx;
+ QGLFormat format;
+
+ QGLFormat req_format;
+ QPointer<QGLWidget> req_shareWidget;
+ QSize req_size;
+
+#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
+ GLXPbuffer pbuf;
+ GLXContext ctx;
+#elif defined(Q_WS_WIN)
+ HDC dc;
+ bool has_render_texture :1;
+#if !defined(QT_OPENGL_ES)
+ HPBUFFERARB pbuf;
+ HGLRC ctx;
+#endif
+#elif defined(Q_WS_MACX)
+# ifdef QT_MAC_USE_COCOA
+ void *pbuf;
+ void *ctx;
+ void *share_ctx;
+# else
+ AGLPbuffer pbuf;
+ AGLContext ctx;
+ AGLContext share_ctx;
+# endif
+#endif
+#if defined(QT_OPENGL_ES)
+ EGLSurface pbuf;
+ QEglContext *ctx;
+ int textureFormat;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QGLPIXELBUFFER_P_H
diff --git a/src/opengl/qglpixelbuffer_win.cpp b/src/opengl/qglpixelbuffer_win.cpp
new file mode 100644
index 0000000000..e81a576072
--- /dev/null
+++ b/src/opengl/qglpixelbuffer_win.cpp
@@ -0,0 +1,390 @@
+/****************************************************************************
+**
+** 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 <qglpixelbuffer.h>
+#include <qgl.h>
+#include <private/qgl_p.h>
+
+#include <qglpixelbuffer_p.h>
+
+#include <qimage.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/* WGL_WGLEXT_PROTOTYPES */
+typedef const char * (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc);
+typedef HPBUFFERARB (WINAPI * PFNWGLCREATEPBUFFERARBPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList);
+typedef HDC (WINAPI * PFNWGLGETPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer);
+typedef int (WINAPI * PFNWGLRELEASEPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer, HDC hDC);
+typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFERARBPROC) (HPBUFFERARB hPbuffer);
+typedef BOOL (WINAPI * PFNWGLQUERYPBUFFERARBPROC) (HPBUFFERARB hPbuffer, int iAttribute, int *piValue);
+typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues);
+typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBFVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues);
+typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
+typedef BOOL (WINAPI * PFNWGLBINDTEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer);
+typedef BOOL (WINAPI * PFNWGLRELEASETEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer);
+typedef BOOL (WINAPI * PFNWGLSETPBUFFERATTRIBARBPROC) (HPBUFFERARB hPbuffer, const int * piAttribList);
+
+#ifndef WGL_ARB_pbuffer
+#define WGL_DRAW_TO_PBUFFER_ARB 0x202D
+#define WGL_MAX_PBUFFER_PIXELS_ARB 0x202E
+#define WGL_MAX_PBUFFER_WIDTH_ARB 0x202F
+#define WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030
+#define WGL_PBUFFER_LARGEST_ARB 0x2033
+#define WGL_PBUFFER_WIDTH_ARB 0x2034
+#define WGL_PBUFFER_HEIGHT_ARB 0x2035
+#define WGL_PBUFFER_LOST_ARB 0x2036
+#endif
+
+#ifndef WGL_ARB_pixel_format
+#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
+#define WGL_DRAW_TO_WINDOW_ARB 0x2001
+#define WGL_DRAW_TO_BITMAP_ARB 0x2002
+#define WGL_ACCELERATION_ARB 0x2003
+#define WGL_NEED_PALETTE_ARB 0x2004
+#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
+#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
+#define WGL_SWAP_METHOD_ARB 0x2007
+#define WGL_NUMBER_OVERLAYS_ARB 0x2008
+#define WGL_NUMBER_UNDERLAYS_ARB 0x2009
+#define WGL_TRANSPARENT_ARB 0x200A
+#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
+#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
+#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
+#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
+#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
+#define WGL_SHARE_DEPTH_ARB 0x200C
+#define WGL_SHARE_STENCIL_ARB 0x200D
+#define WGL_SHARE_ACCUM_ARB 0x200E
+#define WGL_SUPPORT_GDI_ARB 0x200F
+#define WGL_SUPPORT_OPENGL_ARB 0x2010
+#define WGL_DOUBLE_BUFFER_ARB 0x2011
+#define WGL_STEREO_ARB 0x2012
+#define WGL_PIXEL_TYPE_ARB 0x2013
+#define WGL_COLOR_BITS_ARB 0x2014
+#define WGL_RED_BITS_ARB 0x2015
+#define WGL_RED_SHIFT_ARB 0x2016
+#define WGL_GREEN_BITS_ARB 0x2017
+#define WGL_GREEN_SHIFT_ARB 0x2018
+#define WGL_BLUE_BITS_ARB 0x2019
+#define WGL_BLUE_SHIFT_ARB 0x201A
+#define WGL_ALPHA_BITS_ARB 0x201B
+#define WGL_ALPHA_SHIFT_ARB 0x201C
+#define WGL_ACCUM_BITS_ARB 0x201D
+#define WGL_ACCUM_RED_BITS_ARB 0x201E
+#define WGL_ACCUM_GREEN_BITS_ARB 0x201F
+#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
+#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
+#define WGL_DEPTH_BITS_ARB 0x2022
+#define WGL_STENCIL_BITS_ARB 0x2023
+#define WGL_AUX_BUFFERS_ARB 0x2024
+#define WGL_NO_ACCELERATION_ARB 0x2025
+#define WGL_GENERIC_ACCELERATION_ARB 0x2026
+#define WGL_FULL_ACCELERATION_ARB 0x2027
+#define WGL_SWAP_EXCHANGE_ARB 0x2028
+#define WGL_SWAP_COPY_ARB 0x2029
+#define WGL_SWAP_UNDEFINED_ARB 0x202A
+#define WGL_TYPE_RGBA_ARB 0x202B
+#define WGL_TYPE_COLORINDEX_ARB 0x202C
+#endif
+
+#ifndef WGL_ARB_render_texture
+#define WGL_BIND_TO_TEXTURE_RGB_ARB 0x2070
+#define WGL_BIND_TO_TEXTURE_RGBA_ARB 0x2071
+#define WGL_TEXTURE_FORMAT_ARB 0x2072
+#define WGL_TEXTURE_TARGET_ARB 0x2073
+#define WGL_MIPMAP_TEXTURE_ARB 0x2074
+#define WGL_TEXTURE_RGB_ARB 0x2075
+#define WGL_TEXTURE_RGBA_ARB 0x2076
+#define WGL_NO_TEXTURE_ARB 0x2077
+#define WGL_TEXTURE_CUBE_MAP_ARB 0x2078
+#define WGL_TEXTURE_1D_ARB 0x2079
+#define WGL_TEXTURE_2D_ARB 0x207A
+#define WGL_MIPMAP_LEVEL_ARB 0x207B
+#define WGL_CUBE_MAP_FACE_ARB 0x207C
+#define WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x207D
+#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x207E
+#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x207F
+#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x2080
+#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x2081
+#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x2082
+#define WGL_FRONT_LEFT_ARB 0x2083
+#define WGL_FRONT_RIGHT_ARB 0x2084
+#define WGL_BACK_LEFT_ARB 0x2085
+#define WGL_BACK_RIGHT_ARB 0x2086
+#define WGL_AUX0_ARB 0x2087
+#define WGL_AUX1_ARB 0x2088
+#define WGL_AUX2_ARB 0x2089
+#define WGL_AUX3_ARB 0x208A
+#define WGL_AUX4_ARB 0x208B
+#define WGL_AUX5_ARB 0x208C
+#define WGL_AUX6_ARB 0x208D
+#define WGL_AUX7_ARB 0x208E
+#define WGL_AUX8_ARB 0x208F
+#define WGL_AUX9_ARB 0x2090
+#endif
+
+#ifndef WGL_FLOAT_COMPONENTS_NV
+#define WGL_FLOAT_COMPONENTS_NV 0x20B0
+#endif
+
+QGLFormat pfiToQGLFormat(HDC hdc, int pfi);
+
+static void qt_format_to_attrib_list(bool has_render_texture, const QGLFormat &f, int attribs[])
+{
+ int i = 0;
+ attribs[i++] = WGL_SUPPORT_OPENGL_ARB;
+ attribs[i++] = TRUE;
+ attribs[i++] = WGL_DRAW_TO_PBUFFER_ARB;
+ attribs[i++] = TRUE;
+
+ if (has_render_texture) {
+ attribs[i++] = WGL_BIND_TO_TEXTURE_RGBA_ARB;
+ attribs[i++] = TRUE;
+ }
+
+ attribs[i++] = WGL_COLOR_BITS_ARB;
+ attribs[i++] = 32;
+ attribs[i++] = WGL_DOUBLE_BUFFER_ARB;
+ attribs[i++] = FALSE;
+
+ if (f.stereo()) {
+ attribs[i++] = WGL_STEREO_ARB;
+ attribs[i++] = TRUE;
+ }
+ if (f.depth()) {
+ attribs[i++] = WGL_DEPTH_BITS_ARB;
+ attribs[i++] = f.depthBufferSize() == -1 ? 24 : f.depthBufferSize();
+ }
+ if (f.redBufferSize() != -1) {
+ attribs[i++] = WGL_RED_BITS_ARB;
+ attribs[i++] = f.redBufferSize();
+ }
+ if (f.greenBufferSize() != -1) {
+ attribs[i++] = WGL_GREEN_BITS_ARB;
+ attribs[i++] = f.greenBufferSize();
+ }
+ if (f.blueBufferSize() != -1) {
+ attribs[i++] = WGL_BLUE_BITS_ARB;
+ attribs[i++] = f.blueBufferSize();
+ }
+ if (f.alpha()) {
+ attribs[i++] = WGL_ALPHA_BITS_ARB;
+ attribs[i++] = f.alphaBufferSize() == -1 ? 8 : f.alphaBufferSize();
+ }
+ if (f.accum()) {
+ attribs[i++] = WGL_ACCUM_BITS_ARB;
+ attribs[i++] = f.accumBufferSize() == -1 ? 16 : f.accumBufferSize();
+ }
+ if (f.stencil()) {
+ attribs[i++] = WGL_STENCIL_BITS_ARB;
+ attribs[i++] = f.stencilBufferSize() == -1 ? 8 : f.stencilBufferSize();
+ }
+ if ((f.redBufferSize() > 8 || f.greenBufferSize() > 8
+ || f.blueBufferSize() > 8 || f.alphaBufferSize() > 8)
+ && (QGLExtensions::glExtensions & QGLExtensions::NVFloatBuffer))
+ {
+ attribs[i++] = WGL_FLOAT_COMPONENTS_NV;
+ attribs[i++] = TRUE;
+ }
+ // sample buffers doesn't work in conjunction with the render_texture extension
+ // so igonre that for now
+ // if (f.sampleBuffers()) {
+ // attribs[i++] = WGL_SAMPLE_BUFFERS_ARB;
+ // attribs[i++] = 1;
+ // attribs[i++] = WGL_SAMPLES_ARB;
+ // attribs[i++] = f.samples() == -1 ? 16 : f.samples();
+ // }
+ attribs[i] = 0;
+}
+
+bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget)
+{
+ QGLWidget dmy;
+ dmy.makeCurrent(); // needed for wglGetProcAddress() to succeed
+
+ PFNWGLCREATEPBUFFERARBPROC wglCreatePbufferARB =
+ (PFNWGLCREATEPBUFFERARBPROC) wglGetProcAddress("wglCreatePbufferARB");
+ PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDCARB =
+ (PFNWGLGETPBUFFERDCARBPROC) wglGetProcAddress("wglGetPbufferDCARB");
+ PFNWGLQUERYPBUFFERARBPROC wglQueryPbufferARB =
+ (PFNWGLQUERYPBUFFERARBPROC) wglGetProcAddress("wglQueryPbufferARB");
+ PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB =
+ (PFNWGLCHOOSEPIXELFORMATARBPROC) wglGetProcAddress("wglChoosePixelFormatARB");
+
+ if (!wglCreatePbufferARB) // assumes that if one can be resolved, all of them can
+ return false;
+
+ dc = GetDC(dmy.winId());
+ Q_ASSERT(dc);
+
+ PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB =
+ (PFNWGLGETEXTENSIONSSTRINGARBPROC) wglGetProcAddress("wglGetExtensionsStringARB");
+
+ if (wglGetExtensionsStringARB) {
+ QString extensions(QLatin1String(wglGetExtensionsStringARB(dc)));
+ has_render_texture = extensions.contains(QLatin1String("WGL_ARB_render_texture"));
+ }
+
+ int attribs[40];
+ qt_format_to_attrib_list(has_render_texture, f, attribs);
+
+ // Find pbuffer capable pixel format.
+ unsigned int num_formats = 0;
+ int pixel_format;
+ wglChoosePixelFormatARB(dc, attribs, 0, 1, &pixel_format, &num_formats);
+
+ // some GL implementations don't support pbuffers with accum
+ // buffers, so try that before we give up
+ if (num_formats == 0 && f.accum()) {
+ QGLFormat tmp = f;
+ tmp.setAccum(false);
+ qt_format_to_attrib_list(has_render_texture, tmp, attribs);
+ wglChoosePixelFormatARB(dc, attribs, 0, 1, &pixel_format, &num_formats);
+ }
+
+ if (num_formats == 0) {
+ qWarning("QGLPixelBuffer: Unable to find a pixel format with pbuffer - giving up.");
+ ReleaseDC(dmy.winId(), dc);
+ return false;
+ }
+ format = pfiToQGLFormat(dc, pixel_format);
+
+ // NB! The below ONLY works if the width/height are powers of 2.
+ // Set some pBuffer attributes so that we can use this pBuffer as
+ // a 2D RGBA texture target.
+ int pb_attribs[] = {WGL_TEXTURE_FORMAT_ARB, WGL_TEXTURE_RGBA_ARB,
+ WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_2D_ARB, 0};
+
+ pbuf = wglCreatePbufferARB(dc, pixel_format, size.width(), size.height(),
+ has_render_texture ? pb_attribs : 0);
+ if(!pbuf) {
+ // try again without the render_texture extension
+ pbuf = wglCreatePbufferARB(dc, pixel_format, size.width(), size.height(), 0);
+ has_render_texture = false;
+ if (!pbuf) {
+ qWarning("QGLPixelBuffer: Unable to create pbuffer [w=%d, h=%d] - giving up.", size.width(), size.height());
+ ReleaseDC(dmy.winId(), dc);
+ return false;
+ }
+ }
+
+ ReleaseDC(dmy.winId(), dc);
+ dc = wglGetPbufferDCARB(pbuf);
+ ctx = wglCreateContext(dc);
+
+ if (!dc || !ctx) {
+ qWarning("QGLPixelBuffer: Unable to create pbuffer context - giving up.");
+ return false;
+ }
+
+ HGLRC share_ctx = shareWidget ? shareWidget->d_func()->glcx->d_func()->rc : 0;
+ if (share_ctx && !wglShareLists(share_ctx, ctx))
+ qWarning("QGLPixelBuffer: Unable to share display lists - with share widget.");
+
+ int width, height;
+ wglQueryPbufferARB(pbuf, WGL_PBUFFER_WIDTH_ARB, &width);
+ wglQueryPbufferARB(pbuf, WGL_PBUFFER_HEIGHT_ARB, &height);
+ return true;
+}
+
+bool QGLPixelBufferPrivate::cleanup()
+{
+ PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDCARB =
+ (PFNWGLRELEASEPBUFFERDCARBPROC) wglGetProcAddress("wglReleasePbufferDCARB");
+ PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbufferARB =
+ (PFNWGLDESTROYPBUFFERARBPROC) wglGetProcAddress("wglDestroyPbufferARB");
+ if (!invalid && wglReleasePbufferDCARB && wglDestroyPbufferARB) {
+ wglReleasePbufferDCARB(pbuf, dc);
+ wglDestroyPbufferARB(pbuf);
+ }
+ return true;
+}
+
+bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id)
+{
+ Q_D(QGLPixelBuffer);
+ if (d->invalid || !d->has_render_texture)
+ return false;
+ PFNWGLBINDTEXIMAGEARBPROC wglBindTexImageARB =
+ (PFNWGLBINDTEXIMAGEARBPROC) wglGetProcAddress("wglBindTexImageARB");
+ if (wglBindTexImageARB) {
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ return wglBindTexImageARB(d->pbuf, WGL_FRONT_LEFT_ARB);
+ }
+ return false;
+}
+
+void QGLPixelBuffer::releaseFromDynamicTexture()
+{
+ Q_D(QGLPixelBuffer);
+ if (d->invalid || !d->has_render_texture)
+ return;
+ PFNWGLRELEASETEXIMAGEARBPROC wglReleaseTexImageARB =
+ (PFNWGLRELEASETEXIMAGEARBPROC) wglGetProcAddress("wglReleaseTexImageARB");
+ if (wglReleaseTexImageARB)
+ wglReleaseTexImageARB(d->pbuf, WGL_FRONT_LEFT_ARB);
+}
+
+bool QGLPixelBuffer::hasOpenGLPbuffers()
+{
+ bool ret = false;
+ QGLWidget *dmy = 0;
+ if (!QGLContext::currentContext()) {
+ dmy = new QGLWidget;
+ dmy->makeCurrent();
+ }
+ PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB =
+ (PFNWGLGETEXTENSIONSSTRINGARBPROC) wglGetProcAddress("wglGetExtensionsStringARB");
+ if (wglGetExtensionsStringARB) {
+ QString extensions(QLatin1String(wglGetExtensionsStringARB(wglGetCurrentDC())));
+ if (extensions.contains(QLatin1String("WGL_ARB_pbuffer"))
+ && extensions.contains(QLatin1String("WGL_ARB_pixel_format"))) {
+ ret = true;
+ }
+ }
+ if (dmy)
+ delete dmy;
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixelbuffer_x11.cpp b/src/opengl/qglpixelbuffer_x11.cpp
new file mode 100644
index 0000000000..0804503a64
--- /dev/null
+++ b/src/opengl/qglpixelbuffer_x11.cpp
@@ -0,0 +1,301 @@
+/****************************************************************************
+**
+** 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 <qlibrary.h>
+#include <qdebug.h>
+#include <private/qgl_p.h>
+#include <private/qt_x11_p.h>
+#include <private/qpaintengine_opengl_p.h>
+
+#include <qx11info_x11.h>
+#include <GL/glx.h>
+#include <qimage.h>
+
+#include "qglpixelbuffer.h"
+#include "qglpixelbuffer_p.h"
+
+#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
+#include <dlfcn.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef GLX_VERSION_1_3
+#define GLX_RGBA_BIT 0x00000002
+#define GLX_PBUFFER_BIT 0x00000004
+#define GLX_DRAWABLE_TYPE 0x8010
+#define GLX_RENDER_TYPE 0x8011
+#define GLX_RGBA_TYPE 0x8014
+#define GLX_PBUFFER_HEIGHT 0x8040
+#define GLX_PBUFFER_WIDTH 0x8041
+#endif
+
+#ifndef GLX_ARB_multisample
+#define GLX_SAMPLE_BUFFERS_ARB 100000
+#define GLX_SAMPLES_ARB 100001
+#endif
+
+typedef GLXFBConfig* (*_glXChooseFBConfig) (Display *dpy, int screen, const int *attrib_list, int *nelements);
+typedef int (*_glXGetFBConfigAttrib) (Display *dpy, GLXFBConfig config, int attribute, int *value);
+typedef GLXPbuffer (*_glXCreatePbuffer) (Display *dpy, GLXFBConfig config, const int *attrib_list);
+typedef void (*_glXDestroyPbuffer) (Display *dpy, GLXPbuffer pbuf);
+typedef GLXContext (*_glXCreateNewContext) (Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
+typedef Bool (*_glXMakeContextCurrent) (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
+
+static _glXChooseFBConfig qt_glXChooseFBConfig = 0;
+static _glXCreateNewContext qt_glXCreateNewContext = 0;
+static _glXCreatePbuffer qt_glXCreatePbuffer = 0;
+static _glXDestroyPbuffer qt_glXDestroyPbuffer = 0;
+static _glXGetFBConfigAttrib qt_glXGetFBConfigAttrib = 0;
+static _glXMakeContextCurrent qt_glXMakeContextCurrent = 0;
+
+#define glXChooseFBConfig qt_glXChooseFBConfig
+#define glXCreateNewContext qt_glXCreateNewContext
+#define glXCreatePbuffer qt_glXCreatePbuffer
+#define glXDestroyPbuffer qt_glXDestroyPbuffer
+#define glXGetFBConfigAttrib qt_glXGetFBConfigAttrib
+#define glXMakeContextCurrent qt_glXMakeContextCurrent
+
+static bool qt_resolve_pbuffer_extensions()
+{
+ static int resolved = false;
+ if (resolved && qt_glXMakeContextCurrent)
+ return true;
+ else if (resolved)
+ return false;
+
+#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
+ void *handle = dlopen(NULL, RTLD_LAZY);
+ if (handle) {
+ qt_glXChooseFBConfig = (_glXChooseFBConfig) dlsym(handle, "glXChooseFBConfig");
+ qt_glXCreateNewContext = (_glXCreateNewContext) dlsym(handle, "glXCreateNewContext");
+ qt_glXCreatePbuffer = (_glXCreatePbuffer) dlsym(handle, "glXCreatePbuffer");
+ qt_glXDestroyPbuffer = (_glXDestroyPbuffer) dlsym(handle, "glXDestroyPbuffer");
+ qt_glXGetFBConfigAttrib = (_glXGetFBConfigAttrib) dlsym(handle, "glXGetFBConfigAttrib");
+ qt_glXMakeContextCurrent = (_glXMakeContextCurrent) dlsym(handle, "glXMakeContextCurrent");
+ dlclose(handle);
+ }
+ if (!qt_glXChooseFBConfig)
+#endif
+ {
+ extern const QString qt_gl_library_name();
+ QLibrary gl(qt_gl_library_name());
+ qt_glXChooseFBConfig = (_glXChooseFBConfig) gl.resolve("glXChooseFBConfig");
+ qt_glXCreateNewContext = (_glXCreateNewContext) gl.resolve("glXCreateNewContext");
+ qt_glXCreatePbuffer = (_glXCreatePbuffer) gl.resolve("glXCreatePbuffer");
+ qt_glXDestroyPbuffer = (_glXDestroyPbuffer) gl.resolve("glXDestroyPbuffer");
+ qt_glXGetFBConfigAttrib = (_glXGetFBConfigAttrib) gl.resolve("glXGetFBConfigAttrib");
+ qt_glXMakeContextCurrent = (_glXMakeContextCurrent) gl.resolve("glXMakeContextCurrent");
+ }
+
+ resolved = qt_glXMakeContextCurrent ? true : false;
+ return resolved;
+}
+
+static void qt_format_to_attrib_list(const QGLFormat &f, int attribs[])
+{
+ int i = 0;
+ attribs[i++] = GLX_RENDER_TYPE;
+ attribs[i++] = GLX_RGBA_BIT;
+ attribs[i++] = GLX_DRAWABLE_TYPE;
+ attribs[i++] = GLX_PBUFFER_BIT;
+ attribs[i++] = GLX_RED_SIZE;
+ attribs[i++] = f.redBufferSize() == -1 ? 1 : f.redBufferSize();
+ attribs[i++] = GLX_GREEN_SIZE;
+ attribs[i++] = f.greenBufferSize() == -1 ? 1 : f.greenBufferSize();
+ attribs[i++] = GLX_BLUE_SIZE;
+ attribs[i++] = f.blueBufferSize() == -1 ? 1 : f.blueBufferSize();
+ if (f.doubleBuffer()) {
+ attribs[i++] = GLX_DOUBLEBUFFER;
+ attribs[i++] = true;
+ }
+ if (f.depth()) {
+ attribs[i++] = GLX_DEPTH_SIZE;
+ attribs[i++] = f.depthBufferSize() == -1 ? 1 : f.depthBufferSize();
+ }
+ if (f.stereo()) {
+ attribs[i++] = GLX_STEREO;
+ attribs[i++] = true;
+ }
+ if (f.stencil()) {
+ attribs[i++] = GLX_STENCIL_SIZE;
+ attribs[i++] = f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize();
+ }
+ if (f.alpha()) {
+ attribs[i++] = GLX_ALPHA_SIZE;
+ attribs[i++] = f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize();
+ }
+ if (f.accum()) {
+ attribs[i++] = GLX_ACCUM_RED_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ attribs[i++] = GLX_ACCUM_GREEN_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ attribs[i++] = GLX_ACCUM_BLUE_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ if (f.alpha()) {
+ attribs[i++] = GLX_ACCUM_ALPHA_SIZE;
+ attribs[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ }
+ }
+ if (f.sampleBuffers()) {
+ attribs[i++] = GLX_SAMPLE_BUFFERS_ARB;
+ attribs[i++] = 1;
+ attribs[i++] = GLX_SAMPLES_ARB;
+ attribs[i++] = f.samples() == -1 ? 4 : f.samples();
+ }
+
+ attribs[i] = XNone;
+}
+
+bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget)
+{
+ if (!qt_resolve_pbuffer_extensions()) {
+ qWarning("QGLPixelBuffer: pbuffers are not supported on this system.");
+ return false;
+ }
+
+ int attribs[40];
+ int num_configs = 0;
+
+ qt_format_to_attrib_list(f, attribs);
+
+ GLXFBConfig *configs = glXChooseFBConfig(X11->display, X11->defaultScreen, attribs, &num_configs);
+ if (configs && num_configs) {
+ int res;
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_LEVEL, &res);
+ format.setPlane(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_DOUBLEBUFFER, &res);
+ format.setDoubleBuffer(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_DEPTH_SIZE, &res);
+ format.setDepth(res);
+ if (format.depth())
+ format.setDepthBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_RGBA, &res);
+ format.setRgba(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_RED_SIZE, &res);
+ format.setRedBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_GREEN_SIZE, &res);
+ format.setGreenBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_BLUE_SIZE, &res);
+ format.setBlueBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_ALPHA_SIZE, &res);
+ format.setAlpha(res);
+ if (format.alpha())
+ format.setAlphaBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_ACCUM_RED_SIZE, &res);
+ format.setAccum(res);
+ if (format.accum())
+ format.setAccumBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_STENCIL_SIZE, &res);
+ format.setStencil(res);
+ if (format.stencil())
+ format.setStencilBufferSize(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_STEREO, &res);
+ format.setStereo(res);
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_SAMPLE_BUFFERS_ARB, &res);
+ format.setSampleBuffers(res);
+ if (format.sampleBuffers()) {
+ glXGetFBConfigAttrib(X11->display, configs[0], GLX_SAMPLES_ARB, &res);
+ format.setSamples(res);
+ }
+
+ int pb_attribs[] = {GLX_PBUFFER_WIDTH, size.width(), GLX_PBUFFER_HEIGHT, size.height(), XNone};
+ GLXContext shareContext = 0;
+ if (shareWidget && shareWidget->d_func()->glcx)
+ shareContext = (GLXContext) shareWidget->d_func()->glcx->d_func()->cx;
+
+ pbuf = glXCreatePbuffer(QX11Info::display(), configs[0], pb_attribs);
+ ctx = glXCreateNewContext(QX11Info::display(), configs[0], GLX_RGBA_TYPE, shareContext, true);
+
+ XFree(configs);
+ if (!pbuf || !ctx) {
+ qWarning("QGLPixelBuffer: Unable to create a pbuffer/context - giving up.");
+ return false;
+ }
+ return true;
+ } else {
+ qWarning("QGLPixelBuffer: Unable to find a context/format match - giving up.");
+ return false;
+ }
+}
+
+bool QGLPixelBufferPrivate::cleanup()
+{
+ glXDestroyPbuffer(QX11Info::display(), pbuf);
+ return true;
+}
+
+bool QGLPixelBuffer::bindToDynamicTexture(GLuint)
+{
+ return false;
+}
+
+void QGLPixelBuffer::releaseFromDynamicTexture()
+{
+}
+
+bool QGLPixelBuffer::hasOpenGLPbuffers()
+{
+ bool ret = qt_resolve_pbuffer_extensions();
+
+ if (!ret)
+ return false;
+
+ int attribs[40];
+ int num_configs = 0;
+
+ qt_format_to_attrib_list(QGLFormat::defaultFormat(), attribs);
+
+ GLXFBConfig *configs = glXChooseFBConfig(X11->display, X11->defaultScreen, attribs, &num_configs);
+ GLXPbuffer pbuf = 0;
+ GLXContext ctx = 0;
+
+ if (configs && num_configs) {
+ int pb_attribs[] = {GLX_PBUFFER_WIDTH, 128, GLX_PBUFFER_HEIGHT, 128, XNone};
+ pbuf = glXCreatePbuffer(X11->display, configs[0], pb_attribs);
+ ctx = glXCreateNewContext(X11->display, configs[0], GLX_RGBA_TYPE, 0, true);
+ XFree(configs);
+ glXDestroyContext(X11->display, ctx);
+ glXDestroyPbuffer(X11->display, pbuf);
+ }
+ return pbuf && ctx;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp
new file mode 100644
index 0000000000..ff23948a3c
--- /dev/null
+++ b/src/opengl/qglpixmapfilter.cpp
@@ -0,0 +1,409 @@
+/****************************************************************************
+**
+** 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/qpixmapfilter_p.h"
+#include "qglpixmapfilter_p.h"
+#include "qgraphicssystem_gl_p.h"
+#include "qpaintengine_opengl_p.h"
+
+#include "qglpixelbuffer.h"
+#include "qglextensions_p.h"
+#include "qgl_p.h"
+
+#include "private/qapplication_p.h"
+
+
+#ifndef GL_FRAGMENT_SHADER
+#define GL_FRAGMENT_SHADER 0x8B30
+#endif
+
+#ifndef GL_COMPILE_STATUS
+#define GL_COMPILE_STATUS 0x8B81
+#endif
+
+#ifndef GL_LINK_STATUS
+#define GL_LINK_STATUS 0x8B82
+#endif
+
+
+
+
+QT_BEGIN_NAMESPACE
+
+
+void QGLPixmapFilterBase::bindTexture(const QPixmap &src) const
+{
+ const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, true);
+}
+
+void QGLPixmapFilterBase::drawImpl(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF& source) const
+{
+ processGL(painter, pos, src, source);
+}
+
+QGLSLProgram::QGLSLProgram(const QString &src)
+ : ctx(QGLContext::currentContext())
+{
+ if (!qt_resolve_glsl_extensions(const_cast<QGLContext *>(ctx))) {
+ qWarning("Failed to resolve GLSL functions");
+ m_valid = false;
+ return;
+ }
+
+ m_shader = glCreateShader(GL_FRAGMENT_SHADER);
+
+ const QByteArray src_ba = src.toAscii();
+ const char *src_cstr = src_ba.constData();
+
+ glShaderSource(m_shader, 1, &src_cstr, 0);
+ glCompileShader(m_shader);
+ glGetShaderiv(m_shader, GL_COMPILE_STATUS, &m_valid);
+ if (!m_valid) {
+ char data[4096];
+ GLsizei len;
+ glGetShaderInfoLog(m_shader, 4096, &len, data);
+ qWarning("Failed to compile GLSL shader:\n%s\nCODE:\n%s\n", data, src_cstr);
+ return;
+ }
+
+ m_program = glCreateProgram();
+ glAttachShader(m_program, m_shader);
+ glLinkProgram(m_program);
+ glGetProgramiv(m_program, GL_LINK_STATUS, &m_valid);
+ if (!m_valid) {
+ char data[4096];
+ GLsizei len;
+ glGetShaderInfoLog(m_shader, 4096, &len, data);
+ qWarning("Failed to link GLSL program:\n%s\nCODE:\n%s\n", data, src_cstr);
+ return;
+ }
+}
+
+QGLSLProgram::~QGLSLProgram()
+{
+ glDeleteProgram(m_program);
+ glDeleteShader(m_shader);
+}
+
+bool QGLSLProgram::success() const
+{
+ return m_valid;
+}
+
+void QGLSLProgram::enable()
+{
+ glUseProgram(m_program);
+}
+
+void QGLSLProgram::disable()
+{
+ glUseProgram(0);
+}
+
+typedef GLuint (APIENTRY *_glGetUniformLocation) (GLuint, const char*);
+typedef void (APIENTRY *_glUniform4fv) (GLint, GLsizei, GLfloat *);
+typedef void (APIENTRY *_glUniform3fv) (GLint, GLsizei, GLfloat *);
+typedef void (APIENTRY *_glUniform2fv) (GLint, GLsizei, GLfloat *);
+typedef void (APIENTRY *_glUniform1fv) (GLint, GLsizei, GLfloat *);
+typedef void (APIENTRY *_glUniform1i) (GLint, GLint);
+
+int QGLSLProgram::getUniformLocation(const QString &name)
+{
+ return glGetUniformLocation(m_program, name.toAscii().constData());
+}
+
+void QGLSLProgram::setUniform(int uniform, int value)
+{
+ enable();
+ glUniform1i(uniform, value);
+}
+
+void QGLSLProgram::setUniform(int uniform, qreal value)
+{
+ enable();
+ GLfloat v[] = { value };
+ glUniform1fv(uniform, 1, v);
+}
+
+void QGLSLProgram::setUniform(int uniform, qreal v1, qreal v2)
+{
+ enable();
+ GLfloat v[] = { v1, v2 };
+ glUniform2fv(uniform, 1, v);
+}
+
+void QGLSLProgram::setUniform(int uniform, qreal v1, qreal v2, qreal v3)
+{
+ enable();
+ GLfloat v[] = { v1, v2, v3 };
+ glUniform3fv(uniform, 1, v);
+}
+
+void QGLSLProgram::setUniform(int uniform, qreal v1, qreal v2, qreal v3, qreal v4)
+{
+ enable();
+ GLfloat v[] = { v1, v2, v3, v4 };
+ glUniform4fv(uniform, 1, v);
+}
+
+void QGLSLProgram::setUniform(int uniform, int count, float *v)
+{
+ enable();
+ glUniform1fv(uniform, count, v);
+}
+
+class QGLPixmapColorizeFilter: public QGLPixmapFilter<QPixmapColorizeFilter>
+{
+public:
+ QGLPixmapColorizeFilter();
+
+protected:
+ bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &pixmap, const QRectF &srcRect) const;
+
+private:
+ mutable QGLSLProgram m_program;
+ uint m_colorUniform;
+};
+
+class QGLPixmapConvolutionFilter: public QGLPixmapFilter<QPixmapConvolutionFilter>
+{
+public:
+ QGLPixmapConvolutionFilter();
+ ~QGLPixmapConvolutionFilter();
+
+protected:
+ bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
+
+private:
+ QString generateConvolutionShader() const;
+
+ mutable QGLSLProgram *m_program;
+ mutable uint m_scaleUniform;
+ mutable uint m_matrixUniform;
+
+ mutable int m_kernelWidth;
+ mutable int m_kernelHeight;
+};
+
+extern QGLWidget *qt_gl_share_widget();
+
+QPixmapFilter *QGLContextPrivate::createPixmapFilter(int type) const
+{
+ switch (type) {
+ case QPixmapFilter::ColorizeFilter:
+ return new QGLPixmapColorizeFilter;
+
+
+ case QPixmapFilter::ConvolutionFilter:
+ return new QGLPixmapConvolutionFilter;
+
+ default:
+ return 0;
+ break;
+ }
+ return 0;
+}
+
+#if !defined(QT_OPENGL_ES_2)
+extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array);
+extern void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array);
+#endif
+
+static void qgl_drawTexture(const QRectF &rect, int tx_width, int tx_height, const QRectF & src)
+{
+#ifndef QT_OPENGL_ES_2 // XXX: needs to be ported
+#ifndef QT_OPENGL_ES
+ glPushAttrib(GL_CURRENT_BIT);
+#endif
+ qreal x1, x2, y1, y2;
+
+ x1 = src.x() / tx_width;
+ x2 = x1 + src.width() / tx_width;
+ y1 = 1.0 - ((src.y() / tx_height) + (src.height() / tx_height));
+ y2 = 1.0 - (src.y() / tx_height);
+
+ q_vertexType vertexArray[4*2];
+ q_vertexType texCoordArray[4*2];
+
+ qt_add_rect_to_array(rect, vertexArray);
+ qt_add_texcoords_to_array(x1, y2, x2, y1, texCoordArray);
+
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+#ifndef QT_OPENGL_ES
+ glPopAttrib();
+#endif
+#endif
+}
+
+static const char *qt_gl_colorize_filter =
+ "uniform sampler2D texture;"
+ "uniform vec3 color;"
+ "void main(void)"
+ "{"
+ " vec2 coords = gl_TexCoord[0].st;"
+ " vec4 src = texture2D(texture, coords);"
+ " float gray = dot(src.rgb, vec3(0.212671, 0.715160, 0.072169));"
+ " vec3 colorizeed = 1.0-((1.0-gray)*(1.0-color));"
+ " gl_FragColor = vec4(colorizeed, src.a);"
+ "}";
+
+QGLPixmapColorizeFilter::QGLPixmapColorizeFilter()
+ : m_program(QLatin1String(qt_gl_colorize_filter))
+{
+ m_program.setUniform(m_program.getUniformLocation(QLatin1String("texture")), 0); // GL_TEXTURE_0
+ m_colorUniform = m_program.getUniformLocation(QLatin1String("color"));
+}
+
+bool QGLPixmapColorizeFilter::processGL(QPainter *, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const
+{
+ bindTexture(src);
+
+ QColor col = color();
+ m_program.setUniform(m_colorUniform, col.redF(), col.greenF(), col.blueF());
+
+ QRectF target = (srcRect.isNull() ? QRectF(src.rect()) : srcRect).translated(pos);
+ m_program.enable();
+ qgl_drawTexture(target, src.width(), src.height(), srcRect);
+ m_program.disable();
+
+ return true;
+}
+
+// generates convolution filter code for arbitrary sized kernel
+QString QGLPixmapConvolutionFilter::generateConvolutionShader() const {
+ QByteArray code;
+ code.append("uniform sampler2D texture;\n");
+ code.append("uniform vec2 inv_texture_size;\n");
+ code.append("uniform float matrix[");
+ code.append(QByteArray::number(m_kernelWidth * m_kernelHeight));
+ code.append("];\n");
+ code.append("vec2 offset[");
+ code.append(QByteArray::number(m_kernelWidth*m_kernelHeight));
+ code.append("];\n");
+ code.append("void main(void) {\n");
+
+ for(int y = 0; y < m_kernelHeight; y++) {
+ for(int x = 0; x < m_kernelWidth; x++) {
+ code.append(" offset[");
+ code.append(QByteArray::number(y * m_kernelWidth + x));
+ code.append("] = vec2(inv_texture_size.x * ");
+ code.append(QByteArray::number(x-(int)(m_kernelWidth/2)));
+ code.append(".0, inv_texture_size.y * ");
+ code.append(QByteArray::number((int)(m_kernelHeight/2)-y));
+ code.append(".0)");
+ code.append(";\n");
+ }
+ }
+
+ code.append(" int i = 0;\n");
+ code.append(" vec2 coords = gl_TexCoord[0].st;\n");
+ code.append(" vec4 sum = vec4(0.0);\n");
+ code.append(" for (i = 0; i < ");
+ code.append(QByteArray::number(m_kernelWidth * m_kernelHeight));
+ code.append("; i++) {\n");
+ code.append(" vec4 tmp = texture2D(texture,coords+offset[i]);\n");
+ code.append(" sum += matrix[i] * tmp;\n");
+ code.append(" }\n");
+ code.append(" gl_FragColor = sum;\n");
+ code.append("}");
+ return QLatin1String(code);
+}
+
+QGLPixmapConvolutionFilter::QGLPixmapConvolutionFilter()
+ : m_program(0)
+ , m_scaleUniform(0)
+ , m_matrixUniform(0)
+ , m_kernelWidth(0)
+ , m_kernelHeight(0)
+{
+}
+
+QGLPixmapConvolutionFilter::~QGLPixmapConvolutionFilter()
+{
+ delete m_program;
+}
+
+bool QGLPixmapConvolutionFilter::processGL(QPainter *, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const
+{
+ QRectF target = (srcRect.isNull() ? QRectF(src.rect()) : srcRect).translated(pos);
+
+ bindTexture(src);
+#ifdef GL_CLAMP
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+#endif
+ if (!m_program || m_kernelWidth != columns() || m_kernelHeight != rows()) {
+ delete m_program;
+
+ m_kernelWidth = columns();
+ m_kernelHeight = rows();
+
+ QString code = generateConvolutionShader();
+ m_program = new QGLSLProgram(code);
+ m_scaleUniform = m_program->getUniformLocation(QLatin1String("inv_texture_size"));
+ m_matrixUniform = m_program->getUniformLocation(QLatin1String("matrix"));
+ }
+
+ const qreal *kernel = convolutionKernel();
+ GLfloat *conv = new GLfloat[m_kernelWidth * m_kernelHeight];
+ for(int i = 0; i < m_kernelWidth * m_kernelHeight; ++i)
+ conv[i] = kernel[i];
+
+ const qreal iw = 1.0 / src.width();
+ const qreal ih = 1.0 / src.height();
+ m_program->setUniform(m_scaleUniform, iw, ih);
+ m_program->setUniform(m_matrixUniform, m_kernelWidth * m_kernelHeight, conv);
+
+ m_program->enable();
+ qgl_drawTexture(target, src.width(), src.height(), boundingRectFor(srcRect));
+ m_program->disable();
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpixmapfilter_p.h b/src/opengl/qglpixmapfilter_p.h
new file mode 100644
index 0000000000..dc2eea6a4b
--- /dev/null
+++ b/src/opengl/qglpixmapfilter_p.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** 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 QGLPIXMAPFILTER_P_H
+#define QGLPIXMAPFILTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qpixmapfilter_p.h>
+
+#include <QtOpenGL/qgl.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QGLPixelBuffer;
+
+QT_MODULE(OpenGL)
+
+class QGLPixmapFilterBase
+{
+public:
+ virtual ~QGLPixmapFilterBase() {}
+protected:
+ void bindTexture(const QPixmap &src) const;
+ void drawImpl(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect = QRectF()) const;
+
+ virtual bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const = 0;
+};
+
+template <typename Filter>
+class QGLPixmapFilter : public Filter, public QGLPixmapFilterBase
+{
+public:
+ void draw(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect = QRectF()) const {
+ const QRectF source = srcRect.isNull() ? QRectF(src.rect()) : srcRect;
+ if (painter)
+ drawImpl(painter, pos, src, source);
+ }
+};
+
+class Q_OPENGL_EXPORT QGLSLProgram
+{
+public:
+ QGLSLProgram(const QString &src);
+ ~QGLSLProgram();
+
+ bool success() const;
+
+ void enable();
+ void disable();
+
+ int getUniformLocation(const QString &name);
+
+ void setUniform(int uniform, int value);
+ void setUniform(int uniform, qreal value);
+ void setUniform(int uniform, qreal v1, qreal v2);
+ void setUniform(int uniform, qreal v1, qreal v2, qreal v3);
+ void setUniform(int uniform, qreal v1, qreal v2, qreal v3, qreal v4);
+ void setUniform(int uniform, int count, float *v);
+
+private:
+ GLuint m_shader;
+ GLuint m_program;
+
+ GLint m_valid;
+
+ const QGLContext *ctx;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGLPIXMAPFILTER_P_H
diff --git a/src/opengl/qglscreen_qws.cpp b/src/opengl/qglscreen_qws.cpp
new file mode 100644
index 0000000000..a488b972c0
--- /dev/null
+++ b/src/opengl/qglscreen_qws.cpp
@@ -0,0 +1,242 @@
+/****************************************************************************
+**
+** 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 <QGLScreen>
+#include <QGLContext>
+#include <QGLWidget>
+#include "private/qglwindowsurface_qws_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QGLScreenPrivate
+{
+public:
+ QGLScreen::Options options;
+ QGLScreenSurfaceFunctions *functions;
+};
+
+/*!
+ \internal
+ \preliminary
+ \class QGLScreen
+
+ \brief This class encapsulates an OpenGL screen driver.
+*/
+
+QGLScreen::QGLScreen(int displayId)
+ : QScreen(displayId, GLClass), d_ptr(new QGLScreenPrivate)
+{
+ d_ptr->options = NoOptions;
+ d_ptr->functions = new QGLScreenSurfaceFunctions();
+}
+
+QGLScreen::~QGLScreen()
+{
+ delete d_ptr->functions;
+ delete d_ptr;
+}
+
+/*!
+ \since 4.3
+ \obsolete
+
+ Initializes the \a context and sets up the QGLWindowSurface of the
+ QWidget of \a context based on the parameters of \a context and
+ based on its own requirements. The format() of \a context needs
+ to be updated with the actual parameters of the OpenGLES drawable
+ that was set up.
+
+ \a shareContext is used in the same way as for QGLContext. It is
+ the context with which \a context shares display lists and texture
+ ids etc. The window surface must be set up so that this sharing
+ works.
+
+ Returns true in case of success and false if it is not possible to
+ create the necessary OpenGLES drawable/context.
+
+ Since 4.4.2, this function will be not be called if options()
+ indicates that a native window or pixmap drawable can be created
+ via the functions in the surfaceFunctions() object.
+
+ This function is obsolete in Qt 4.5 and higher. Use surfaceFunctions()
+ instead.
+
+ \sa options(), surfaceFunctions()
+*/
+bool
+QGLScreen::chooseContext(QGLContext *context, const QGLContext *shareContext)
+{
+ Q_UNUSED(context);
+ Q_UNUSED(shareContext);
+ return false;
+}
+
+/*!
+ \enum QGLScreen::Option
+ This enum defines options that can be set on QGLScreen instances.
+
+ \value NoOptions There are no special options on the screen. This is the default.
+ \value NativeWindows Native windows can be created with QGLScreenSurfaceFunctions::createNativeWindow().
+ \value NativePixmaps Native pixmaps can be created with QGLScreenSurfaceFunctions::createNativePixmap().
+ \value NativeImages Native images can be created with QGLScreenSurfaceFunctions::createNativeImage().
+ \value Overlays The screen supports GL overlays.
+*/
+
+/*!
+ \since 4.4.2
+
+ Returns the options associated with this QGLScreen.
+
+ \sa setOptions()
+*/
+QGLScreen::Options QGLScreen::options() const
+{
+ return d_ptr->options;
+}
+
+/*!
+ \since 4.4.2
+
+ Sets the options associated with this QGLScreen to \a value.
+
+ \sa options()
+*/
+void QGLScreen::setOptions(QGLScreen::Options value)
+{
+ d_ptr->options = value;
+}
+
+/*!
+ \since 4.4.2
+
+ Returns the surface functions object for this QGLScreen.
+
+ \sa setSurfaceFunctions()
+*/
+QGLScreenSurfaceFunctions *QGLScreen::surfaceFunctions() const
+{
+ return d_ptr->functions;
+}
+
+/*!
+ \since 4.4.2
+
+ Sets the surface functions object for this QGLScreen to \a functions.
+ The QGLScreen will take over ownership of \a functions and delete
+ it when the QGLScreen is deleted.
+
+ \sa setSurfaceFunctions()
+*/
+void QGLScreen::setSurfaceFunctions(QGLScreenSurfaceFunctions *functions)
+{
+ if (functions && functions != d_ptr->functions) {
+ delete d_ptr->functions;
+ d_ptr->functions = functions;
+ }
+}
+
+/*!
+ \internal
+ \preliminary
+ \class QGLScreenSurfaceFunctions
+ \brief The QGLScreenSurfaceFunctions class encapsulates the functions for creating native windows and pixmaps for OpenGL ES.
+*/
+
+/*!
+ \since 4.4.2
+
+ Creates a native OpenGLES drawable for the surface of \a widget and
+ returns it in \a native. Returns true if the OpenGLES drawable could
+ be created, or false if windows are not supported.
+
+ This function will be called if the NativeWindows option is set on
+ the screen.
+
+ \sa createNativePixmap(), createNativeImage(), QGLScreen::options()
+*/
+bool QGLScreenSurfaceFunctions::createNativeWindow(QWidget *widget, EGLNativeWindowType *native)
+{
+ Q_UNUSED(widget);
+ Q_UNUSED(native);
+ return false;
+}
+
+/*!
+ \since 4.4.2
+
+ Creates a native OpenGLES drawable for directly rendering into
+ \a pixmap and returns it in \a native. Returns true if the OpenGLES
+ drawable could be created, or false if direct rendering into pixmaps
+ is not supported.
+
+ This function will be called if the NativePixmaps option is set on
+ the screen.
+
+ \sa createNativeWindow(), createNativeImage(), QGLScreen::options()
+*/
+bool QGLScreenSurfaceFunctions::createNativePixmap(QPixmap *pixmap, EGLNativePixmapType *native)
+{
+ Q_UNUSED(pixmap);
+ Q_UNUSED(native);
+ return false;
+}
+
+/*!
+ \since 4.4.2
+
+ Creates a native OpenGLES drawable for directly rendering into
+ \a image and returns it in \a native. Returns true if the OpenGLES
+ drawable could be created, or false if direct rendering into images
+ is not supported.
+
+ This function will be called if the NativeImages option is set on
+ the screen.
+
+ \sa createNativeWindow(), createNativePixmap(), QGLScreen::options()
+*/
+bool QGLScreenSurfaceFunctions::createNativeImage(QImage *image, EGLNativePixmapType *native)
+{
+ Q_UNUSED(image);
+ Q_UNUSED(native);
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglscreen_qws.h b/src/opengl/qglscreen_qws.h
new file mode 100644
index 0000000000..d045bea37c
--- /dev/null
+++ b/src/opengl/qglscreen_qws.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** 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 QSCREENEGL_P_H
+#define QSCREENEGL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QScreenEGL class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/QScreen>
+#include <QtOpenGL/qgl.h>
+#if defined(QT_OPENGL_ES_2)
+#include <EGL/egl.h>
+#else
+#include <GLES/egl.h>
+#endif
+#if !defined(EGL_VERSION_1_3) && !defined(QEGL_NATIVE_TYPES_DEFINED)
+#undef EGLNativeWindowType
+#undef EGLNativePixmapType
+#undef EGLNativeDisplayType
+typedef NativeWindowType EGLNativeWindowType;
+typedef NativePixmapType EGLNativePixmapType;
+typedef NativeDisplayType EGLNativeDisplayType;
+#define QEGL_NATIVE_TYPES_DEFINED 1
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLScreenPrivate;
+
+class Q_OPENGL_EXPORT QGLScreenSurfaceFunctions
+{
+public:
+ virtual bool createNativeWindow(QWidget *widget, EGLNativeWindowType *native);
+ virtual bool createNativePixmap(QPixmap *pixmap, EGLNativePixmapType *native);
+ virtual bool createNativeImage(QImage *image, EGLNativePixmapType *native);
+};
+
+class Q_OPENGL_EXPORT QGLScreen : public QScreen
+{
+ Q_DECLARE_PRIVATE(QGLScreen)
+public:
+ QGLScreen(int displayId);
+ virtual ~QGLScreen();
+
+ enum Option
+ {
+ NoOptions = 0,
+ NativeWindows = 1,
+ NativePixmaps = 2,
+ NativeImages = 4,
+ Overlays = 8
+ };
+ Q_DECLARE_FLAGS(Options, Option)
+
+ QGLScreen::Options options() const;
+
+ virtual bool chooseContext(QGLContext *context, const QGLContext *shareContext);
+ virtual bool hasOpenGL() = 0;
+
+ QGLScreenSurfaceFunctions *surfaceFunctions() const;
+
+protected:
+ void setOptions(QGLScreen::Options value);
+ void setSurfaceFunctions(QGLScreenSurfaceFunctions *functions);
+
+private:
+ QGLScreenPrivate *d_ptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGLScreen::Options)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSCREENEGL_P_H
diff --git a/src/opengl/qglwindowsurface_qws.cpp b/src/opengl/qglwindowsurface_qws.cpp
new file mode 100644
index 0000000000..40e549b275
--- /dev/null
+++ b/src/opengl/qglwindowsurface_qws.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** 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 <QtGui/QPaintDevice>
+#include <QtGui/QWidget>
+#include <QtOpenGL/QGLWidget>
+#include "private/qglwindowsurface_qws_p.h"
+#include "private/qglpaintdevice_qws_p.h"
+#include "private/qpaintengine_opengl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWSGLWindowSurface
+ \since 4.3
+ \ingroup qws
+ \preliminary
+
+ \brief The QWSGLWindowSurface class provides the drawing area for top-level
+ windows with Qt for Embedded Linux on EGL/OpenGL ES. It also provides the
+ drawing area for \l{QGLWidget}s whether they are top-level windows or
+ children of another QWidget.
+
+ Note that this class is only available in Qt for Embedded Linux and only
+ available if Qt is configured with OpenGL support.
+*/
+
+class QWSGLWindowSurfacePrivate
+{
+public:
+ QWSGLWindowSurfacePrivate() :
+ qglContext(0), ownsContext(false) {}
+
+ QGLContext *qglContext;
+ bool ownsContext;
+};
+
+/*!
+ Constructs an empty QWSGLWindowSurface for the given top-level \a window.
+ The window surface is later initialized from chooseContext() and resources for it
+ is typically allocated in setGeometry().
+*/
+QWSGLWindowSurface::QWSGLWindowSurface(QWidget *window)
+ : QWSWindowSurface(window),
+ d_ptr(new QWSGLWindowSurfacePrivate)
+{
+}
+
+/*!
+ Constructs an empty QWSGLWindowSurface.
+*/
+QWSGLWindowSurface::QWSGLWindowSurface()
+ : d_ptr(new QWSGLWindowSurfacePrivate)
+{
+}
+
+/*!
+ Destroys the QWSGLWindowSurface object and frees any
+ allocated resources.
+ */
+QWSGLWindowSurface::~QWSGLWindowSurface()
+{
+ Q_D(QWSGLWindowSurface);
+ if (d->ownsContext)
+ delete d->qglContext;
+ delete d;
+}
+
+/*!
+ Returns the QGLContext of the window surface.
+*/
+QGLContext *QWSGLWindowSurface::context() const
+{
+ Q_D(const QWSGLWindowSurface);
+ if (!d->qglContext) {
+ QWSGLWindowSurface *that = const_cast<QWSGLWindowSurface*>(this);
+ that->setContext(new QGLContext(QGLFormat::defaultFormat()));
+ that->d_func()->ownsContext = true;
+ }
+ return d->qglContext;
+}
+
+/*!
+ Sets the QGLContext for this window surface to \a context.
+*/
+void QWSGLWindowSurface::setContext(QGLContext *context)
+{
+ Q_D(QWSGLWindowSurface);
+ if (d->ownsContext) {
+ delete d->qglContext;
+ d->ownsContext = false;
+ }
+ d->qglContext = context;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglwindowsurface_qws_p.h b/src/opengl/qglwindowsurface_qws_p.h
new file mode 100644
index 0000000000..740f8793b5
--- /dev/null
+++ b/src/opengl/qglwindowsurface_qws_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** 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 QGLWINDOWSURFACE_QWS_P_H
+#define QGLWINDOWSURFACE_QWS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QWSGLWindowSurface class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+#include <QPaintDevice>
+#include "private/qwindowsurface_qws_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+class QPoint;
+class QRegion;
+class QSize;
+class QWidget;
+class QGLContext;
+
+class QWSGLWindowSurfacePrivate;
+
+class Q_OPENGL_EXPORT QWSGLWindowSurface : public QWSWindowSurface
+{
+ Q_DECLARE_PRIVATE(QWSGLWindowSurface)
+
+public:
+ QWSGLWindowSurface(QWidget *widget);
+ QWSGLWindowSurface();
+ ~QWSGLWindowSurface();
+
+ QGLContext *context() const;
+ void setContext(QGLContext *context);
+
+private:
+ QWSGLWindowSurfacePrivate *d_ptr;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QGLWINDOWSURFACE_QWS_P_H
diff --git a/src/opengl/qgraphicssystem_gl.cpp b/src/opengl/qgraphicssystem_gl.cpp
new file mode 100644
index 0000000000..ce8ad7a398
--- /dev/null
+++ b/src/opengl/qgraphicssystem_gl.cpp
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** 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 "qgraphicssystem_gl_p.h"
+
+#include "private/qpixmap_raster_p.h"
+#include "private/qpixmapdata_gl_p.h"
+#include "private/qwindowsurface_gl_p.h"
+#include <private/qwindowsurface_raster_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern QGLWidget *qt_gl_getShareWidget();
+
+QPixmapData *QGLGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const
+{
+ return new QGLPixmapData(type);
+}
+
+QWindowSurface *QGLGraphicsSystem::createWindowSurface(QWidget *widget) const
+{
+#ifdef Q_WS_WIN
+ // On Windows the QGLWindowSurface class can't handle
+ // drop shadows and native effects, e.g. fading a menu in/out using
+ // top level window opacity.
+ if (widget->windowType() == Qt::Popup)
+ return new QRasterWindowSurface(widget);
+#endif
+
+ return new QGLWindowSurface(widget);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/opengl/qgraphicssystem_gl_p.h b/src/opengl/qgraphicssystem_gl_p.h
new file mode 100644
index 0000000000..9e36cdf140
--- /dev/null
+++ b/src/opengl/qgraphicssystem_gl_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** 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 QGRAPHICSSYSTEM_RASTER_P_H
+#define QGRAPHICSSYSTEM_RASTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qgraphicssystem_p.h"
+
+#include <QMap>
+
+QT_BEGIN_NAMESPACE
+
+class Q_OPENGL_EXPORT QGLGraphicsSystem : public QGraphicsSystem
+{
+public:
+ QGLGraphicsSystem();
+
+ QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
+ QWindowSurface *createWindowSurface(QWidget *widget) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/opengl/qpaintengine_opengl.cpp b/src/opengl/qpaintengine_opengl.cpp
new file mode 100644
index 0000000000..f332a50a73
--- /dev/null
+++ b/src/opengl/qpaintengine_opengl.cpp
@@ -0,0 +1,5787 @@
+/****************************************************************************
+**
+** 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/qtextengine_p.h>
+#include <qdebug.h>
+#include <private/qfontengine_p.h>
+#include <qmath.h>
+#include <private/qmath_p.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qpaintengine_p.h>
+#include "qapplication.h"
+#include "qbrush.h"
+#include "qgl.h"
+#include <private/qgl_p.h>
+#include <private/qpainter_p.h>
+#include "qmap.h"
+#include <private/qpaintengine_opengl_p.h>
+#include <private/qdatabuffer_p.h>
+#include "qpen.h"
+#include "qvarlengtharray.h"
+#include <private/qpainter_p.h>
+#include <qglpixelbuffer.h>
+#include <private/qglpixelbuffer_p.h>
+#include <private/qbezier_p.h>
+#include <qglframebufferobject.h>
+
+#include "private/qtessellator_p.h"
+#include "private/qwindowsurface_gl_p.h"
+
+#include "util/fragmentprograms_p.h"
+
+#ifdef Q_WS_QWS
+#include "private/qglpaintdevice_qws_p.h"
+#include "private/qglwindowsurface_qws_p.h"
+#include "qwsmanager_qws.h"
+#include "private/qwsmanager_p.h"
+#endif
+
+#ifdef QT_OPENGL_ES_1_CL
+#include "qgl_cl_p.h"
+#endif
+
+#define QGL_FUNC_CONTEXT QGLContext *ctx = const_cast<QGLContext *>(drawable.context());
+
+#include <stdlib.h>
+#include "qpaintengine_opengl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+extern QImage qt_imageForBrush(int brushStyle, bool invert); //in qbrush.cpp
+#ifdef QT_MAC_USE_COCOA
+extern void *qt_current_nsopengl_context(); // qgl_mac.mm
+#endif
+
+#define QREAL_MAX 9e100
+#define QREAL_MIN -9e100
+
+extern int qt_next_power_of_two(int v);
+
+#define DISABLE_DEBUG_ONCE
+
+//#define DEBUG_DISPLAY_MASK_TEXTURE
+
+#ifdef DISABLE_DEBUG_ONCE
+#define DEBUG_OVERRIDE(state) ;
+#define DEBUG_ONCE_STR(str) ;
+#define DEBUG_ONCE if (0)
+#else
+static int DEBUG_OVERRIDE_FLAG = 0;
+static bool DEBUG_TEMP_FLAG;
+#define DEBUG_OVERRIDE(state) { state ? ++DEBUG_OVERRIDE_FLAG : --DEBUG_OVERRIDE_FLAG; }
+#define DEBUG_ONCE if ((DEBUG_TEMP_FLAG = DEBUG_OVERRIDE_FLAG) && 0) ; else for (static int DEBUG_ONCE_FLAG = false; !DEBUG_ONCE_FLAG || DEBUG_TEMP_FLAG; DEBUG_ONCE_FLAG = true, DEBUG_TEMP_FLAG = false)
+#define DEBUG_ONCE_STR(str) DEBUG_ONCE qDebug() << (str);
+#endif
+
+static inline void qt_glColor4ubv(unsigned char *col)
+{
+#ifdef QT_OPENGL_ES
+ glColor4f(col[0]/255.0, col[1]/255.0, col[2]/255.0, col[3]/255.0);
+#else
+ glColor4ubv(col);
+#endif
+}
+
+struct QT_PointF {
+ qreal x;
+ qreal y;
+};
+
+void qt_add_rect_to_array(const QRectF &r, q_vertexType *array)
+{
+ qreal left = r.left();
+ qreal right = r.right();
+ qreal top = r.top();
+ qreal bottom = r.bottom();
+
+ array[0] = f2vt(left);
+ array[1] = f2vt(top);
+ array[2] = f2vt(right);
+ array[3] = f2vt(top);
+ array[4] = f2vt(right);
+ array[5] = f2vt(bottom);
+ array[6] = f2vt(left);
+ array[7] = f2vt(bottom);
+}
+
+void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array)
+{
+ array[0] = f2vt(x1);
+ array[1] = f2vt(y1);
+ array[2] = f2vt(x2);
+ array[3] = f2vt(y1);
+ array[4] = f2vt(x2);
+ array[5] = f2vt(y2);
+ array[6] = f2vt(x1);
+ array[7] = f2vt(y2);
+}
+
+struct QGLTrapezoid
+{
+ QGLTrapezoid()
+ {}
+
+ QGLTrapezoid(qreal top_, qreal bottom_, qreal topLeftX_, qreal topRightX_, qreal bottomLeftX_, qreal bottomRightX_)
+ : top(top_),
+ bottom(bottom_),
+ topLeftX(topLeftX_),
+ topRightX(topRightX_),
+ bottomLeftX(bottomLeftX_),
+ bottomRightX(bottomRightX_)
+ {}
+
+ const QGLTrapezoid translated(const QPointF &delta) const;
+
+ qreal top;
+ qreal bottom;
+ qreal topLeftX;
+ qreal topRightX;
+ qreal bottomLeftX;
+ qreal bottomRightX;
+};
+
+const QGLTrapezoid QGLTrapezoid::translated(const QPointF &delta) const
+{
+ QGLTrapezoid trap(*this);
+ trap.top += delta.y();
+ trap.bottom += delta.y();
+ trap.topLeftX += delta.x();
+ trap.topRightX += delta.x();
+ trap.bottomLeftX += delta.x();
+ trap.bottomRightX += delta.x();
+ return trap;
+}
+
+class QGLDrawable {
+public:
+ QGLDrawable() : widget(0), buffer(0), fbo(0)
+ , wsurf(0)
+ {}
+ inline void setDevice(QPaintDevice *pdev);
+ inline void swapBuffers();
+ inline void makeCurrent();
+ inline void doneCurrent();
+ inline QSize size() const;
+ inline QGLFormat format() const;
+ inline GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D, GLint format = GL_RGBA);
+ inline GLuint bindTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D, GLint format = GL_RGBA);
+ inline QColor backgroundColor() const;
+ inline QGLContext *context() const;
+ inline bool autoFillBackground() const;
+
+private:
+ bool wasBound;
+ QGLWidget *widget;
+ QGLPixelBuffer *buffer;
+ QGLFramebufferObject *fbo;
+#ifdef Q_WS_QWS
+ QWSGLWindowSurface *wsurf;
+#else
+ QGLWindowSurface *wsurf;
+#endif
+};
+
+void QGLDrawable::setDevice(QPaintDevice *pdev)
+{
+ wasBound = false;
+ widget = 0;
+ buffer = 0;
+ fbo = 0;
+#ifdef Q_WS_QWS
+ wsurf = 0;
+#endif
+ if (pdev->devType() == QInternal::Widget)
+ widget = static_cast<QGLWidget *>(pdev);
+ else if (pdev->devType() == QInternal::Pbuffer)
+ buffer = static_cast<QGLPixelBuffer *>(pdev);
+ else if (pdev->devType() == QInternal::FramebufferObject)
+ fbo = static_cast<QGLFramebufferObject *>(pdev);
+ else if (pdev->devType() == QInternal::UnknownDevice)
+#ifdef Q_WS_QWS
+ wsurf = static_cast<QWSGLPaintDevice*>(pdev)->windowSurface();
+#else
+ wsurf = static_cast<QGLWindowSurface *>(pdev);
+#endif
+}
+
+inline void QGLDrawable::swapBuffers()
+{
+ if (widget) {
+ if (widget->autoBufferSwap())
+ widget->swapBuffers();
+ } else {
+ glFlush();
+ }
+}
+
+inline void QGLDrawable::makeCurrent()
+{
+ if (widget)
+ widget->makeCurrent();
+ else if (buffer)
+ buffer->makeCurrent();
+ else if (wsurf)
+ wsurf->context()->makeCurrent();
+ else if (fbo) {
+ wasBound = fbo->isBound();
+ if (!wasBound)
+ fbo->bind();
+ }
+}
+
+inline void QGLDrawable::doneCurrent()
+{
+ if (fbo && !wasBound)
+ fbo->release();
+}
+
+inline QSize QGLDrawable::size() const
+{
+ if (widget) {
+ return QSize(widget->d_func()->glcx->device()->width(),
+ widget->d_func()->glcx->device()->height());
+ } else if (buffer) {
+ return buffer->size();
+ } else if (fbo) {
+ return fbo->size();
+ } else if (wsurf) {
+#ifdef Q_WS_QWS
+ return wsurf->window()->frameSize();
+#else
+ return QSize(wsurf->width(), wsurf->height());
+#endif
+ }
+ return QSize();
+}
+
+inline QGLFormat QGLDrawable::format() const
+{
+ if (widget)
+ return widget->format();
+ else if (buffer)
+ return buffer->format();
+ else if (wsurf)
+ return wsurf->context()->format();
+ else if (fbo && QGLContext::currentContext()) {
+ QGLFormat fmt = QGLContext::currentContext()->format();
+ fmt.setStencil(fbo->attachment() == QGLFramebufferObject::CombinedDepthStencil);
+ fmt.setDepth(fbo->attachment() != QGLFramebufferObject::NoAttachment);
+ return fmt;
+ }
+
+ return QGLFormat();
+}
+
+inline GLuint QGLDrawable::bindTexture(const QImage &image, GLenum target, GLint format)
+{
+ if (widget)
+ return widget->d_func()->glcx->d_func()->bindTexture(image, target, format, true);
+ else if (buffer)
+ return buffer->d_func()->qctx->d_func()->bindTexture(image, target, format, true);
+ else if (fbo && QGLContext::currentContext())
+ return const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(image, target, format, true);
+ else if (wsurf)
+ return wsurf->context()->d_func()->bindTexture(image, target, format, true);
+ return 0;
+}
+
+inline GLuint QGLDrawable::bindTexture(const QPixmap &pixmap, GLenum target, GLint format)
+{
+ if (widget)
+ return widget->d_func()->glcx->d_func()->bindTexture(pixmap, target, format, true);
+ else if (buffer)
+ return buffer->d_func()->qctx->d_func()->bindTexture(pixmap, target, format, true);
+ else if (fbo && QGLContext::currentContext())
+ return const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(pixmap, target, format, true);
+ else if (wsurf)
+ return wsurf->context()->d_func()->bindTexture(pixmap, target, format, true);
+ return 0;
+}
+
+inline QColor QGLDrawable::backgroundColor() const
+{
+ if (widget)
+ return widget->palette().brush(widget->backgroundRole()).color();
+ return QApplication::palette().brush(QPalette::Background).color();
+}
+
+inline QGLContext *QGLDrawable::context() const
+{
+ if (widget)
+ return widget->d_func()->glcx;
+ else if (buffer)
+ return buffer->d_func()->qctx;
+ else if (fbo)
+ return const_cast<QGLContext *>(QGLContext::currentContext());
+ else if (wsurf)
+ return wsurf->context();
+ return 0;
+}
+
+inline bool QGLDrawable::autoFillBackground() const
+{
+ if (widget)
+ return widget->autoFillBackground();
+ else
+ return false;
+}
+
+
+class QOpenGLImmediateModeTessellator;
+class QGLMaskGenerator;
+class QGLOffscreen;
+
+class QGLMaskTextureCache
+{
+public:
+ void setOffscreenSize(const QSize &offscreenSize);
+ void setDrawableSize(const QSize &drawableSize);
+
+ struct CacheLocation {
+ QRect rect;
+ int channel;
+
+ QRect screen_rect;
+ };
+
+ struct CacheInfo {
+ inline CacheInfo(const QPainterPath &p, const QTransform &m, qreal w = -1) :
+ path(p), matrix(m), stroke_width(w), age(0) {}
+
+ QPainterPath path;
+ QTransform matrix;
+ qreal stroke_width;
+
+ CacheLocation loc;
+
+ int age;
+ };
+
+ struct QuadTreeNode {
+ quint64 key;
+
+ int largest_available_block;
+ int largest_used_block;
+ };
+
+ CacheLocation getMask(QGLMaskGenerator &maskGenerator, QOpenGLPaintEnginePrivate *engine);
+
+ typedef QMultiHash<quint64, CacheInfo> QGLTextureCacheHash;
+
+ enum {block_size = 64};
+
+ // throw out keys that are too old
+ void maintainCache();
+ void clearCache();
+
+private:
+ quint64 hash(const QPainterPath &p, const QTransform &m, qreal w);
+
+ void createMask(quint64 key, CacheInfo &info, QGLMaskGenerator &maskGenerator);
+
+ QSize offscreenSize;
+ QSize drawableSize;
+
+ QGLTextureCacheHash cache;
+
+ QVector<QuadTreeNode> occupied_quadtree[4];
+
+ void quadtreeUpdate(int channel, int node, int current_block_size);
+ void quadtreeAllocate(quint64 key, const QSize &size, QRect *rect, int *channel);
+
+ bool quadtreeFindAvailableLocation(const QSize &size, QRect *rect, int *channel);
+ void quadtreeFindExistingLocation(const QSize &size, QRect *rect, int *channel);
+
+ void quadtreeInsert(int channel, quint64 key, const QRect &rect, int node = 0);
+ void quadtreeClear(int channel, const QRect &rect, int node = 0);
+
+ int quadtreeBlocksize(int node);
+ QPoint quadtreeLocation(int node);
+
+ QOpenGLPaintEnginePrivate *engine;
+};
+
+Q_GLOBAL_STATIC(QGLMaskTextureCache, qt_mask_texture_cache)
+
+class QGLOffscreen : public QObject
+{
+ Q_OBJECT
+public:
+ QGLOffscreen()
+ : QObject(),
+ offscreen(0),
+ ctx(0),
+ mask_dim(0),
+ activated(false),
+ bound(false)
+ {
+ connect(QGLSignalProxy::instance(),
+ SIGNAL(aboutToDestroyContext(const QGLContext *)),
+ SLOT(cleanupGLContextRefs(const QGLContext *)));
+ }
+
+ inline void setDevice(QPaintDevice *pdev);
+
+ void begin();
+ void end();
+
+ inline void bind();
+ inline void release();
+
+ inline bool isBound() const;
+
+ inline QSize drawableSize() const;
+ inline QSize offscreenSize() const;
+
+ inline GLuint offscreenTexture() const;
+
+ QGLContext *context() const;
+
+ static bool isSupported();
+
+ inline void initialize();
+
+ inline bool isValid() const;
+
+public Q_SLOTS:
+ void cleanupGLContextRefs(const QGLContext *context) {
+ if (context == ctx) {
+ delete offscreen;
+ ctx = 0;
+ offscreen = 0;
+ mask_dim = 0;
+ }
+ }
+
+private:
+ QGLDrawable drawable;
+
+ QGLFramebufferObject *offscreen;
+ QGLContext *ctx;
+
+ // dimensions of mask texture (square)
+ int mask_dim;
+ QSize last_failed_size;
+
+ bool drawable_fbo;
+
+ bool activated;
+ bool initialized;
+
+ bool bound;
+};
+
+inline void QGLOffscreen::setDevice(QPaintDevice *pdev)
+{
+ drawable.setDevice(pdev);
+
+ drawable_fbo = (pdev->devType() == QInternal::FramebufferObject);
+}
+
+void QGLOffscreen::begin()
+{
+#ifndef QT_OPENGL_ES
+ initialized = false;
+
+ if (activated)
+ initialize();
+#endif
+}
+
+void QGLOffscreen::initialize()
+{
+#ifndef QT_OPENGL_ES
+ if (initialized)
+ return;
+
+ activated = true;
+ initialized = true;
+
+ int dim = qMax(2048, static_cast<int>(qt_next_power_of_two(qMax(drawable.size().width(), drawable.size().height()))));
+
+ bool shared_context = qgl_share_reg()->checkSharing(drawable.context(), ctx);
+ bool would_fail = last_failed_size.isValid() &&
+ (drawable.size().width() >= last_failed_size.width() ||
+ drawable.size().height() >= last_failed_size.height());
+ bool needs_refresh = dim > mask_dim || !shared_context;
+
+ if (needs_refresh && !would_fail) {
+ DEBUG_ONCE qDebug() << "QGLOffscreen::initialize(): creating offscreen of size" << dim;
+ delete offscreen;
+ offscreen = new QGLFramebufferObject(dim, dim, GLenum(GL_TEXTURE_2D));
+ mask_dim = dim;
+
+ if (!offscreen->isValid()) {
+ qWarning("QGLOffscreen: Invalid offscreen fbo (size %dx%d)", mask_dim, mask_dim);
+ delete offscreen;
+ offscreen = 0;
+ mask_dim = 0;
+ last_failed_size = drawable.size();
+ }
+ }
+
+ qt_mask_texture_cache()->setOffscreenSize(offscreenSize());
+ qt_mask_texture_cache()->setDrawableSize(drawable.size());
+ ctx = drawable.context();
+#endif
+}
+
+inline bool QGLOffscreen::isValid() const
+{
+ return offscreen;
+}
+
+void QGLOffscreen::end()
+{
+ if (bound)
+ release();
+#ifdef DEBUG_DISPLAY_MASK_TEXTURE
+ glReadBuffer(GL_BACK);
+ glDrawBuffer(GL_BACK);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glColor4f(1, 1, 1, 1);
+ glDisable(GL_DEPTH_TEST);
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, offscreen->texture());
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.0, 1.0); glVertex2f(0.0, 0.0);
+ glTexCoord2f(1.0, 1.0); glVertex2f(drawable.size().width(), 0.0);
+ glTexCoord2f(1.0, 0.0); glVertex2f(drawable.size().width(), drawable.size().height());
+ glTexCoord2f(0.0, 0.0); glVertex2f(0.0, drawable.size().height());
+ glEnd();
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+#endif
+}
+
+inline void QGLOffscreen::bind()
+{
+#ifndef QT_OPENGL_ES
+ Q_ASSERT(initialized);
+
+ if (!offscreen || bound)
+ return;
+
+ DEBUG_ONCE qDebug() << "QGLOffscreen: binding offscreen";
+ offscreen->bind();
+
+ bound = true;
+
+ glViewport(0, 0, offscreenSize().width(), offscreenSize().height());
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, offscreenSize().width(), offscreenSize().height(), 0, -999999, 999999);
+ glMatrixMode(GL_MODELVIEW);
+#endif
+}
+
+inline void QGLOffscreen::release()
+{
+#ifndef QT_OPENGL_ES
+ if (!offscreen || !bound)
+ return;
+
+#ifdef Q_WS_X11
+ // workaround for bug in nvidia driver versions 9x.xx
+ if (QGLExtensions::nvidiaFboNeedsFinish)
+ glFinish();
+#endif
+
+ DEBUG_ONCE_STR("QGLOffscreen: releasing offscreen");
+
+ if (drawable_fbo)
+ drawable.makeCurrent();
+ else
+ offscreen->release();
+
+ QSize sz(drawable.size());
+ glViewport(0, 0, sz.width(), sz.height());
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+#ifndef QT_OPENGL_ES
+ glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
+#else
+ glOrthof(0, sz.width(), sz.height(), 0, -999999, 999999);
+#endif
+ glMatrixMode(GL_MODELVIEW);
+
+ bound = false;
+#endif
+}
+
+inline bool QGLOffscreen::isBound() const
+{
+ return bound;
+}
+
+inline QSize QGLOffscreen::drawableSize() const
+{
+ return drawable.size();
+}
+
+inline QSize QGLOffscreen::offscreenSize() const
+{
+ return QSize(mask_dim, mask_dim);
+}
+
+inline GLuint QGLOffscreen::offscreenTexture() const
+{
+ return offscreen ? offscreen->texture() : 0;
+}
+
+inline QGLContext *QGLOffscreen::context() const
+{
+ return ctx;
+}
+
+bool QGLOffscreen::isSupported()
+{
+ return (QGLExtensions::glExtensions & QGLExtensions::FramebufferObject); // for fbo
+}
+
+struct QDrawQueueItem
+{
+ QDrawQueueItem(qreal _opacity,
+ QBrush _brush,
+ const QPointF &_brush_origion,
+ QPainter::CompositionMode _composition_mode,
+ const QTransform &_matrix,
+ QGLMaskTextureCache::CacheLocation _location)
+ : opacity(_opacity),
+ brush(_brush),
+ brush_origin(_brush_origion),
+ composition_mode(_composition_mode),
+ matrix(_matrix),
+ location(_location) {}
+ qreal opacity;
+ QBrush brush;
+ QPointF brush_origin;
+ QPainter::CompositionMode composition_mode;
+
+ QTransform matrix;
+ QGLMaskTextureCache::CacheLocation location;
+};
+
+////////// GL program cache: start
+
+typedef struct {
+ int brush; // brush index or mask index
+ int mode; // composition mode index
+ bool mask;
+ GLuint program;
+} GLProgram;
+
+typedef QMultiHash<const QGLContext *, GLProgram> QGLProgramHash;
+
+class QGLProgramCache : public QObject
+{
+ Q_OBJECT
+public:
+ QGLProgramCache() {
+ // we have to know when a context is deleted so we can free
+ // any program handles it holds
+ connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext *)),
+ SLOT(cleanupPrograms(const QGLContext *)));
+
+ }
+ ~QGLProgramCache() {
+ // at this point the cache should contain 0 elements
+ // Q_ASSERT(program.size() == 0);
+ }
+
+ GLuint getProgram(const QGLContext *ctx, int brush, int mode, bool mask_mode)
+ {
+ // 1. see if we have an entry for the ctx context
+ QList<GLProgram> progs = programs.values(ctx);
+ for (int i=0; i<progs.size(); ++i) {
+ const GLProgram &prg = progs.at(i);
+ if (mask_mode) {
+ if (prg.mask && prg.brush == brush)
+ return prg.program;
+ } else {
+ if (!prg.mask && prg.brush == brush && prg.mode == mode)
+ return prg.program;
+ }
+ }
+
+ // 2. try to find a match in a shared context, and update the
+ // hash with the entry found
+ QList<const QGLContext *> contexts = programs.uniqueKeys();
+ for (int i=0; i<contexts.size(); ++i) {
+ const QGLContext *cx = contexts.at(i);
+ if (cx != ctx && qgl_share_reg()->checkSharing(cx, ctx)) {
+ QList<GLProgram> progs = programs.values(cx);
+ for (int k=0; k<progs.size(); ++k) {
+ const GLProgram &prg = progs.at(k);
+ if (mask_mode) {
+ if (prg.mask && prg.brush == brush) {
+ programs.insert(ctx, prg);
+ return prg.program;
+ }
+ } else {
+ if (!prg.mask && prg.brush == brush && prg.mode == mode) {
+ programs.insert(ctx, prg);
+ return prg.program;
+ }
+ }
+ }
+ }
+ }
+
+ // 3. compile a new program and place it into the cache
+ // NB! assumes ctx is the current GL context
+ GLProgram prg;
+ prg.brush = brush;
+ prg.mode = mode;
+ prg.mask = mask_mode;
+ glGenProgramsARB(1, &prg.program);
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, prg.program);
+ const char *src = mask_mode
+ ? mask_fragment_program_sources[brush]
+ : painter_fragment_program_sources[brush][mode];
+ // necessary for .NET 2002, apparently
+ const GLbyte *gl_src = reinterpret_cast<const GLbyte *>(src);
+
+ while (glGetError() != GL_NO_ERROR) {} // reset error state
+ glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,
+ int(strlen(src)), gl_src);
+ if (glGetError() != GL_NO_ERROR) {
+// qDebug() << "QGLProgramCache: Unable to compile fragment program.";
+ glDeleteProgramsARB(1, &prg.program);
+ return 0;
+ }
+
+// qDebug() << "QGLProgramCache: Creating GL program:" << prg.program << hex << ctx;
+ programs.insert(ctx, prg);
+ return prg.program;
+ }
+
+public Q_SLOTS:
+ void cleanupPrograms(const QGLContext *context)
+ {
+ QGLProgramHash::iterator it = programs.begin();
+ while (it != programs.end()) {
+ if (it.key() == context) {
+ if (!context->isSharing()) {
+ // the ctx variable below is needed for the glDeleteProgramARB call
+ // since it is resolved from our extension system
+ // NB! assumes context is the current GL context
+ const QGLContext *ctx = context;
+ // qDebug() << "QGLProgramHash: Deleting GL program:" << it.value().program << hex << it.key();
+ glDeleteProgramsARB(1, &it.value().program);
+ }
+ it = programs.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+private:
+ QGLProgramHash programs;
+};
+
+Q_GLOBAL_STATIC(QGLProgramCache, qt_gl_program_cache)
+
+////////// GL program cache: end
+
+class QOpenGLPaintEnginePrivate;
+class QGLPrivateCleanup : public QObject
+{
+ Q_OBJECT
+public:
+ QGLPrivateCleanup(QOpenGLPaintEnginePrivate *priv)
+ : p(priv)
+ {
+ connect(QGLSignalProxy::instance(),
+ SIGNAL(aboutToDestroyContext(const QGLContext *)),
+ SLOT(cleanupGLContextRefs(const QGLContext *)));
+ }
+
+public Q_SLOTS:
+ void cleanupGLContextRefs(const QGLContext *context);
+
+private:
+ QOpenGLPaintEnginePrivate *p;
+};
+
+class QOpenGLPaintEnginePrivate : public QPaintEngineExPrivate
+{
+ Q_DECLARE_PUBLIC(QOpenGLPaintEngine)
+public:
+ QOpenGLPaintEnginePrivate()
+ : opacity(1)
+ , composition_mode(QPainter::CompositionMode_SourceOver)
+ , has_fast_pen(false)
+ , use_stencil_method(false)
+ , dirty_drawable_texture(false)
+ , has_stencil_face_ext(false)
+ , use_fragment_programs(false)
+ , high_quality_antialiasing(false)
+ , use_smooth_pixmap_transform(false)
+ , use_emulation(false)
+ , txop(QTransform::TxNone)
+ , inverseScale(1)
+ , moveToCount(0)
+ , shader_ctx(0)
+ , grad_palette(0)
+ , drawable_texture(0)
+ , ref_cleaner(this)
+ {}
+
+ inline void setGLPen(const QColor &c) {
+ uint alpha = qRound(c.alpha() * opacity);
+ pen_color[0] = qt_div_255(c.red() * alpha);
+ pen_color[1] = qt_div_255(c.green() * alpha);
+ pen_color[2] = qt_div_255(c.blue() * alpha);
+ pen_color[3] = alpha;
+ }
+
+ inline void setGLBrush(const QColor &c) {
+ uint alpha = qRound(c.alpha() * opacity);
+ brush_color[0] = qt_div_255(c.red() * alpha);
+ brush_color[1] = qt_div_255(c.green() * alpha);
+ brush_color[2] = qt_div_255(c.blue() * alpha);
+ brush_color[3] = alpha;
+ }
+
+ inline void setGradientOps(const QBrush &brush, const QRectF &bounds);
+ void createGradientPaletteTexture(const QGradient& g);
+
+ void updateGradient(const QBrush &brush, const QRectF &bounds);
+
+ inline void lineToStencil(qreal x, qreal y);
+ inline void curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep);
+ void pathToVertexArrays(const QPainterPath &path);
+ void fillVertexArray(Qt::FillRule fillRule);
+ void drawVertexArrays();
+ void fillPath(const QPainterPath &path);
+ void fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
+ Qt::FillRule fill);
+
+ void drawFastRect(const QRectF &rect);
+ void strokePath(const QPainterPath &path, bool use_cache);
+ void strokePathFastPen(const QPainterPath &path, bool needsResolving);
+ void strokeLines(const QPainterPath &path);
+
+ void updateDepthClip();
+ void systemStateChanged();
+
+ void cleanupGLContextRefs(const QGLContext *context) {
+ if (context == shader_ctx)
+ shader_ctx = 0;
+ }
+
+ inline void updateFastPen() {
+ qreal pen_width = cpen.widthF();
+ has_fast_pen =
+ ((pen_width == 0 || (pen_width <= 1 && matrix.type() <= QTransform::TxTranslate))
+ || cpen.isCosmetic())
+ && cpen.style() == Qt::SolidLine
+ && cpen.isSolid();
+
+ }
+
+ void disableClipping();
+ void enableClipping();
+ void ensureDrawableTexture();
+
+ QPen cpen;
+ QBrush cbrush;
+ Qt::BrushStyle brush_style;
+ QPointF brush_origin;
+ Qt::BrushStyle pen_brush_style;
+ qreal opacity;
+ QPainter::CompositionMode composition_mode;
+
+ Qt::BrushStyle current_style;
+
+ uint has_pen : 1;
+ uint has_brush : 1;
+ uint has_fast_pen : 1;
+ uint use_stencil_method : 1;
+ uint dirty_stencil : 1;
+ uint dirty_drawable_texture : 1;
+ uint has_stencil_face_ext : 1;
+ uint use_fragment_programs : 1;
+ uint high_quality_antialiasing : 1;
+ uint has_antialiasing : 1;
+ uint has_fast_composition_mode : 1;
+ uint use_smooth_pixmap_transform : 1;
+ uint use_system_clip : 1;
+ uint use_emulation : 1;
+
+ void updateUseEmulation();
+
+ QTransform matrix;
+ GLubyte pen_color[4];
+ GLubyte brush_color[4];
+ QTransform::TransformationType txop;
+ QGLDrawable drawable;
+ QGLOffscreen offscreen;
+
+ qreal inverseScale;
+
+ int moveToCount;
+ QPointF path_start;
+
+ bool isFastRect(const QRectF &r);
+
+ void drawImageAsPath(const QRectF &r, const QImage &img, const QRectF &sr);
+ void drawTiledImageAsPath(const QRectF &r, const QImage &img, qreal sx, qreal sy);
+
+ void drawOffscreenPath(const QPainterPath &path);
+
+ void composite(const QRectF &rect, const QPoint &maskOffset = QPoint());
+ void composite(GLuint primitive, const q_vertexType *vertexArray, int vertexCount, const QPoint &maskOffset = QPoint());
+
+ bool createFragmentPrograms();
+ void deleteFragmentPrograms();
+ void updateFragmentProgramData(int locations[]);
+
+ void cacheItemErased(int channel, const QRect &rect);
+
+ void addItem(const QGLMaskTextureCache::CacheLocation &location);
+ void drawItem(const QDrawQueueItem &item);
+ void flushDrawQueue();
+
+ void copyDrawable(const QRectF &rect);
+
+ void updateGLMatrix() const;
+
+ QGLContext *shader_ctx;
+ GLuint grad_palette;
+
+ GLuint painter_fragment_programs[num_fragment_brushes][num_fragment_composition_modes];
+ GLuint mask_fragment_programs[num_fragment_masks];
+
+ float inv_matrix_data[3][4];
+ float fmp_data[4];
+ float fmp2_m_radius2_data[4];
+ float angle_data[4];
+ float linear_data[4];
+
+ float porterduff_ab_data[4];
+ float porterduff_xyz_data[4];
+
+ float mask_offset_data[4];
+ float mask_channel_data[4];
+
+ FragmentBrushType fragment_brush;
+ FragmentCompositionModeType fragment_composition_mode;
+
+ void setPorterDuffData(float a, float b, float x, float y, float z);
+ void setInvMatrixData(const QTransform &inv_matrix);
+
+ qreal max_x;
+ qreal max_y;
+ qreal min_x;
+ qreal min_y;
+
+ QDataBuffer<QPointF> tess_points;
+ QVector<int> tess_points_stops;
+
+ GLdouble projection_matrix[4][4];
+
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
+ GLfloat mv_matrix[4][4];
+#else
+ GLdouble mv_matrix[4][4];
+#endif
+
+ QList<QDrawQueueItem> drawQueue;
+
+ GLuint drawable_texture;
+ QSize drawable_texture_size;
+
+ int max_texture_size;
+
+ QGLPrivateCleanup ref_cleaner;
+ friend class QGLMaskTextureCache;
+};
+
+class QOpenGLCoordinateOffset
+{
+public:
+ QOpenGLCoordinateOffset(QOpenGLPaintEnginePrivate *d);
+ ~QOpenGLCoordinateOffset();
+
+ static void enableOffset(QOpenGLPaintEnginePrivate *d);
+ static void disableOffset(QOpenGLPaintEnginePrivate *d);
+
+private:
+ QOpenGLPaintEnginePrivate *d;
+};
+
+QOpenGLCoordinateOffset::QOpenGLCoordinateOffset(QOpenGLPaintEnginePrivate *d_)
+ : d(d_)
+{
+ enableOffset(d);
+}
+
+void QOpenGLCoordinateOffset::enableOffset(QOpenGLPaintEnginePrivate *d)
+{
+ if (!d->has_antialiasing) {
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ d->mv_matrix[3][0] += 0.5;
+ d->mv_matrix[3][1] += 0.5;
+ d->updateGLMatrix();
+ }
+}
+
+QOpenGLCoordinateOffset::~QOpenGLCoordinateOffset()
+{
+ disableOffset(d);
+}
+
+void QOpenGLCoordinateOffset::disableOffset(QOpenGLPaintEnginePrivate *d)
+{
+ if (!d->has_antialiasing) {
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ d->mv_matrix[3][0] -= 0.5;
+ d->mv_matrix[3][1] -= 0.5;
+ }
+}
+
+void QGLPrivateCleanup::cleanupGLContextRefs(const QGLContext *context)
+{
+ p->cleanupGLContextRefs(context);
+}
+
+
+static inline void updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform)
+{
+ if (smoothPixmapTransform) {
+ glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+ glTexParameterf(target, GL_TEXTURE_WRAP_S, wrapMode);
+ glTexParameterf(target, GL_TEXTURE_WRAP_T, wrapMode);
+}
+
+static inline QPainterPath strokeForPath(const QPainterPath &path, const QPen &cpen) {
+ QPainterPathStroker stroker;
+ if (cpen.style() == Qt::CustomDashLine)
+ stroker.setDashPattern(cpen.dashPattern());
+ else
+ stroker.setDashPattern(cpen.style());
+
+ stroker.setCapStyle(cpen.capStyle());
+ stroker.setJoinStyle(cpen.joinStyle());
+ stroker.setMiterLimit(cpen.miterLimit());
+
+ qreal width = cpen.widthF();
+ if (width == 0)
+ stroker.setWidth(1);
+ else
+ stroker.setWidth(width);
+
+ QPainterPath stroke = stroker.createStroke(path);
+ stroke.setFillRule(Qt::WindingFill);
+ return stroke;
+}
+
+class QGLStrokeCache
+{
+ struct CacheInfo
+ {
+ inline CacheInfo(QPainterPath p, QPainterPath sp, QPen stroke_pen) :
+ path(p), stroked_path(sp), pen(stroke_pen) {}
+ QPainterPath path;
+ QPainterPath stroked_path;
+ QPen pen;
+ };
+
+ typedef QMultiHash<quint64, CacheInfo> QGLStrokeTableHash;
+
+public:
+ inline QPainterPath getStrokedPath(const QPainterPath &path, const QPen &pen) {
+ quint64 hash_val = 0;
+
+ for (int i = 0; i < path.elementCount() && i <= 2; i++) {
+ hash_val += quint64(path.elementAt(i).x);
+ hash_val += quint64(path.elementAt(i).y);
+ }
+
+ QGLStrokeTableHash::const_iterator it = cache.constFind(hash_val);
+
+ if (it == cache.constEnd())
+ return addCacheElement(hash_val, path, pen);
+ else {
+ do {
+ const CacheInfo &cache_info = it.value();
+ if (cache_info.path == path && cache_info.pen == pen)
+ return cache_info.stroked_path;
+ ++it;
+ } while (it != cache.constEnd() && it.key() == hash_val);
+ // an exact match for this path was not found, create new cache element
+ return addCacheElement(hash_val, path, pen);
+ }
+ }
+
+protected:
+ inline int maxCacheSize() const { return 500; }
+ QPainterPath addCacheElement(quint64 hash_val, QPainterPath path, const QPen &pen) {
+ if (cache.size() == maxCacheSize()) {
+ int elem_to_remove = qrand() % maxCacheSize();
+ cache.remove(cache.keys()[elem_to_remove]); // may remove more than 1, but OK
+ }
+ QPainterPath stroke = strokeForPath(path, pen);
+ CacheInfo cache_entry(path, stroke, pen);
+ return cache.insert(hash_val, cache_entry).value().stroked_path;
+ }
+
+ QGLStrokeTableHash cache;
+};
+
+Q_GLOBAL_STATIC(QGLStrokeCache, qt_opengl_stroke_cache)
+
+class QGLGradientCache : public QObject
+{
+ Q_OBJECT
+ struct CacheInfo
+ {
+ inline CacheInfo(QGradientStops s, qreal op, QGradient::InterpolationMode mode) :
+ stops(s), opacity(op), interpolationMode(mode) {}
+
+ GLuint texId;
+ QGradientStops stops;
+ qreal opacity;
+ QGradient::InterpolationMode interpolationMode;
+ };
+
+ typedef QMultiHash<quint64, CacheInfo> QGLGradientColorTableHash;
+
+public:
+ QGLGradientCache() : QObject(), buffer_ctx(0)
+ {
+ connect(QGLSignalProxy::instance(),
+ SIGNAL(aboutToDestroyContext(const QGLContext *)),
+ SLOT(cleanupGLContextRefs(const QGLContext *)));
+ }
+
+ inline GLuint getBuffer(const QGradient &gradient, qreal opacity, QGLContext *ctx) {
+ if (buffer_ctx && !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);
+ }
+ }
+
+ inline int paletteSize() const { return 1024; }
+
+protected:
+ inline int maxCacheSize() const { return 60; }
+ inline void generateGradientColorTable(const QGradient& g,
+ uint *colorTable,
+ int size, qreal opacity) const;
+ GLuint addCacheElement(quint64 hash_val, const QGradient &gradient, qreal opacity) {
+ if (cache.size() == maxCacheSize()) {
+ int elem_to_remove = qrand() % maxCacheSize();
+ quint64 key = cache.keys()[elem_to_remove];
+
+ // need to call glDeleteTextures on each removed cache entry:
+ QGLGradientColorTableHash::const_iterator it = cache.constFind(key);
+ do {
+ glDeleteTextures(1, &it.value().texId);
+ } while (++it != cache.constEnd() && it.key() == key);
+ cache.remove(key); // may remove more than 1, but OK
+ }
+ CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
+ uint buffer[1024];
+ generateGradientColorTable(gradient, buffer, paletteSize(), opacity);
+ glGenTextures(1, &cache_entry.texId);
+#ifndef QT_OPENGL_ES
+ glBindTexture(GL_TEXTURE_1D, cache_entry.texId);
+ glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, paletteSize(),
+ 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer);
+#else
+ // create 2D one-line texture instead. This requires an impl of manual GL_TEXGEN for all primitives
+#endif
+ return cache.insert(hash_val, cache_entry).value().texId;
+ }
+
+ void cleanCache() {
+ QGLGradientColorTableHash::const_iterator it = cache.constBegin();
+ for (; it != cache.constEnd(); ++it) {
+ const CacheInfo &cache_info = it.value();
+ glDeleteTextures(1, &cache_info.texId);
+ }
+ cache.clear();
+ }
+
+ QGLGradientColorTableHash cache;
+
+ QGLContext *buffer_ctx;
+
+public Q_SLOTS:
+ void cleanupGLContextRefs(const QGLContext *context) {
+ if (context == buffer_ctx) {
+ cleanCache();
+ buffer_ctx = 0;
+ }
+ }
+};
+
+static inline uint endianColor(uint c)
+{
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ return c;
+#else
+ return ( (c << 24) & 0xff000000)
+ | ((c >> 24) & 0x000000ff)
+ | ((c << 8) & 0x00ff0000)
+ | ((c >> 8) & 0x0000ff00);
+#endif // Q_BYTE_ORDER
+}
+
+void QGLGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, qreal opacity) const
+{
+ int pos = 0;
+ QGradientStops s = gradient.stops();
+ QVector<uint> colors(s.size());
+
+ for (int i = 0; i < s.size(); ++i)
+ colors[i] = s[i].second.rgba();
+
+ bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
+
+ uint alpha = qRound(opacity * 256);
+ uint current_color = ARGB_COMBINE_ALPHA(colors[0], alpha);
+ qreal incr = 1.0 / qreal(size);
+ qreal fpos = 1.5 * incr;
+ colorTable[pos++] = endianColor(PREMUL(current_color));
+
+ while (fpos <= s.first().first) {
+ colorTable[pos] = colorTable[pos - 1];
+ pos++;
+ fpos += incr;
+ }
+
+ if (colorInterpolation)
+ current_color = PREMUL(current_color);
+
+ for (int i = 0; i < s.size() - 1; ++i) {
+ qreal delta = 1/(s[i+1].first - s[i].first);
+ uint next_color = ARGB_COMBINE_ALPHA(colors[i+1], alpha);
+ if (colorInterpolation)
+ next_color = PREMUL(next_color);
+
+ while (fpos < s[i+1].first && pos < size) {
+ int dist = int(256 * ((fpos - s[i].first) * delta));
+ int idist = 256 - dist;
+ if (colorInterpolation)
+ colorTable[pos] = endianColor(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
+ else
+ colorTable[pos] = endianColor(PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)));
+ ++pos;
+ fpos += incr;
+ }
+ current_color = next_color;
+ }
+
+ Q_ASSERT(s.size() > 0);
+
+ uint last_color = endianColor(PREMUL(ARGB_COMBINE_ALPHA(colors[s.size() - 1], alpha)));
+ for (;pos < size; ++pos)
+ colorTable[pos] = last_color;
+
+ // Make sure the last color stop is represented at the end of the table
+ colorTable[size-1] = last_color;
+}
+
+#ifndef Q_WS_QWS
+Q_GLOBAL_STATIC(QGLGradientCache, qt_opengl_gradient_cache)
+#endif
+
+void QOpenGLPaintEnginePrivate::createGradientPaletteTexture(const QGradient& g)
+{
+#ifdef QT_OPENGL_ES //###
+ Q_UNUSED(g);
+#else
+ GLuint texId = qt_opengl_gradient_cache()->getBuffer(g, opacity, drawable.context());
+ glBindTexture(GL_TEXTURE_1D, texId);
+ grad_palette = texId;
+ if (g.spread() == QGradient::RepeatSpread || g.type() == QGradient::ConicalGradient)
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ else if (g.spread() == QGradient::ReflectSpread)
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT_IBM);
+ else
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+#endif
+}
+
+
+inline void QOpenGLPaintEnginePrivate::setGradientOps(const QBrush &brush, const QRectF &bounds)
+{
+ current_style = brush.style();
+
+ if (current_style < Qt::LinearGradientPattern || current_style > Qt::ConicalGradientPattern) {
+ setGLBrush(brush.color());
+ qt_glColor4ubv(brush_color);
+ }
+
+ updateGradient(brush, bounds);
+
+#ifndef QT_OPENGL_ES //### GLES does not have GL_TEXTURE_GEN_ so we are falling back for gradients
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_1D);
+
+ if (current_style == Qt::LinearGradientPattern) {
+ if (high_quality_antialiasing || !has_fast_composition_mode) {
+ fragment_brush = FRAGMENT_PROGRAM_BRUSH_LINEAR;
+ } else {
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_1D);
+ }
+ } else {
+ if (use_fragment_programs) {
+ if (current_style == Qt::RadialGradientPattern)
+ fragment_brush = FRAGMENT_PROGRAM_BRUSH_RADIAL;
+ else if (current_style == Qt::ConicalGradientPattern)
+ fragment_brush = FRAGMENT_PROGRAM_BRUSH_CONICAL;
+ else if (current_style == Qt::SolidPattern)
+ fragment_brush = FRAGMENT_PROGRAM_BRUSH_SOLID;
+ else if (current_style == Qt::TexturePattern)
+ fragment_brush = FRAGMENT_PROGRAM_BRUSH_TEXTURE;
+ else
+ fragment_brush = FRAGMENT_PROGRAM_BRUSH_PATTERN;
+ }
+ }
+#endif
+}
+
+QOpenGLPaintEngine::QOpenGLPaintEngine()
+ : QPaintEngineEx(*(new QOpenGLPaintEnginePrivate))
+{
+}
+
+QOpenGLPaintEngine::~QOpenGLPaintEngine()
+{
+}
+
+bool QOpenGLPaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ d->drawable.setDevice(pdev);
+ d->offscreen.setDevice(pdev);
+ d->has_fast_pen = false;
+ d->inverseScale = 1;
+ d->opacity = 1;
+ d->drawable.makeCurrent();
+ d->matrix = QTransform();
+ d->has_antialiasing = false;
+ d->high_quality_antialiasing = false;
+ d->dirty_stencil = true;
+
+ d->use_emulation = false;
+
+ for (int i = 0; i < 4; ++i)
+ for (int j = 0; j < 4; ++j)
+ d->mv_matrix[i][j] = (i == j ? qreal(1) : qreal(0));
+
+ bool has_frag_program = (QGLExtensions::glExtensions & QGLExtensions::FragmentProgram)
+ && (pdev->devType() != QInternal::Pixmap);
+
+ QGLContext *ctx = const_cast<QGLContext *>(d->drawable.context());
+ if (!ctx) {
+ qWarning() << "QOpenGLPaintEngine: paint device doesn't have a valid GL context.";
+ return false;
+ }
+
+ if (has_frag_program)
+ has_frag_program = qt_resolve_frag_program_extensions(ctx) && qt_resolve_version_1_3_functions(ctx);
+
+ d->use_stencil_method = d->drawable.format().stencil()
+ && (QGLExtensions::glExtensions & QGLExtensions::StencilWrap);
+ if (d->drawable.format().directRendering()
+ && (d->use_stencil_method && QGLExtensions::glExtensions & QGLExtensions::StencilTwoSide))
+ d->has_stencil_face_ext = qt_resolve_stencil_face_extension(ctx);
+
+#ifndef QT_OPENGL_ES
+ if (!ctx->d_ptr->internal_context) {
+ glGetDoublev(GL_PROJECTION_MATRIX, &d->projection_matrix[0][0]);
+ glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
+ glPushAttrib(GL_ALL_ATTRIB_BITS);
+
+ glDisableClientState(GL_EDGE_FLAG_ARRAY);
+ glDisableClientState(GL_INDEX_ARRAY);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ glDisable(GL_TEXTURE_1D);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glPixelTransferi(GL_MAP_COLOR, false);
+ glPixelTransferi(GL_MAP_STENCIL, false);
+ glDisable(GL_TEXTURE_GEN_S);
+
+ glPixelStorei(GL_PACK_SWAP_BYTES, false);
+ glPixelStorei(GL_PACK_LSB_FIRST, false);
+ glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_PACK_SKIP_ROWS, 0);
+ glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
+ glPixelStorei(GL_PACK_ALIGNMENT, 4);
+
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, false);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, false);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+
+ if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2) {
+ glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
+ glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
+ glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
+ glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
+ }
+ }
+#endif
+
+ if (!ctx->d_ptr->internal_context) {
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ glLoadIdentity();
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ if (QGLExtensions::glExtensions & QGLExtensions::SampleBuffers)
+ glDisable(GL_MULTISAMPLE);
+ glDisable(GL_TEXTURE_2D);
+ if (QGLExtensions::glExtensions & QGLExtensions::TextureRectangle)
+ glDisable(GL_TEXTURE_RECTANGLE_NV);
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_LIGHTING);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ }
+
+ d->offscreen.begin();
+
+ const QColor &c = d->drawable.backgroundColor();
+ glClearColor(c.redF(), c.greenF(), c.blueF(), 1.0);
+ if (d->drawable.context()->d_func()->clear_on_painter_begin && d->drawable.autoFillBackground()) {
+ GLbitfield clearBits = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
+#ifndef QT_OPENGL_ES
+ clearBits |= GL_ACCUM_BUFFER_BIT;
+#endif
+ glClear(clearBits);
+ }
+
+ QSize sz(d->drawable.size());
+ glViewport(0, 0, sz.width(), sz.height()); // XXX (Embedded): We need a solution for GLWidgets that draw in a part or a bigger surface...
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+#ifdef QT_OPENGL_ES
+ glOrthof(0, sz.width(), sz.height(), 0, -999999, 999999);
+#else
+ glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
+#endif
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glEnable(GL_BLEND);
+ d->composition_mode = QPainter::CompositionMode_SourceOver;
+
+#ifdef QT_OPENGL_ES
+ d->max_texture_size = ctx->d_func()->maxTextureSize();
+#else
+ bool shared_ctx = qgl_share_reg()->checkSharing(d->drawable.context(), d->shader_ctx);
+
+ if (shared_ctx) {
+ d->max_texture_size = d->shader_ctx->d_func()->maxTextureSize();
+ } else {
+ d->max_texture_size = ctx->d_func()->maxTextureSize();
+
+ if (d->shader_ctx) {
+ d->shader_ctx->makeCurrent();
+ glBindTexture(GL_TEXTURE_1D, 0);
+ glDeleteTextures(1, &d->grad_palette);
+
+ if (has_frag_program && d->use_fragment_programs)
+ glDeleteTextures(1, &d->drawable_texture);
+ ctx->makeCurrent();
+ }
+ d->shader_ctx = d->drawable.context();
+ glGenTextures(1, &d->grad_palette);
+
+ qt_mask_texture_cache()->clearCache();
+ d->use_fragment_programs = has_frag_program;
+ }
+
+ if (d->use_fragment_programs && (!shared_ctx || sz.width() > d->drawable_texture_size.width()
+ || sz.height() > d->drawable_texture_size.height()))
+ {
+ // delete old texture if size has increased, otherwise it was deleted earlier
+ if (shared_ctx)
+ glDeleteTextures(1, &d->drawable_texture);
+
+ d->dirty_drawable_texture = true;
+ d->drawable_texture_size = QSize(qt_next_power_of_two(sz.width()),
+ qt_next_power_of_two(sz.height()));
+ }
+#endif
+
+ updateClipRegion(QRegion(), Qt::NoClip);
+ penChanged();
+ brushChanged();
+ opacityChanged();
+ compositionModeChanged();
+ renderHintsChanged();
+ transformChanged();
+ return true;
+}
+
+bool QOpenGLPaintEngine::end()
+{
+ Q_D(QOpenGLPaintEngine);
+ d->flushDrawQueue();
+ d->offscreen.end();
+ QGLContext *ctx = const_cast<QGLContext *>(d->drawable.context());
+ if (!ctx->d_ptr->internal_context) {
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ }
+#ifndef QT_OPENGL_ES
+ if (ctx->d_ptr->internal_context) {
+ glDisable(GL_SCISSOR_TEST);
+ } else {
+ glMatrixMode(GL_PROJECTION);
+ glLoadMatrixd(&d->projection_matrix[0][0]);
+ glPopAttrib();
+ glPopClientAttrib();
+ }
+#endif
+ d->drawable.swapBuffers();
+ d->drawable.doneCurrent();
+ qt_mask_texture_cache()->maintainCache();
+
+ return true;
+}
+
+void QOpenGLPaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QOpenGLPaintEngine);
+ QPaintEngine::DirtyFlags flags = state.state();
+
+ bool update_fast_pen = false;
+
+ if (flags & DirtyOpacity) {
+ update_fast_pen = true;
+ d->opacity = state.opacity();
+ if (d->opacity > 1.0f)
+ d->opacity = 1.0f;
+ if (d->opacity < 0.f)
+ d->opacity = 0.f;
+ // force update
+ flags |= DirtyPen;
+ flags |= DirtyBrush;
+ }
+
+ if (flags & DirtyTransform) {
+ update_fast_pen = true;
+ updateMatrix(state.transform());
+ // brush setup depends on transform state
+ if (state.brush().style() != Qt::NoBrush)
+ flags |= DirtyBrush;
+ }
+
+ if (flags & DirtyPen) {
+ update_fast_pen = true;
+ updatePen(state.pen());
+ }
+
+ if (flags & (DirtyBrush | DirtyBrushOrigin)) {
+ updateBrush(state.brush(), state.brushOrigin());
+ }
+
+ if (flags & DirtyFont) {
+ updateFont(state.font());
+ }
+
+ if (state.state() & DirtyClipEnabled) {
+ if (state.isClipEnabled())
+ updateClipRegion(painter()->clipRegion(), Qt::ReplaceClip);
+ else
+ updateClipRegion(QRegion(), Qt::NoClip);
+ }
+
+ if (flags & DirtyClipPath) {
+ updateClipRegion(QRegion(state.clipPath().toFillPolygon().toPolygon(),
+ state.clipPath().fillRule()),
+ state.clipOperation());
+ }
+
+ if (flags & DirtyClipRegion) {
+ updateClipRegion(state.clipRegion(), state.clipOperation());
+ }
+
+ if (flags & DirtyHints) {
+ updateRenderHints(state.renderHints());
+ }
+
+ if (flags & DirtyCompositionMode) {
+ updateCompositionMode(state.compositionMode());
+ }
+
+ if (update_fast_pen) {
+ Q_D(QOpenGLPaintEngine);
+ qreal pen_width = d->cpen.widthF();
+ d->has_fast_pen =
+ ((pen_width == 0 || (pen_width <= 1 && d->txop <= QTransform::TxTranslate))
+ || d->cpen.isCosmetic())
+ && d->cpen.style() == Qt::SolidLine
+ && d->cpen.isSolid();
+ }
+}
+
+
+void QOpenGLPaintEnginePrivate::setInvMatrixData(const QTransform &inv_matrix)
+{
+ inv_matrix_data[0][0] = inv_matrix.m11();
+ inv_matrix_data[1][0] = inv_matrix.m21();
+ inv_matrix_data[2][0] = inv_matrix.m31();
+
+ inv_matrix_data[0][1] = inv_matrix.m12();
+ inv_matrix_data[1][1] = inv_matrix.m22();
+ inv_matrix_data[2][1] = inv_matrix.m32();
+
+ inv_matrix_data[0][2] = inv_matrix.m13();
+ inv_matrix_data[1][2] = inv_matrix.m23();
+ inv_matrix_data[2][2] = inv_matrix.m33();
+}
+
+
+void QOpenGLPaintEnginePrivate::updateGradient(const QBrush &brush, const QRectF &)
+{
+#ifdef QT_OPENGL_ES
+ Q_UNUSED(brush);
+#else
+ bool has_mirrored_repeat = QGLExtensions::glExtensions & QGLExtensions::MirroredRepeat;
+ Qt::BrushStyle style = brush.style();
+
+ QTransform m = brush.transform();
+
+ if (has_mirrored_repeat && style == Qt::LinearGradientPattern) {
+ const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
+ QTransform m = brush.transform();
+ QPointF realStart = g->start();
+ QPointF realFinal = g->finalStop();
+ QPointF start = m.map(realStart);
+ QPointF stop;
+
+ if (qFuzzyCompare(m.m11(), m.m22()) && m.m12() == 0.0 && m.m21() == 0.0) {
+ // It is a simple uniform scale and/or translation
+ stop = m.map(realFinal);
+ } else {
+ // It is not enough to just transform the endpoints.
+ // We have to make sure the _pattern_ is transformed correctly.
+
+ qreal odx = realFinal.x() - realStart.x();
+ qreal ody = realFinal.y() - realStart.y();
+
+ // nx, ny and dx, dy are normal and gradient direction after transform:
+ qreal nx = m.m11()*ody - m.m21()*odx;
+ qreal ny = m.m12()*ody - m.m22()*odx;
+
+ qreal dx = m.m11()*odx + m.m21()*ody;
+ qreal dy = m.m12()*odx + m.m22()*ody;
+
+ qreal lx = 1 / (dx - dy*nx/ny);
+ qreal ly = 1 / (dy - dx*ny/nx);
+ qreal l = 1 / qSqrt(lx*lx+ly*ly);
+
+ stop = start + QPointF(-ny, nx) * l/qSqrt(nx*nx+ny*ny);
+ }
+
+ float tr[4], f;
+ tr[0] = stop.x() - start.x();
+ tr[1] = stop.y() - start.y();
+ f = 1.0 / (tr[0]*tr[0] + tr[1]*tr[1]);
+ tr[0] *= f;
+ tr[1] *= f;
+ tr[2] = 0;
+ tr[3] = -(start.x()*tr[0] + start.y()*tr[1]);
+ brush_color[0] = brush_color[1] = brush_color[2] = brush_color[3] = 255;
+ qt_glColor4ubv(brush_color);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tr);
+ }
+
+ if (use_fragment_programs) {
+ if (style == Qt::RadialGradientPattern) {
+ const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
+ QPointF realCenter = g->center();
+ QPointF realFocal = g->focalPoint();
+ qreal realRadius = g->radius();
+ QTransform translate(1, 0, 0, 1, -realFocal.x(), -realFocal.y());
+ QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
+ QTransform inv_matrix = gl_to_qt * matrix.inverted() * brush.transform().inverted() * translate;
+
+ setInvMatrixData(inv_matrix);
+
+ fmp_data[0] = realCenter.x() - realFocal.x();
+ fmp_data[1] = realCenter.y() - realFocal.y();
+
+ fmp2_m_radius2_data[0] = -fmp_data[0] * fmp_data[0] - fmp_data[1] * fmp_data[1] + realRadius * realRadius;
+ } else if (style == Qt::ConicalGradientPattern) {
+ const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
+ QPointF realCenter = g->center();
+ QTransform translate(1, 0, 0, 1, -realCenter.x(), -realCenter.y());
+ QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
+ QTransform inv_matrix = gl_to_qt * matrix.inverted() * brush.transform().inverted() * translate;
+
+ setInvMatrixData(inv_matrix);
+
+ angle_data[0] = -(g->angle() * 2 * Q_PI) / 360.0;
+ } else if (style == Qt::LinearGradientPattern) {
+ const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
+
+ QPointF realStart = g->start();
+ QPointF realFinal = g->finalStop();
+ QTransform translate(1, 0, 0, 1, -realStart.x(), -realStart.y());
+ QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
+
+ QTransform inv_matrix = gl_to_qt * matrix.inverted() * brush.transform().inverted() * translate;
+
+ setInvMatrixData(inv_matrix);
+
+ QPointF l = realFinal - realStart;
+
+ linear_data[0] = l.x();
+ linear_data[1] = l.y();
+
+ linear_data[2] = 1.0f / (l.x() * l.x() + l.y() * l.y());
+ } else if (style != Qt::SolidPattern) {
+ QTransform translate(1, 0, 0, 1, brush_origin.x(), brush_origin.y());
+ QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
+
+ QTransform inv_matrix = gl_to_qt * matrix.inverted() * brush.transform().inverted() * translate;
+
+ setInvMatrixData(inv_matrix);
+ }
+ }
+
+ if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
+ createGradientPaletteTexture(*brush.gradient());
+ }
+#endif
+}
+
+
+class QOpenGLTessellator : public QTessellator
+{
+public:
+ QOpenGLTessellator() {}
+ ~QOpenGLTessellator() { }
+ QGLTrapezoid toGLTrapezoid(const Trapezoid &trap);
+};
+
+QGLTrapezoid QOpenGLTessellator::toGLTrapezoid(const Trapezoid &trap)
+{
+ QGLTrapezoid t;
+
+ t.top = Q27Dot5ToDouble(trap.top);
+ t.bottom = Q27Dot5ToDouble(trap.bottom);
+
+ Q27Dot5 y = trap.topLeft->y - trap.bottomLeft->y;
+
+ qreal topLeftY = Q27Dot5ToDouble(trap.topLeft->y);
+
+ qreal tx = Q27Dot5ToDouble(trap.topLeft->x);
+ qreal m = (-tx + Q27Dot5ToDouble(trap.bottomLeft->x)) / Q27Dot5ToDouble(y);
+ t.topLeftX = tx + m * (topLeftY - t.top);
+ t.bottomLeftX = tx + m * (topLeftY - t.bottom);
+
+ y = trap.topRight->y - trap.bottomRight->y;
+
+ qreal topRightY = Q27Dot5ToDouble(trap.topRight->y);
+
+ tx = Q27Dot5ToDouble(trap.topRight->x);
+ m = (-tx + Q27Dot5ToDouble(trap.bottomRight->x)) / Q27Dot5ToDouble(y);
+
+ t.topRightX = tx + m * (topRightY - Q27Dot5ToDouble(trap.top));
+ t.bottomRightX = tx + m * (topRightY - Q27Dot5ToDouble(trap.bottom));
+
+ return t;
+}
+
+class QOpenGLImmediateModeTessellator : public QOpenGLTessellator
+{
+public:
+ void addTrap(const Trapezoid &trap);
+ void tessellate(const QPointF *points, int nPoints, bool winding) {
+ trapezoids.reserve(trapezoids.size() + nPoints);
+ setWinding(winding);
+ QTessellator::tessellate(points, nPoints);
+ }
+
+ QVector<QGLTrapezoid> trapezoids;
+};
+
+void QOpenGLImmediateModeTessellator::addTrap(const Trapezoid &trap)
+{
+ trapezoids.append(toGLTrapezoid(trap));
+}
+
+#ifndef QT_OPENGL_ES
+static void drawTrapezoid(const QGLTrapezoid &trap, const qreal offscreenHeight, QGLContext *ctx)
+{
+ qreal minX = qMin(trap.topLeftX, trap.bottomLeftX);
+ qreal maxX = qMax(trap.topRightX, trap.bottomRightX);
+
+ if (qFuzzyCompare(trap.top, trap.bottom) || qFuzzyCompare(minX, maxX) ||
+ (qFuzzyCompare(trap.topLeftX, trap.topRightX) && qFuzzyCompare(trap.bottomLeftX, trap.bottomRightX)))
+ return;
+
+ const qreal xpadding = 1.0;
+ const qreal ypadding = 1.0;
+
+ qreal topDist = offscreenHeight - trap.top;
+ qreal bottomDist = offscreenHeight - trap.bottom;
+
+ qreal reciprocal = bottomDist / (bottomDist - topDist);
+
+ qreal leftB = trap.bottomLeftX + (trap.topLeftX - trap.bottomLeftX) * reciprocal;
+ qreal rightB = trap.bottomRightX + (trap.topRightX - trap.bottomRightX) * reciprocal;
+
+ const bool topZero = qFuzzyCompare(topDist + 1, 1);
+
+ reciprocal = topZero ? 1.0 / bottomDist : 1.0 / topDist;
+
+ qreal leftA = topZero ? (trap.bottomLeftX - leftB) * reciprocal : (trap.topLeftX - leftB) * reciprocal;
+ qreal rightA = topZero ? (trap.bottomRightX - rightB) * reciprocal : (trap.topRightX - rightB) * reciprocal;
+
+ qreal invLeftA = qFuzzyCompare(leftA + 1, 1) ? 0.0 : 1.0 / leftA;
+ qreal invRightA = qFuzzyCompare(rightA + 1, 1) ? 0.0 : 1.0 / rightA;
+
+ // fragment program needs the negative of invRightA as it mirrors the line
+ glTexCoord4f(topDist, bottomDist, invLeftA, -invRightA);
+ glMultiTexCoord4f(GL_TEXTURE1, leftA, leftB, rightA, rightB);
+
+ qreal topY = trap.top - ypadding;
+ qreal bottomY = trap.bottom + ypadding;
+
+ qreal bounds_bottomLeftX = leftA * (offscreenHeight - bottomY) + leftB;
+ qreal bounds_bottomRightX = rightA * (offscreenHeight - bottomY) + rightB;
+ qreal bounds_topLeftX = leftA * (offscreenHeight - topY) + leftB;
+ qreal bounds_topRightX = rightA * (offscreenHeight - topY) + rightB;
+
+ QPointF leftNormal(1, -leftA);
+ leftNormal /= qSqrt(leftNormal.x() * leftNormal.x() + leftNormal.y() * leftNormal.y());
+ QPointF rightNormal(1, -rightA);
+ rightNormal /= qSqrt(rightNormal.x() * rightNormal.x() + rightNormal.y() * rightNormal.y());
+
+ qreal left_padding = xpadding / qAbs(leftNormal.x());
+ qreal right_padding = xpadding / qAbs(rightNormal.x());
+
+ glVertex2d(bounds_topLeftX - left_padding, topY);
+ glVertex2d(bounds_topRightX + right_padding, topY);
+ glVertex2d(bounds_bottomRightX + right_padding, bottomY);
+ glVertex2d(bounds_bottomLeftX - left_padding, bottomY);
+
+ glTexCoord4f(0.0f, 0.0f, 0.0f, 1.0f);
+}
+#endif // !Q_WS_QWS
+
+class QOpenGLTrapezoidToArrayTessellator : public QOpenGLTessellator
+{
+public:
+ QOpenGLTrapezoidToArrayTessellator() : vertices(0), allocated(0), size(0) {}
+ ~QOpenGLTrapezoidToArrayTessellator() { free(vertices); }
+ q_vertexType *vertices;
+ int allocated;
+ int size;
+ QRectF bounds;
+ void addTrap(const Trapezoid &trap);
+ void tessellate(const QPointF *points, int nPoints, bool winding) {
+ size = 0;
+ setWinding(winding);
+ bounds = QTessellator::tessellate(points, nPoints);
+ }
+};
+
+void QOpenGLTrapezoidToArrayTessellator::addTrap(const Trapezoid &trap)
+{
+ // On OpenGL ES we convert the trap to 2 triangles
+#ifndef QT_OPENGL_ES_1
+ if (size > allocated - 8) {
+#else
+ if (size > allocated - 12) {
+#endif
+ allocated = qMax(2*allocated, 512);
+ vertices = (q_vertexType *)realloc(vertices, allocated * sizeof(q_vertexType));
+ }
+
+ QGLTrapezoid t = toGLTrapezoid(trap);
+
+#ifndef QT_OPENGL_ES_1
+ vertices[size++] = t.topLeftX;
+ vertices[size++] = t.top;
+ vertices[size++] = t.topRightX;
+ vertices[size++] = t.top;
+ vertices[size++] = t.bottomRightX;
+ vertices[size++] = t.bottom;
+ vertices[size++] = t.bottomLeftX;
+ vertices[size++] = t.bottom;
+#else
+ // First triangle
+ vertices[size++] = t.topLeftX;
+ vertices[size++] = t.top;
+ vertices[size++] = t.topRightX;
+ vertices[size++] = t.top;
+ vertices[size++] = t.bottomRightX;
+ vertices[size++] = t.bottom;
+
+ // Second triangle
+ vertices[size++] = t.bottomLeftX;
+ vertices[size++] = t.bottom;
+ vertices[size++] = t.topLeftX;
+ vertices[size++] = t.top;
+ vertices[size++] = t.bottomRightX;
+ vertices[size++] = t.bottom;
+#endif
+}
+
+
+void QOpenGLPaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
+ Qt::FillRule fill)
+{
+ QOpenGLTrapezoidToArrayTessellator tessellator;
+ tessellator.tessellate(polygonPoints, pointCount, fill == Qt::WindingFill);
+
+ DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon with" << pointCount << "points using fillPolygon_dev";
+
+ setGradientOps(cbrush, tessellator.bounds);
+
+ bool fast_style = current_style == Qt::LinearGradientPattern
+ || current_style == Qt::SolidPattern;
+
+#ifndef QT_OPENGL_ES
+ GLenum geometry_mode = GL_QUADS;
+#else
+ GLenum geometry_mode = GL_TRIANGLES;
+#endif
+
+ if (use_fragment_programs && !(fast_style && has_fast_composition_mode)) {
+ composite(geometry_mode, tessellator.vertices, tessellator.size / 2);
+ } else {
+ glVertexPointer(2, q_vertexTypeEnum, 0, tessellator.vertices);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDrawArrays(geometry_mode, 0, tessellator.size/2);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ }
+}
+
+
+inline void QOpenGLPaintEnginePrivate::lineToStencil(qreal x, qreal y)
+{
+ tess_points.add(QPointF(x, y));
+
+ if (x > max_x)
+ max_x = x;
+ else if (x < min_x)
+ min_x = x;
+ if (y > max_y)
+ max_y = y;
+ else if (y < min_y)
+ min_y = y;
+}
+
+inline void QOpenGLPaintEnginePrivate::curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep)
+{
+ qreal inverseScaleHalf = inverseScale / 2;
+
+ QBezier beziers[32];
+ beziers[0] = QBezier::fromPoints(tess_points.last(), cp1, cp2, ep);
+ QBezier *b = beziers;
+ while (b >= beziers) {
+ // check if we can pop the top bezier curve from the stack
+ qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
+ qreal d;
+ if (l > inverseScale) {
+ d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2) - (b->y4 - b->y1)*(b->x1 - b->x2) )
+ + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3) - (b->y4 - b->y1)*(b->x1 - b->x3) );
+ d /= l;
+ } else {
+ d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
+ qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
+ }
+ if (d < inverseScaleHalf || b == beziers + 31) {
+ // good enough, we pop it off and add the endpoint
+ lineToStencil(b->x4, b->y4);
+ --b;
+ } else {
+ // split, second half of the polygon goes lower into the stack
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+}
+
+
+void QOpenGLPaintEnginePrivate::pathToVertexArrays(const QPainterPath &path)
+{
+ const QPainterPath::Element &first = path.elementAt(0);
+ min_x = max_x = first.x;
+ min_y = max_y = first.y;
+
+ tess_points.reset();
+ tess_points_stops.clear();
+ lineToStencil(first.x, first.y);
+
+ for (int i=1; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ tess_points_stops.append(tess_points.size());
+ lineToStencil(e.x, e.y);
+ break;
+ case QPainterPath::LineToElement:
+ lineToStencil(e.x, e.y);
+ break;
+ case QPainterPath::CurveToElement:
+ curveToStencil(e, path.elementAt(i+1), path.elementAt(i+2));
+ i+=2;
+ break;
+ default:
+ break;
+ }
+ }
+ lineToStencil(first.x, first.y);
+ tess_points_stops.append(tess_points.size());
+}
+
+
+void QOpenGLPaintEnginePrivate::drawVertexArrays()
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, GL_DOUBLE, 0, tess_points.data());
+ int previous_stop = 0;
+ foreach(int stop, tess_points_stops) {
+ glDrawArrays(GL_TRIANGLE_FAN, previous_stop, stop-previous_stop);
+ previous_stop = stop;
+ }
+ glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+void QOpenGLPaintEnginePrivate::fillVertexArray(Qt::FillRule fillRule)
+{
+ Q_Q(QOpenGLPaintEngine);
+
+ if (dirty_stencil) {
+ disableClipping();
+
+ if (use_system_clip) {
+ glEnable(GL_SCISSOR_TEST);
+
+ QRect rect = q->systemClip().boundingRect();
+
+ const int left = rect.left();
+ const int width = rect.width();
+ const int bottom = drawable.size().height() - (rect.bottom() + 1);
+ const int height = rect.height();
+
+ glScissor(left, bottom, width, height);
+ }
+
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ dirty_stencil = false;
+
+ if (use_system_clip)
+ glDisable(GL_SCISSOR_TEST);
+
+ enableClipping();
+ }
+
+ glStencilMask(~0);
+
+ // Enable stencil.
+ glEnable(GL_STENCIL_TEST);
+
+ // Disable color writes.
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+
+ GLuint stencilMask = 0;
+
+ if (fillRule == Qt::OddEvenFill) {
+ stencilMask = 1;
+
+ // Enable stencil writes.
+ glStencilMask(stencilMask);
+
+ // Set stencil xor mode.
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
+
+ // Disable stencil func.
+ glStencilFunc(GL_ALWAYS, 0, ~0);
+
+ drawVertexArrays();
+ } else if (fillRule == Qt::WindingFill) {
+ stencilMask = ~0;
+
+ if (has_stencil_face_ext) {
+ QGL_FUNC_CONTEXT;
+ glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
+
+ glActiveStencilFaceEXT(GL_BACK);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
+ glStencilFunc(GL_ALWAYS, 0, ~0);
+
+ glActiveStencilFaceEXT(GL_FRONT);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
+ glStencilFunc(GL_ALWAYS, 0, ~0);
+
+ drawVertexArrays();
+
+ glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
+ } else {
+ glStencilFunc(GL_ALWAYS, 0, ~0);
+ glEnable(GL_CULL_FACE);
+
+ glCullFace(GL_BACK);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
+ drawVertexArrays();
+
+ glCullFace(GL_FRONT);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
+ drawVertexArrays();
+
+ glDisable(GL_CULL_FACE);
+ }
+ }
+
+ // Enable color writes.
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glStencilMask(0);
+
+ setGradientOps(cbrush, QRectF(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y)));
+
+ bool fast_fill = has_fast_composition_mode && (current_style == Qt::LinearGradientPattern || current_style == Qt::SolidPattern);
+
+ if (use_fragment_programs && !fast_fill) {
+ DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (fragment programs)";
+ QRectF rect(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y));
+
+ // Enable stencil func.
+ glStencilFunc(GL_NOTEQUAL, 0, stencilMask);
+ composite(rect);
+ } else {
+ DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (no fragment programs)";
+
+ // Enable stencil func.
+ glStencilFunc(GL_NOTEQUAL, 0, stencilMask);
+#ifndef QT_OPENGL_ES
+ glBegin(GL_QUADS);
+ glVertex2f(min_x, min_y);
+ glVertex2f(max_x, min_y);
+ glVertex2f(max_x, max_y);
+ glVertex2f(min_x, max_y);
+ glEnd();
+#endif
+ }
+
+ glStencilMask(~0);
+ glStencilFunc(GL_ALWAYS, 0, 0);
+ glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
+
+ // clear all stencil values to 0
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+
+#ifndef QT_OPENGL_ES
+ glBegin(GL_QUADS);
+ glVertex2f(min_x, min_y);
+ glVertex2f(max_x, min_y);
+ glVertex2f(max_x, max_y);
+ glVertex2f(min_x, max_y);
+ glEnd();
+#endif
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+ // Disable stencil writes.
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ glStencilMask(0);
+ glDisable(GL_STENCIL_TEST);
+}
+
+void QOpenGLPaintEnginePrivate::fillPath(const QPainterPath &path)
+{
+ if (path.isEmpty())
+ return;
+
+ if (use_stencil_method && !high_quality_antialiasing) {
+ pathToVertexArrays(path);
+ fillVertexArray(path.fillRule());
+ return;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ if (high_quality_antialiasing)
+ drawOffscreenPath(path);
+ else {
+ QPolygonF poly = path.toFillPolygon(matrix);
+ fillPolygon_dev(poly.data(), poly.count(),
+ path.fillRule());
+ }
+
+ updateGLMatrix();
+}
+
+
+static inline bool needsEmulation(Qt::BrushStyle style)
+{
+ return !(style == Qt::SolidPattern
+ || (style == Qt::LinearGradientPattern
+ && (QGLExtensions::glExtensions & QGLExtensions::MirroredRepeat)));
+}
+
+void QOpenGLPaintEnginePrivate::updateUseEmulation()
+{
+ use_emulation = !use_fragment_programs
+ && ((has_pen && needsEmulation(pen_brush_style))
+ || (has_brush && needsEmulation(brush_style)));
+}
+
+void QOpenGLPaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QOpenGLPaintEngine);
+ Qt::PenStyle pen_style = pen.style();
+ d->pen_brush_style = pen.brush().style();
+ d->cpen = pen;
+ d->has_pen = (pen_style != Qt::NoPen);
+ d->updateUseEmulation();
+
+ if (pen.isCosmetic()) {
+ GLfloat width = pen.widthF() == 0.0f ? 1.0f : pen.widthF();
+ glLineWidth(width);
+ glPointSize(width);
+ }
+
+ if (d->pen_brush_style >= Qt::LinearGradientPattern
+ && d->pen_brush_style <= Qt::ConicalGradientPattern)
+ {
+ d->setGLPen(Qt::white);
+ } else {
+ d->setGLPen(pen.color());
+ }
+
+ d->updateFastPen();
+}
+
+void QOpenGLPaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
+{
+ Q_D(QOpenGLPaintEngine);
+ d->cbrush = brush;
+ d->brush_style = brush.style();
+ d->brush_origin = origin;
+ d->has_brush = (d->brush_style != Qt::NoBrush);
+ d->updateUseEmulation();
+}
+
+void QOpenGLPaintEngine::updateFont(const QFont &)
+{
+}
+
+void QOpenGLPaintEngine::updateMatrix(const QTransform &mtx)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ d->matrix = mtx;
+
+ d->mv_matrix[0][0] = mtx.m11();
+ d->mv_matrix[0][1] = mtx.m12();
+ d->mv_matrix[0][2] = 0;
+ d->mv_matrix[0][3] = mtx.m13();
+
+ d->mv_matrix[1][0] = mtx.m21();
+ d->mv_matrix[1][1] = mtx.m22();
+ d->mv_matrix[1][2] = 0;
+ d->mv_matrix[1][3] = mtx.m23();
+
+ d->mv_matrix[2][0] = 0;
+ d->mv_matrix[2][1] = 0;
+ d->mv_matrix[2][2] = 1;
+ d->mv_matrix[2][3] = 0;
+
+ d->mv_matrix[3][0] = mtx.dx();
+ d->mv_matrix[3][1] = mtx.dy();
+ d->mv_matrix[3][2] = 0;
+ d->mv_matrix[3][3] = mtx.m33();
+
+ d->txop = mtx.type();
+
+ // 1/10000 == 0.0001, so we have good enough res to cover curves
+ // that span the entire widget...
+ d->inverseScale = qMax(1 / qMax( qMax(qAbs(mtx.m11()), qAbs(mtx.m22())),
+ qMax(qAbs(mtx.m12()), qAbs(mtx.m21())) ),
+ qreal(0.0001));
+
+ d->updateGLMatrix();
+ d->updateFastPen();
+}
+
+void QOpenGLPaintEnginePrivate::updateGLMatrix() const
+{
+ glMatrixMode(GL_MODELVIEW);
+#ifndef QT_OPENGL_ES
+ glLoadMatrixd(&mv_matrix[0][0]);
+#else
+ glLoadMatrixf(&mv_matrix[0][0]);
+#endif
+}
+
+void QOpenGLPaintEnginePrivate::disableClipping()
+{
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+}
+
+void QOpenGLPaintEnginePrivate::enableClipping()
+{
+ Q_Q(QOpenGLPaintEngine);
+ if (!q->state()->hasClipping)
+ return;
+
+ if (q->state()->fastClip.isEmpty())
+ glEnable(GL_DEPTH_TEST);
+ else
+ updateDepthClip(); // this will enable the scissor test
+}
+
+void QOpenGLPaintEnginePrivate::updateDepthClip()
+{
+ Q_Q(QOpenGLPaintEngine);
+
+ 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 = drawable.size().height() - (fastClip.bottom() + 1);
+ const int height = fastClip.height();
+
+ glScissor(left, bottom, width, height);
+ return;
+ }
+
+#ifndef QT_OPENGL_ES
+ glClearDepth(0.0f);
+#else
+ glClearDepthf(0.0f);
+#endif
+ glEnable(GL_DEPTH_TEST);
+ glDepthMask(GL_TRUE);
+ glClear(GL_DEPTH_BUFFER_BIT);
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ glDepthFunc(GL_ALWAYS);
+
+ const QVector<QRect> rects = q->state()->clipEnabled ? q->state()->clipRegion.rects() : q->systemClip().rects();
+
+ // rectangle count * 2 (triangles) * vertex count * component count (Z omitted)
+ QDataBuffer<q_vertexType> clipVertex(rects.size()*2*3*2);
+ for (int i = 0; i < rects.size(); ++i) {
+ q_vertexType x = i2vt(rects.at(i).left());
+ q_vertexType w = i2vt(rects.at(i).width());
+ q_vertexType h = i2vt(rects.at(i).height());
+ q_vertexType y = i2vt(rects.at(i).top());
+
+ // First triangle
+ clipVertex.add(x);
+ clipVertex.add(y);
+
+ clipVertex.add(x);
+ clipVertex.add(y + h);
+
+ clipVertex.add(x + w);
+ clipVertex.add(y);
+
+ // Second triangle
+ clipVertex.add(x);
+ clipVertex.add(y + h);
+
+ clipVertex.add(x + w);
+ clipVertex.add(y + h);
+
+ clipVertex.add (x + w);
+ clipVertex.add(y);
+ }
+
+ if (rects.size()) {
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, q_vertexTypeEnum, 0, clipVertex.data());
+
+ glDrawArrays(GL_TRIANGLES, 0, rects.size()*2*3);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ updateGLMatrix();
+ }
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glDepthMask(GL_FALSE);
+ glDepthFunc(GL_LEQUAL);
+}
+
+void QOpenGLPaintEnginePrivate::systemStateChanged()
+{
+ Q_Q(QOpenGLPaintEngine);
+ if (q->state()->hasClipping)
+ q->updateClipRegion(q->painter()->clipRegion(), Qt::ReplaceClip);
+ else
+ q->updateClipRegion(QRegion(), Qt::NoClip);
+}
+
+void QOpenGLPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ // clipping is only supported when a stencil or depth buffer is
+ // available
+ if (!d->drawable.format().depth())
+ return;
+
+ d->use_system_clip = false;
+ QRegion sysClip = systemClip();
+ if (!sysClip.isEmpty()) {
+ if (d->pdev->devType() != QInternal::Widget) {
+ d->use_system_clip = true;
+ } else {
+#ifndef Q_WS_QWS
+ // Only use the system clip if we're currently rendering a widget with a GL painter.
+ if (d->currentClipWidget) {
+ QWidgetPrivate *widgetPrivate = qt_widget_private(d->currentClipWidget->window());
+ d->use_system_clip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter;
+ }
+#endif
+ }
+ }
+
+ d->flushDrawQueue();
+
+ if (op == Qt::NoClip && !d->use_system_clip) {
+ state()->hasClipping = false;
+ state()->clipRegion = QRegion();
+ d->updateDepthClip();
+ return;
+ }
+
+ bool isScreenClip = false;
+ if (!d->use_system_clip) {
+ QVector<QRect> untransformedRects = clipRegion.rects();
+
+ if (untransformedRects.size() == 1) {
+ QPainterPath path;
+ path.addRect(untransformedRects[0]);
+ path = d->matrix.map(path);
+
+ if (path.contains(QRectF(QPointF(), d->drawable.size())))
+ isScreenClip = true;
+ }
+ }
+
+ QRegion region = isScreenClip ? QRegion() : clipRegion * d->matrix;
+ switch (op) {
+ case Qt::NoClip:
+ if (!d->use_system_clip)
+ break;
+ state()->clipRegion = sysClip;
+ break;
+ case Qt::IntersectClip:
+ if (isScreenClip)
+ return;
+ if (state()->hasClipping) {
+ state()->clipRegion &= region;
+ break;
+ }
+ // fall through
+ case Qt::ReplaceClip:
+ if (d->use_system_clip)
+ state()->clipRegion = region & sysClip;
+ else
+ state()->clipRegion = region;
+ break;
+ case Qt::UniteClip:
+ state()->clipRegion |= region;
+ if (d->use_system_clip)
+ state()->clipRegion &= sysClip;
+ break;
+ default:
+ break;
+ }
+
+ if (isScreenClip) {
+ state()->hasClipping = false;
+ state()->clipRegion = QRegion();
+ } else {
+ state()->hasClipping = op != Qt::NoClip || d->use_system_clip;
+ }
+
+ if (state()->hasClipping && state()->clipRegion.rects().size() == 1)
+ state()->fastClip = state()->clipRegion.rects().at(0);
+ else
+ state()->fastClip = QRect();
+
+ d->updateDepthClip();
+}
+
+void QOpenGLPaintEngine::updateRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ d->flushDrawQueue();
+ d->use_smooth_pixmap_transform = bool(hints & QPainter::SmoothPixmapTransform);
+ if ((hints & QPainter::Antialiasing) || (hints & QPainter::HighQualityAntialiasing)) {
+ if (d->use_fragment_programs && QGLOffscreen::isSupported()
+ && (hints & QPainter::HighQualityAntialiasing)) {
+ d->high_quality_antialiasing = true;
+ } else {
+ d->high_quality_antialiasing = false;
+ if (QGLExtensions::glExtensions & QGLExtensions::SampleBuffers)
+ glEnable(GL_MULTISAMPLE);
+ }
+ } else {
+ d->high_quality_antialiasing = false;
+ if (QGLExtensions::glExtensions & QGLExtensions::SampleBuffers)
+ glDisable(GL_MULTISAMPLE);
+ }
+
+ if (d->high_quality_antialiasing) {
+ d->offscreen.initialize();
+
+ if (!d->offscreen.isValid()) {
+ DEBUG_ONCE_STR("Unable to initialize offscreen, disabling high quality antialiasing");
+ d->high_quality_antialiasing = false;
+ if (QGLExtensions::glExtensions & QGLExtensions::SampleBuffers)
+ glEnable(GL_MULTISAMPLE);
+ }
+ }
+
+ d->has_antialiasing = d->high_quality_antialiasing
+ || ((hints & QPainter::Antialiasing)
+ && (QGLExtensions::glExtensions & QGLExtensions::SampleBuffers));
+}
+
+
+void QOpenGLPaintEnginePrivate::setPorterDuffData(float a, float b, float x, float y, float z)
+{
+ porterduff_ab_data[0] = a;
+ porterduff_ab_data[1] = b;
+
+ porterduff_xyz_data[0] = x;
+ porterduff_xyz_data[1] = y;
+ porterduff_xyz_data[2] = z;
+}
+
+
+void QOpenGLPaintEngine::updateCompositionMode(QPainter::CompositionMode composition_mode)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (!d->use_fragment_programs && composition_mode > QPainter::CompositionMode_Plus)
+ composition_mode = QPainter::CompositionMode_SourceOver;
+
+ d->composition_mode = composition_mode;
+
+ d->has_fast_composition_mode = (!d->high_quality_antialiasing && composition_mode <= QPainter::CompositionMode_Plus)
+ || composition_mode == QPainter::CompositionMode_SourceOver
+ || composition_mode == QPainter::CompositionMode_Destination
+ || composition_mode == QPainter::CompositionMode_DestinationOver
+ || composition_mode == QPainter::CompositionMode_DestinationOut
+ || composition_mode == QPainter::CompositionMode_SourceAtop
+ || composition_mode == QPainter::CompositionMode_Xor
+ || composition_mode == QPainter::CompositionMode_Plus;
+
+ if (d->has_fast_composition_mode)
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODE_BLEND_MODE_MASK : COMPOSITION_MODE_BLEND_MODE_NOMASK;
+ else if (composition_mode <= QPainter::CompositionMode_Plus)
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SIMPLE_PORTER_DUFF : COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK;
+ else
+ switch (composition_mode) {
+ case QPainter::CompositionMode_Multiply:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_MULTIPLY : COMPOSITION_MODES_MULTIPLY_NOMASK;
+ break;
+ case QPainter::CompositionMode_Screen:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SCREEN : COMPOSITION_MODES_SCREEN_NOMASK;
+ break;
+ case QPainter::CompositionMode_Overlay:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_OVERLAY : COMPOSITION_MODES_OVERLAY_NOMASK;
+ break;
+ case QPainter::CompositionMode_Darken:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_DARKEN : COMPOSITION_MODES_DARKEN_NOMASK;
+ break;
+ case QPainter::CompositionMode_Lighten:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_LIGHTEN : COMPOSITION_MODES_LIGHTEN_NOMASK;
+ break;
+ case QPainter::CompositionMode_ColorDodge:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_COLORDODGE : COMPOSITION_MODES_COLORDODGE_NOMASK;
+ break;
+ case QPainter::CompositionMode_ColorBurn:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_COLORBURN : COMPOSITION_MODES_COLORBURN_NOMASK;
+ break;
+ case QPainter::CompositionMode_HardLight:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_HARDLIGHT : COMPOSITION_MODES_HARDLIGHT_NOMASK;
+ break;
+ case QPainter::CompositionMode_SoftLight:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SOFTLIGHT : COMPOSITION_MODES_SOFTLIGHT_NOMASK;
+ break;
+ case QPainter::CompositionMode_Difference:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_DIFFERENCE : COMPOSITION_MODES_DIFFERENCE_NOMASK;
+ break;
+ case QPainter::CompositionMode_Exclusion:
+ d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_EXCLUSION : COMPOSITION_MODES_EXCLUSION_NOMASK;
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+
+ switch(composition_mode) {
+ case QPainter::CompositionMode_DestinationOver:
+ glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
+ d->setPorterDuffData(0, 1, 1, 1, 1);
+ break;
+ case QPainter::CompositionMode_Clear:
+ glBlendFunc(GL_ZERO, GL_ZERO);
+ d->setPorterDuffData(0, 0, 0, 0, 0);
+ break;
+ case QPainter::CompositionMode_Source:
+ glBlendFunc(GL_ONE, GL_ZERO);
+ d->setPorterDuffData(1, 0, 1, 1, 0);
+ break;
+ case QPainter::CompositionMode_Destination:
+ glBlendFunc(GL_ZERO, GL_ONE);
+ d->setPorterDuffData(0, 1, 1, 0, 1);
+ break;
+ case QPainter::CompositionMode_SourceIn:
+ glBlendFunc(GL_DST_ALPHA, GL_ZERO);
+ d->setPorterDuffData(1, 0, 1, 0, 0);
+ break;
+ case QPainter::CompositionMode_DestinationIn:
+ glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
+ d->setPorterDuffData(0, 1, 1, 0, 0);
+ break;
+ case QPainter::CompositionMode_SourceOut:
+ glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
+ d->setPorterDuffData(0, 0, 0, 1, 0);
+ break;
+ case QPainter::CompositionMode_DestinationOut:
+ glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
+ d->setPorterDuffData(0, 0, 0, 0, 1);
+ break;
+ case QPainter::CompositionMode_SourceAtop:
+ glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ d->setPorterDuffData(1, 0, 1, 0, 1);
+ break;
+ case QPainter::CompositionMode_DestinationAtop:
+ glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
+ d->setPorterDuffData(0, 1, 1, 1, 0);
+ break;
+ case QPainter::CompositionMode_Xor:
+ glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ d->setPorterDuffData(0, 0, 0, 1, 1);
+ break;
+ case QPainter::CompositionMode_SourceOver:
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ d->setPorterDuffData(1, 0, 1, 1, 1);
+ break;
+ case QPainter::CompositionMode_Plus:
+ glBlendFunc(GL_ONE, GL_ONE);
+ d->setPorterDuffData(1, 1, 1, 1, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+class QGLMaskGenerator
+{
+public:
+ QGLMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal stroke_width = -1)
+ : p(path),
+ m(matrix),
+ w(stroke_width)
+ {
+ }
+
+ virtual QRect screenRect() = 0;
+ virtual void drawMask(const QRect &rect) = 0;
+
+ QPainterPath path() const { return p; }
+ QTransform matrix() const { return m; }
+ qreal strokeWidth() const { return w; }
+
+ virtual ~QGLMaskGenerator() {}
+
+private:
+ QPainterPath p;
+ QTransform m;
+ qreal w;
+};
+
+void QGLMaskTextureCache::setOffscreenSize(const QSize &sz)
+{
+ Q_ASSERT(sz.width() == sz.height());
+
+ if (offscreenSize != sz) {
+ offscreenSize = sz;
+ clearCache();
+ }
+}
+
+void QGLMaskTextureCache::clearCache()
+{
+ cache.clear();
+
+ int quad_tree_size = 1;
+
+ for (int i = block_size; i < offscreenSize.width(); i *= 2)
+ quad_tree_size += quad_tree_size * 4;
+
+ for (int i = 0; i < 4; ++i) {
+ occupied_quadtree[i].resize(quad_tree_size);
+
+ occupied_quadtree[i][0].key = 0;
+ occupied_quadtree[i][0].largest_available_block = offscreenSize.width();
+ occupied_quadtree[i][0].largest_used_block = 0;
+
+ DEBUG_ONCE qDebug() << "QGLMaskTextureCache:: created quad tree of size" << quad_tree_size;
+ }
+}
+
+void QGLMaskTextureCache::setDrawableSize(const QSize &sz)
+{
+ drawableSize = sz;
+}
+
+void QGLMaskTextureCache::maintainCache()
+{
+ QGLTextureCacheHash::iterator it = cache.begin();
+ QGLTextureCacheHash::iterator end = cache.end();
+
+ while (it != end) {
+ CacheInfo &cache_info = it.value();
+ ++cache_info.age;
+
+ if (cache_info.age > 1) {
+ quadtreeInsert(cache_info.loc.channel, 0, cache_info.loc.rect);
+ it = cache.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+//#define DISABLE_MASK_CACHE
+
+QGLMaskTextureCache::CacheLocation QGLMaskTextureCache::getMask(QGLMaskGenerator &maskGenerator, QOpenGLPaintEnginePrivate *e)
+{
+#ifndef DISABLE_MASK_CACHE
+ engine = e;
+
+ quint64 key = hash(maskGenerator.path(), maskGenerator.matrix(), maskGenerator.strokeWidth());
+
+ if (key == 0)
+ key = 1;
+
+ CacheInfo info(maskGenerator.path(), maskGenerator.matrix(), maskGenerator.strokeWidth());
+
+ QGLTextureCacheHash::iterator it = cache.find(key);
+
+ while (it != cache.end() && it.key() == key) {
+ CacheInfo &cache_info = it.value();
+ if (info.stroke_width == cache_info.stroke_width && info.matrix == cache_info.matrix && info.path == cache_info.path) {
+ DEBUG_ONCE_STR("QGLMaskTextureCache::getMask(): Using cached mask");
+
+ cache_info.age = 0;
+ return cache_info.loc;
+ }
+ ++it;
+ }
+
+ // mask was not found, create new mask
+
+ DEBUG_ONCE_STR("QGLMaskTextureCache::getMask(): Creating new mask...");
+
+ createMask(key, info, maskGenerator);
+
+ cache.insert(key, info);
+
+ return info.loc;
+#else
+ CacheInfo info(maskGenerator.path(), maskGenerator.matrix());
+ createMask(0, info, maskGenerator);
+ return info.loc;
+#endif
+}
+
+#ifndef FloatToQuint64
+#define FloatToQuint64(i) (quint64)((i) * 32)
+#endif
+
+quint64 QGLMaskTextureCache::hash(const QPainterPath &p, const QTransform &m, qreal w)
+{
+ Q_ASSERT(sizeof(quint64) == 8);
+
+ quint64 h = 0;
+
+ for (int i = 0; i < p.elementCount(); ++i) {
+ h += FloatToQuint64(p.elementAt(i).x) << 32;
+ h += FloatToQuint64(p.elementAt(i).y);
+ h += p.elementAt(i).type;
+ }
+
+ h += FloatToQuint64(m.m11());
+#ifndef Q_OS_WINCE // ###
+ //Compiler crashes for arm on WinCE
+ h += FloatToQuint64(m.m12()) << 4;
+ h += FloatToQuint64(m.m13()) << 8;
+ h += FloatToQuint64(m.m21()) << 12;
+ h += FloatToQuint64(m.m22()) << 16;
+ h += FloatToQuint64(m.m23()) << 20;
+ h += FloatToQuint64(m.m31()) << 24;
+ h += FloatToQuint64(m.m32()) << 28;
+#endif
+ h += FloatToQuint64(m.m33()) << 32;
+
+ h += FloatToQuint64(w);
+
+ return h;
+}
+
+void QGLMaskTextureCache::createMask(quint64 key, CacheInfo &info, QGLMaskGenerator &maskGenerator)
+{
+ info.loc.screen_rect = maskGenerator.screenRect();
+
+ if (info.loc.screen_rect.isEmpty()) {
+ info.loc.channel = 0;
+ info.loc.rect = QRect();
+ return;
+ }
+
+ quadtreeAllocate(key, info.loc.screen_rect.size(), &info.loc.rect, &info.loc.channel);
+
+ int ch = info.loc.channel;
+ glColorMask(ch == 0, ch == 1, ch == 2, ch == 3);
+
+ maskGenerator.drawMask(info.loc.rect);
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+}
+
+int QGLMaskTextureCache::quadtreeBlocksize(int node)
+{
+ DEBUG_ONCE qDebug() << "Offscreen size:" << offscreenSize.width();
+
+ int blocksize = offscreenSize.width();
+
+ while (node) {
+ node = (node - 1) / 4;
+ blocksize /= 2;
+ }
+
+ return blocksize;
+}
+
+QPoint QGLMaskTextureCache::quadtreeLocation(int node)
+{
+ QPoint location;
+ int blocksize = quadtreeBlocksize(node);
+
+ while (node) {
+ --node;
+
+ if (node & 1)
+ location.setX(location.x() + blocksize);
+
+ if (node & 2)
+ location.setY(location.y() + blocksize);
+
+ node /= 4;
+ blocksize *= 2;
+ }
+
+ return location;
+}
+
+void QGLMaskTextureCache::quadtreeUpdate(int channel, int node, int current_block_size)
+{
+ while (node) {
+ node = (node - 1) / 4;
+
+ int first_child = node * 4 + 1;
+
+ int largest_available = 0;
+ int largest_used = 0;
+
+ bool all_empty = true;
+
+ for (int i = 0; i < 4; ++i) {
+ largest_available = qMax(largest_available, occupied_quadtree[channel][first_child + i].largest_available_block);
+ largest_used = qMax(largest_used, occupied_quadtree[channel][first_child + i].largest_used_block);
+
+ if (occupied_quadtree[channel][first_child + i].largest_available_block < current_block_size)
+ all_empty = false;
+ }
+
+ current_block_size *= 2;
+
+ if (all_empty) {
+ occupied_quadtree[channel][node].largest_available_block = current_block_size;
+ occupied_quadtree[channel][node].largest_used_block = 0;
+ } else {
+ occupied_quadtree[channel][node].largest_available_block = largest_available;
+ occupied_quadtree[channel][node].largest_used_block = largest_used;
+ }
+ }
+}
+
+void QGLMaskTextureCache::quadtreeInsert(int channel, quint64 key, const QRect &rect, int node)
+{
+ int current_block_size = quadtreeBlocksize(node);
+ QPoint location = quadtreeLocation(node);
+ QRect relative = rect.translated(-location);
+
+ if (relative.left() >= current_block_size || relative.top() >= current_block_size
+ || relative.right() < 0 || relative.bottom() < 0)
+ return;
+
+ if (current_block_size == block_size // no more refining possible
+ || (relative.top() < block_size && relative.bottom() >= (current_block_size - block_size)
+ && relative.left() < block_size && relative.right() >= (current_block_size - block_size)))
+ {
+ if (key != 0) {
+ occupied_quadtree[channel][node].largest_available_block = 0;
+ occupied_quadtree[channel][node].largest_used_block = rect.width() * rect.height();
+ } else {
+ occupied_quadtree[channel][node].largest_available_block = current_block_size;
+ occupied_quadtree[channel][node].largest_used_block = 0;
+ }
+
+ occupied_quadtree[channel][node].key = key;
+
+ quadtreeUpdate(channel, node, current_block_size);
+ } else {
+ if (key && occupied_quadtree[channel][node].largest_available_block == current_block_size) {
+ // refining the quad tree, initialize child nodes
+ int half_block_size = current_block_size / 2;
+
+ int temp = node * 4 + 1;
+ for (int sibling = 0; sibling < 4; ++sibling) {
+ occupied_quadtree[channel][temp + sibling].largest_available_block = half_block_size;
+ occupied_quadtree[channel][temp + sibling].largest_used_block = 0;
+ occupied_quadtree[channel][temp + sibling].key = 0;
+ }
+ }
+
+ node = node * 4 + 1;
+
+ for (int sibling = 0; sibling < 4; ++sibling)
+ quadtreeInsert(channel, key, rect, node + sibling);
+ }
+}
+
+void QGLMaskTextureCache::quadtreeClear(int channel, const QRect &rect, int node)
+{
+ const quint64 &key = occupied_quadtree[channel][node].key;
+
+ int current_block_size = quadtreeBlocksize(node);
+ QPoint location = quadtreeLocation(node);
+
+ QRect relative = rect.translated(-location);
+
+ if (relative.left() >= current_block_size || relative.top() >= current_block_size
+ || relative.right() < 0 || relative.bottom() < 0)
+ return;
+
+ if (key != 0) {
+ QGLTextureCacheHash::iterator it = cache.find(key);
+
+ Q_ASSERT(it != cache.end());
+
+ while (it != cache.end() && it.key() == key) {
+ const CacheInfo &cache_info = it.value();
+
+ if (cache_info.loc.channel == channel
+ && cache_info.loc.rect.left() <= location.x()
+ && cache_info.loc.rect.top() <= location.y()
+ && cache_info.loc.rect.right() >= location.x()
+ && cache_info.loc.rect.bottom() >= location.y())
+ {
+ quadtreeInsert(channel, 0, cache_info.loc.rect);
+ engine->cacheItemErased(channel, cache_info.loc.rect);
+ cache.erase(it);
+ goto found;
+ } else {
+ ++it;
+ }
+ }
+
+ // if we don't find the key there's an error in the quadtree
+ Q_ASSERT(false);
+found:
+ Q_ASSERT(occupied_quadtree[channel][node].key == 0);
+ } else if (occupied_quadtree[channel][node].largest_available_block < current_block_size) {
+ Q_ASSERT(current_block_size >= block_size);
+
+ node = node * 4 + 1;
+
+ for (int sibling = 0; sibling < 4; ++sibling)
+ quadtreeClear(channel, rect, node + sibling);
+ }
+}
+
+bool QGLMaskTextureCache::quadtreeFindAvailableLocation(const QSize &size, QRect *rect, int *channel)
+{
+ int needed_block_size = qMax(1, qMax(size.width(), size.height()));
+
+ for (int i = 0; i < 4; ++i) {
+ int current_block_size = offscreenSize.width();
+
+ if (occupied_quadtree[i][0].largest_available_block >= needed_block_size) {
+ int node = 0;
+
+ while (current_block_size != occupied_quadtree[i][node].largest_available_block) {
+ Q_ASSERT(current_block_size > block_size);
+ Q_ASSERT(current_block_size > occupied_quadtree[i][node].largest_available_block);
+
+ node = node * 4 + 1;
+ current_block_size /= 2;
+
+ int sibling = 0;
+
+ while (occupied_quadtree[i][node + sibling].largest_available_block < needed_block_size)
+ ++sibling;
+
+ Q_ASSERT(sibling < 4);
+ node += sibling;
+ }
+
+ *channel = i;
+ *rect = QRect(quadtreeLocation(node), size);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QGLMaskTextureCache::quadtreeFindExistingLocation(const QSize &size, QRect *rect, int *channel)
+{
+ // try to pick small masks to throw out, as large masks are more expensive to recompute
+ *channel = qrand() % 4;
+ for (int i = 0; i < 4; ++i)
+ if (occupied_quadtree[i][0].largest_used_block < occupied_quadtree[*channel][0].largest_used_block)
+ *channel = i;
+
+ int needed_block_size = qt_next_power_of_two(qMax(1, qMax(size.width(), size.height())));
+
+ int node = 0;
+ int current_block_size = offscreenSize.width();
+
+ while (current_block_size > block_size
+ && current_block_size >= needed_block_size * 2
+ && occupied_quadtree[*channel][node].key == 0)
+ {
+ node = node * 4 + 1;
+
+ int sibling = 0;
+
+ for (int i = 1; i < 4; ++i) {
+ if (occupied_quadtree[*channel][node + i].largest_used_block
+ <= occupied_quadtree[*channel][node + sibling].largest_used_block)
+ {
+ sibling = i;
+ }
+ }
+
+ node += sibling;
+ current_block_size /= 2;
+ }
+
+ *rect = QRect(quadtreeLocation(node), size);
+}
+
+void QGLMaskTextureCache::quadtreeAllocate(quint64 key, const QSize &size, QRect *rect, int *channel)
+{
+#ifndef DISABLE_MASK_CACHE
+ if (!quadtreeFindAvailableLocation(size, rect, channel)) {
+ quadtreeFindExistingLocation(size, rect, channel);
+ quadtreeClear(*channel, *rect);
+ }
+
+ quadtreeInsert(*channel, key, *rect);
+#else
+ *channel = 0;
+ *rect = QRect(QPoint(), size);
+#endif
+}
+
+class QGLTrapezoidMaskGenerator : public QGLMaskGenerator
+{
+public:
+ QGLTrapezoidMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram, qreal strokeWidth = -1.0);
+
+ QRect screenRect();
+ void drawMask(const QRect &rect);
+
+private:
+ QRect screen_rect;
+ bool has_screen_rect;
+
+ QGLOffscreen *offscreen;
+
+ GLuint maskFragmentProgram;
+
+ virtual QVector<QGLTrapezoid> generateTrapezoids() = 0;
+ virtual QRect computeScreenRect() = 0;
+};
+
+class QGLPathMaskGenerator : public QGLTrapezoidMaskGenerator
+{
+public:
+ QGLPathMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
+
+private:
+ QVector<QGLTrapezoid> generateTrapezoids();
+ QRect computeScreenRect();
+
+ QPolygonF poly;
+};
+
+class QGLLineMaskGenerator : public QGLTrapezoidMaskGenerator
+{
+public:
+ QGLLineMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal width, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
+
+private:
+ QVector<QGLTrapezoid> generateTrapezoids();
+ QRect computeScreenRect();
+
+ QPainterPath transformedPath;
+};
+
+class QGLRectMaskGenerator : public QGLTrapezoidMaskGenerator
+{
+public:
+ QGLRectMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
+
+private:
+ QVector<QGLTrapezoid> generateTrapezoids();
+ QRect computeScreenRect();
+
+ QPainterPath transformedPath;
+};
+
+class QGLEllipseMaskGenerator : public QGLMaskGenerator
+{
+public:
+ QGLEllipseMaskGenerator(const QRectF &rect, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram, int *maskVariableLocations);
+
+ QRect screenRect();
+ void drawMask(const QRect &rect);
+
+private:
+ QRect screen_rect;
+
+ QRectF ellipseRect;
+
+ QGLOffscreen *offscreen;
+
+ GLuint maskFragmentProgram;
+
+ int *maskVariableLocations;
+
+ float vertexArray[4 * 2];
+};
+
+QGLTrapezoidMaskGenerator::QGLTrapezoidMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program, qreal stroke_width)
+ : QGLMaskGenerator(path, matrix, stroke_width)
+ , has_screen_rect(false)
+ , offscreen(&offs)
+ , maskFragmentProgram(program)
+{
+}
+
+void QGLTrapezoidMaskGenerator::drawMask(const QRect &rect)
+{
+#ifdef QT_OPENGL_ES
+ Q_UNUSED(rect);
+#else
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ QGLContext *ctx = offscreen->context();
+ offscreen->bind();
+
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_1D);
+
+ GLfloat vertexArray[4 * 2];
+ qt_add_rect_to_array(rect, vertexArray);
+
+ bool needs_scissor = rect != screen_rect;
+
+ if (needs_scissor) {
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(rect.left(), offscreen->offscreenSize().height() - rect.bottom() - 1, rect.width(), rect.height());
+ }
+
+ QVector<QGLTrapezoid> trapezoids = generateTrapezoids();
+
+ // clear mask
+ glBlendFunc(GL_ZERO, GL_ZERO); // clear
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glBlendFunc(GL_ONE, GL_ONE); // add mask
+ glEnable(GL_FRAGMENT_PROGRAM_ARB);
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, maskFragmentProgram);
+
+ QPoint delta = rect.topLeft() - screen_rect.topLeft();
+ glBegin(GL_QUADS);
+ for (int i = 0; i < trapezoids.size(); ++i)
+ drawTrapezoid(trapezoids[i].translated(delta), offscreen->offscreenSize().height(), ctx);
+ glEnd();
+
+ if (needs_scissor)
+ glDisable(GL_SCISSOR_TEST);
+
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+#endif
+}
+
+QRect QGLTrapezoidMaskGenerator::screenRect()
+{
+ if (!has_screen_rect) {
+ screen_rect = computeScreenRect();
+ has_screen_rect = true;
+ }
+
+ screen_rect = screen_rect.intersected(QRect(QPoint(), offscreen->drawableSize()));
+
+ return screen_rect;
+}
+
+QGLPathMaskGenerator::QGLPathMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program)
+ : QGLTrapezoidMaskGenerator(path, matrix, offs, program)
+{
+}
+
+QRect QGLPathMaskGenerator::computeScreenRect()
+{
+ poly = path().toFillPolygon(matrix());
+ return poly.boundingRect().toAlignedRect();
+}
+
+QVector<QGLTrapezoid> QGLPathMaskGenerator::generateTrapezoids()
+{
+ QOpenGLImmediateModeTessellator tessellator;
+ tessellator.tessellate(poly.data(), poly.count(), path().fillRule() == Qt::WindingFill);
+ return tessellator.trapezoids;
+}
+
+QGLRectMaskGenerator::QGLRectMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program)
+ : QGLTrapezoidMaskGenerator(path, matrix, offs, program)
+{
+}
+
+QGLLineMaskGenerator::QGLLineMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal width, QGLOffscreen &offs, GLuint program)
+ : QGLTrapezoidMaskGenerator(path, matrix, offs, program, width)
+{
+}
+
+QRect QGLRectMaskGenerator::computeScreenRect()
+{
+ transformedPath = matrix().map(path());
+
+ return transformedPath.controlPointRect().adjusted(-1, -1, 1, 1).toAlignedRect();
+}
+
+QRect QGLLineMaskGenerator::computeScreenRect()
+{
+ transformedPath = matrix().map(path());
+
+ return transformedPath.controlPointRect().adjusted(-1, -1, 1, 1).toAlignedRect();
+}
+
+QVector<QGLTrapezoid> QGLLineMaskGenerator::generateTrapezoids()
+{
+ QOpenGLImmediateModeTessellator tessellator;
+ QPointF last;
+ for (int i = 0; i < transformedPath.elementCount(); ++i) {
+ QPainterPath::Element element = transformedPath.elementAt(i);
+
+ Q_ASSERT(!element.isCurveTo());
+
+ if (element.isLineTo())
+ tessellator.tessellateRect(last, element, strokeWidth());
+
+ last = element;
+ }
+
+ return tessellator.trapezoids;
+}
+
+QVector<QGLTrapezoid> QGLRectMaskGenerator::generateTrapezoids()
+{
+ Q_ASSERT(transformedPath.elementCount() == 5);
+
+ QOpenGLImmediateModeTessellator tessellator;
+ if (matrix().type() <= QTransform::TxScale) {
+ QPointF a = transformedPath.elementAt(0);
+ QPointF b = transformedPath.elementAt(1);
+ QPointF c = transformedPath.elementAt(2);
+ QPointF d = transformedPath.elementAt(3);
+
+ QPointF first = (a + d) * 0.5;
+ QPointF last = (b + c) * 0.5;
+
+ QPointF delta = a - d;
+
+ // manhattan distance (no rotation)
+ qreal width = qAbs(delta.x()) + qAbs(delta.y());
+
+ Q_ASSERT(qFuzzyCompare(delta.x() + 1, static_cast<qreal>(1))
+ || qFuzzyCompare(delta.y() + 1, static_cast<qreal>(1)));
+
+ tessellator.tessellateRect(first, last, width);
+ } else {
+ QPointF points[5];
+
+ for (int i = 0; i < 5; ++i)
+ points[i] = transformedPath.elementAt(i);
+
+ tessellator.tessellateConvex(points, 5);
+ }
+ return tessellator.trapezoids;
+}
+
+static QPainterPath ellipseRectToPath(const QRectF &rect)
+{
+ QPainterPath path;
+ path.addEllipse(rect);
+ return path;
+}
+
+QGLEllipseMaskGenerator::QGLEllipseMaskGenerator(const QRectF &rect, const QTransform &matrix, QGLOffscreen &offs, GLuint program, int *locations)
+ : QGLMaskGenerator(ellipseRectToPath(rect), matrix),
+ ellipseRect(rect),
+ offscreen(&offs),
+ maskFragmentProgram(program),
+ maskVariableLocations(locations)
+{
+}
+
+QRect QGLEllipseMaskGenerator::screenRect()
+{
+ QPointF center = ellipseRect.center();
+
+ QPointF points[] = {
+ QPointF(ellipseRect.left(), center.y()),
+ QPointF(ellipseRect.right(), center.y()),
+ QPointF(center.x(), ellipseRect.top()),
+ QPointF(center.x(), ellipseRect.bottom())
+ };
+
+ qreal min_screen_delta_len = QREAL_MAX;
+
+ for (int i = 0; i < 4; ++i) {
+ QPointF delta = points[i] - center;
+
+ // normalize
+ delta /= qSqrt(delta.x() * delta.x() + delta.y() * delta.y());
+
+ QPointF screen_delta(matrix().m11() * delta.x() + matrix().m21() * delta.y(),
+ matrix().m12() * delta.x() + matrix().m22() * delta.y());
+
+ min_screen_delta_len = qMin(min_screen_delta_len,
+ qreal(qSqrt(screen_delta.x() * screen_delta.x() + screen_delta.y() * screen_delta.y())));
+ }
+
+ const qreal padding = 2.0f;
+
+ qreal grow = padding / min_screen_delta_len;
+
+ QRectF boundingRect = ellipseRect.adjusted(-grow, -grow, grow, grow);
+
+ boundingRect = matrix().mapRect(boundingRect);
+
+ QPointF p(0.5, 0.5);
+
+ screen_rect = QRect((boundingRect.topLeft() - p).toPoint(),
+ (boundingRect.bottomRight() + p).toPoint());
+
+ return screen_rect;
+}
+
+void QGLEllipseMaskGenerator::drawMask(const QRect &rect)
+{
+#ifdef QT_OPENGL_ES
+ Q_UNUSED(rect);
+#else
+ QGLContext *ctx = offscreen->context();
+ offscreen->bind();
+
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_1D);
+
+ // fragment program needs the inverse radii of the ellipse
+ glTexCoord2f(1.0f / (ellipseRect.width() * 0.5f),
+ 1.0f / (ellipseRect.height() * 0.5f));
+
+ QTransform translate(1, 0, 0, 1, -ellipseRect.center().x(), -ellipseRect.center().y());
+ QTransform gl_to_qt(1, 0, 0, -1, 0, offscreen->drawableSize().height());
+ QTransform inv_matrix = gl_to_qt * matrix().inverted() * translate;
+
+ float m[3][4] = { { inv_matrix.m11(), inv_matrix.m12(), inv_matrix.m13() },
+ { inv_matrix.m21(), inv_matrix.m22(), inv_matrix.m23() },
+ { inv_matrix.m31(), inv_matrix.m32(), inv_matrix.m33() } };
+
+ QPoint offs(screen_rect.left() - rect.left(), (offscreen->drawableSize().height() - screen_rect.top())
+ - (offscreen->offscreenSize().height() - rect.top()));
+
+ // last component needs to be 1.0f to avoid Nvidia bug on linux
+ float ellipse_offset[4] = { offs.x(), offs.y(), 0.0f, 1.0f };
+
+ GLfloat vertexArray[4 * 2];
+ qt_add_rect_to_array(rect, vertexArray);
+
+ glBlendFunc(GL_ONE, GL_ZERO); // set mask
+ glEnable(GL_FRAGMENT_PROGRAM_ARB);
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, maskFragmentProgram);
+
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M0], m[0]);
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M1], m[1]);
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M2], m[2]);
+
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_ELLIPSE_OFFSET], ellipse_offset);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+#endif
+}
+
+void QOpenGLPaintEnginePrivate::drawOffscreenPath(const QPainterPath &path)
+{
+#ifdef Q_WS_QWS
+ Q_UNUSED(path);
+#else
+ DEBUG_ONCE_STR("QOpenGLPaintEnginePrivate::drawOffscreenPath()");
+
+ disableClipping();
+
+ GLuint program = qt_gl_program_cache()->getProgram(drawable.context(),
+ FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
+ QGLPathMaskGenerator maskGenerator(path, matrix, offscreen, program);
+ addItem(qt_mask_texture_cache()->getMask(maskGenerator, this));
+
+ enableClipping();
+#endif
+}
+
+void QOpenGLPaintEnginePrivate::drawFastRect(const QRectF &r)
+{
+ Q_Q(QOpenGLPaintEngine);
+ DEBUG_ONCE_STR("QOpenGLPaintEngine::drawRects(): drawing fast rect");
+
+ q_vertexType vertexArray[10];
+ qt_add_rect_to_array(r, vertexArray);
+
+ if (has_pen)
+ QOpenGLCoordinateOffset::enableOffset(this);
+
+ if (has_brush) {
+ flushDrawQueue();
+
+ bool temp = high_quality_antialiasing;
+ high_quality_antialiasing = false;
+
+ q->updateCompositionMode(composition_mode);
+
+ setGradientOps(cbrush, r);
+
+ bool fast_style = current_style == Qt::LinearGradientPattern
+ || current_style == Qt::SolidPattern;
+
+ if (fast_style && has_fast_composition_mode) {
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ } else {
+ composite(r);
+ }
+
+ high_quality_antialiasing = temp;
+
+ q->updateCompositionMode(composition_mode);
+ }
+
+ if (has_pen) {
+ if (has_fast_pen && !high_quality_antialiasing) {
+ setGradientOps(cpen.brush(), r);
+
+ vertexArray[8] = vertexArray[0];
+ vertexArray[9] = vertexArray[1];
+
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDrawArrays(GL_LINE_STRIP, 0, 5);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ } else {
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+
+ qreal left = r.left();
+ qreal right = r.right();
+ qreal top = r.top();
+ qreal bottom = r.bottom();
+
+ path.moveTo(left, top);
+ path.lineTo(right, top);
+ path.lineTo(right, bottom);
+ path.lineTo(left, bottom);
+ path.lineTo(left, top);
+
+ strokePath(path, false);
+ }
+
+ QOpenGLCoordinateOffset::disableOffset(this);
+ }
+}
+
+bool QOpenGLPaintEnginePrivate::isFastRect(const QRectF &rect)
+{
+ if (matrix.type() < QTransform::TxRotate) {
+ QRectF r = matrix.mapRect(rect);
+ return r.topLeft().toPoint() == r.topLeft()
+ && r.bottomRight().toPoint() == r.bottomRight();
+ }
+
+ return false;
+}
+
+void QOpenGLPaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+ struct RectF {
+ qreal x;
+ qreal y;
+ qreal w;
+ qreal h;
+ };
+ Q_ASSERT(sizeof(RectF) == sizeof(QRectF));
+ RectF fr[256];
+ while (rectCount) {
+ int i = 0;
+ while (i < rectCount && i < 256) {
+ fr[i].x = rects[i].x();
+ fr[i].y = rects[i].y();
+ fr[i].w = rects[i].width();
+ fr[i].h = rects[i].height();
+ ++i;
+ }
+ drawRects((QRectF *)(void *)fr, i);
+ rects += i;
+ rectCount -= i;
+ }
+}
+
+void QOpenGLPaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (d->use_emulation) {
+ QPaintEngineEx::drawRects(rects, rectCount);
+ return;
+ }
+
+ for (int i=0; i<rectCount; ++i) {
+ const QRectF &r = rects[i];
+
+ // optimization for rects which can be drawn aliased
+ if (!d->high_quality_antialiasing || d->isFastRect(r)) {
+ d->drawFastRect(r);
+ } else {
+ QPainterPath path;
+ path.addRect(r);
+
+ if (d->has_brush) {
+ d->disableClipping();
+ GLuint program = qt_gl_program_cache()->getProgram(d->drawable.context(),
+ FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
+ QGLRectMaskGenerator maskGenerator(path, d->matrix, d->offscreen, program);
+ d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
+
+ d->enableClipping();
+ }
+
+ if (d->has_pen) {
+ if (d->has_fast_pen)
+ d->strokeLines(path);
+ else
+ d->strokePath(path, false);
+ }
+ }
+ }
+}
+
+static void addQuadAsTriangle(q_vertexType *quad, q_vertexType *triangle)
+{
+ triangle[0] = quad[0];
+ triangle[1] = quad[1];
+
+ triangle[2] = quad[2];
+ triangle[3] = quad[3];
+
+ triangle[4] = quad[4];
+ triangle[5] = quad[5];
+
+ triangle[6] = quad[4];
+ triangle[7] = quad[5];
+
+ triangle[8] = quad[6];
+ triangle[9] = quad[7];
+
+ triangle[10] = quad[0];
+ triangle[11] = quad[1];
+}
+
+void QOpenGLPaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+ Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
+ QT_PointF fp[256];
+ while (pointCount) {
+ int i = 0;
+ while (i < pointCount && i < 256) {
+ fp[i].x = points[i].x();
+ fp[i].y = points[i].y();
+ ++i;
+ }
+ drawPoints((QPointF *)(void *)fp, i);
+ points += i;
+ pointCount -= i;
+ }
+}
+
+void QOpenGLPaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (d->use_emulation) {
+ QPaintEngineEx::drawPoints(points, pointCount);
+ return;
+ }
+
+ d->setGradientOps(d->cpen.brush(), QRectF());
+
+ if (!d->cpen.isCosmetic() || d->high_quality_antialiasing) {
+ Qt::PenCapStyle capStyle = d->cpen.capStyle();
+ if (capStyle == Qt::FlatCap)
+ d->cpen.setCapStyle(Qt::SquareCap);
+ QPaintEngine::drawPoints(points, pointCount);
+ d->cpen.setCapStyle(capStyle);
+ return;
+ }
+
+ d->flushDrawQueue();
+
+ if (d->has_fast_pen) {
+ QVarLengthArray<q_vertexType> vertexArray(6 * pointCount);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ int j = 0;
+ for (int i = 0; i < pointCount; ++i) {
+ QPointF mapped = d->matrix.map(points[i]);
+
+ qreal xf = qRound(mapped.x());
+ qreal yf = qRound(mapped.y());
+
+ q_vertexType x = f2vt(xf);
+ q_vertexType y = f2vt(yf);
+
+ vertexArray[j++] = x;
+ vertexArray[j++] = y - f2vt(0.5);
+
+ vertexArray[j++] = x + f2vt(1.5);
+ vertexArray[j++] = y + f2vt(1.0);
+
+ vertexArray[j++] = x;
+ vertexArray[j++] = y + f2vt(1.0);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
+ glDrawArrays(GL_TRIANGLES, 0, pointCount*3);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glPopMatrix();
+ return;
+ }
+
+ const qreal *vertexArray = reinterpret_cast<const qreal*>(&points[0]);
+
+ if (sizeof(qreal) == sizeof(double)) {
+ Q_ASSERT(sizeof(QPointF) == 16);
+ glVertexPointer(2, GL_DOUBLE, 0, vertexArray);
+ }
+ else {
+ Q_ASSERT(sizeof(QPointF) == 8);
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDrawArrays(GL_POINTS, 0, pointCount);
+ glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+void QOpenGLPaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+ struct PointF {
+ qreal x;
+ qreal y;
+ };
+ struct LineF {
+ PointF p1;
+ PointF p2;
+ };
+ Q_ASSERT(sizeof(PointF) == sizeof(QPointF));
+ Q_ASSERT(sizeof(LineF) == sizeof(QLineF));
+ LineF fl[256];
+ while (lineCount) {
+ int i = 0;
+ while (i < lineCount && i < 256) {
+ fl[i].p1.x = lines[i].x1();
+ fl[i].p1.y = lines[i].y1();
+ fl[i].p2.x = lines[i].x2();
+ fl[i].p2.y = lines[i].y2();
+ ++i;
+ }
+ drawLines((QLineF *)(void *)fl, i);
+ lines += i;
+ lineCount -= i;
+ }
+}
+
+void QOpenGLPaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (d->use_emulation) {
+ QPaintEngineEx::drawLines(lines, lineCount);
+ return;
+ }
+
+ if (d->has_pen) {
+ QOpenGLCoordinateOffset offset(d);
+ if (d->has_fast_pen && !d->high_quality_antialiasing) {
+ //### gradient resolving on lines isn't correct
+ d->setGradientOps(d->cpen.brush(), QRectF());
+
+ bool useRects = false;
+ // scale or 90 degree rotation?
+ if (d->matrix.type() <= QTransform::TxTranslate
+ || !d->cpen.isCosmetic()
+ && (d->matrix.type() <= QTransform::TxScale
+ || (d->matrix.type() == QTransform::TxRotate
+ && d->matrix.m11() == 0 && d->matrix.m22() == 0))) {
+ useRects = true;
+ for (int i = 0; i < lineCount; ++i) {
+ if (lines[i].p1().x() != lines[i].p2().x()
+ && lines[i].p1().y() != lines[i].p2().y()) {
+ useRects = false;
+ break;
+ }
+ }
+ }
+
+ q_vertexType endCap = f2vt(d->cpen.capStyle() == Qt::FlatCap ? 0 : 0.5);
+ if (useRects) {
+ QVarLengthArray<q_vertexType> vertexArray(12 * lineCount);
+
+ q_vertexType quad[8];
+ for (int i = 0; i < lineCount; ++i) {
+ q_vertexType x1 = f2vt(lines[i].x1());
+ q_vertexType x2 = f2vt(lines[i].x2());
+ q_vertexType y1 = f2vt(lines[i].y1());
+ q_vertexType y2 = f2vt(lines[i].y2());
+
+ if (x1 == x2) {
+ if (y1 > y2)
+ qSwap(y1, y2);
+
+ quad[0] = x1 - f2vt(0.5);
+ quad[1] = y1 - endCap;
+
+ quad[2] = x1 + f2vt(0.5);
+ quad[3] = y1 - endCap;
+
+ quad[4] = x1 + f2vt(0.5);
+ quad[5] = y2 + endCap;
+
+ quad[6] = x1 - f2vt(0.5);
+ quad[7] = y2 + endCap;
+ } else {
+ if (x1 > x2)
+ qSwap(x1, x2);
+
+ quad[0] = x1 - endCap;
+ quad[1] = y1 + f2vt(0.5);
+
+ quad[2] = x1 - endCap;
+ quad[3] = y1 - f2vt(0.5);
+
+ quad[4] = x2 + endCap;
+ quad[5] = y1 - f2vt(0.5);
+
+ quad[6] = x2 + endCap;
+ quad[7] = y1 + f2vt(0.5);
+ }
+
+ addQuadAsTriangle(quad, &vertexArray[12*i]);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
+ glDrawArrays(GL_TRIANGLES, 0, lineCount*6);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ } else {
+ QVarLengthArray<q_vertexType> vertexArray(4 * lineCount);
+ for (int i = 0; i < lineCount; ++i) {
+ const QPointF a = lines[i].p1();
+ vertexArray[4*i] = f2vt(lines[i].x1());
+ vertexArray[4*i+1] = f2vt(lines[i].y1());
+ vertexArray[4*i+2] = f2vt(lines[i].x2());
+ vertexArray[4*i+3] = f2vt(lines[i].y2());
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
+ glDrawArrays(GL_LINES, 0, lineCount*2);
+
+ glVertexPointer(2, q_vertexTypeEnum, 4*sizeof(q_vertexType), vertexArray.constData() + 2);
+ glDrawArrays(GL_POINTS, 0, lineCount);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ }
+ } else {
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+ for (int i=0; i<lineCount; ++i) {
+ const QLineF &l = lines[i];
+
+ if (l.p1() == l.p2()) {
+ if (d->cpen.capStyle() != Qt::FlatCap) {
+ QPointF p = l.p1();
+ drawPoints(&p, 1);
+ }
+ continue;
+ }
+
+ path.moveTo(l.x1(), l.y1());
+ path.lineTo(l.x2(), l.y2());
+ }
+
+ if (d->has_fast_pen && d->high_quality_antialiasing)
+ d->strokeLines(path);
+ else
+ d->strokePath(path, false);
+ }
+ }
+}
+
+void QOpenGLPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
+ QVarLengthArray<QT_PointF> p(pointCount);
+ for (int i=0; i<pointCount; ++i) {
+ p[i].x = points[i].x();
+ p[i].y = points[i].y();
+ }
+ drawPolygon((QPointF *)p.data(), pointCount, mode);
+}
+
+void QOpenGLPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QOpenGLPaintEngine);
+ if(pointCount < 2)
+ return;
+
+ if (d->use_emulation) {
+ QPaintEngineEx::drawPolygon(points, pointCount, mode);
+ return;
+ }
+
+ QRectF bounds;
+ if ((mode == ConvexMode && !d->high_quality_antialiasing && state()->brushNeedsResolving()) ||
+ ((d->has_fast_pen && !d->high_quality_antialiasing) && state()->penNeedsResolving())) {
+ qreal minx = points[0].x(), miny = points[0].y(),
+ maxx = points[0].x(), maxy = points[0].y();
+ for (int i = 1; i < pointCount; ++i) {
+ const QPointF &pt = points[i];
+ if (minx > pt.x())
+ minx = pt.x();
+ if (miny > pt.y())
+ miny = pt.y();
+ if (maxx < pt.x())
+ maxx = pt.x();
+ if (maxy < pt.y())
+ maxy = pt.y();
+ }
+ bounds = QRectF(minx, maxx, maxx-minx, maxy-miny);
+ }
+
+ QOpenGLCoordinateOffset offset(d);
+
+ if (d->has_brush && mode != PolylineMode) {
+ if (mode == ConvexMode && !d->high_quality_antialiasing) {
+ //### resolving on polygon from points isn't correct
+ d->setGradientOps(d->cbrush, bounds);
+
+ const qreal *vertexArray = reinterpret_cast<const qreal*>(&points[0]);
+
+ if (sizeof(qreal) == sizeof(double)) {
+ Q_ASSERT(sizeof(QPointF) == 16);
+ glVertexPointer(2, GL_DOUBLE, 0, vertexArray);
+ }
+ else {
+ Q_ASSERT(sizeof(QPointF) == 8);
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, pointCount);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ } else {
+ QPainterPath path;
+ path.setFillRule(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
+ path.moveTo(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ path.lineTo(points[i]);
+ d->fillPath(path);
+ }
+ }
+
+ if (d->has_pen) {
+ if (d->has_fast_pen && !d->high_quality_antialiasing) {
+ d->setGradientOps(d->cpen.brush(), bounds);
+ QVarLengthArray<q_vertexType> vertexArray(pointCount*2 + 2);
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
+ int i;
+ for (i=0; i<pointCount; ++i) {
+ vertexArray[i*2] = f2vt(points[i].x());
+ vertexArray[i*2+1] = f2vt(points[i].y());
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ if (mode != PolylineMode) {
+ vertexArray[i*2] = vertexArray[0];
+ vertexArray[i*2+1] = vertexArray[1];
+ glDrawArrays(GL_LINE_STRIP, 0, pointCount+1);
+ } else {
+ glDrawArrays(GL_LINE_STRIP, 0, pointCount);
+ glDrawArrays(GL_POINTS, pointCount-1, 1);
+ }
+ glDisableClientState(GL_VERTEX_ARRAY);
+ } else {
+ QPainterPath path(points[0]);
+ for (int i = 1; i < pointCount; ++i)
+ path.lineTo(points[i]);
+ if (mode != PolylineMode)
+ path.lineTo(points[0]);
+
+ if (d->has_fast_pen)
+ d->strokeLines(path);
+ else
+ d->strokePath(path, true);
+ }
+ }
+}
+
+void QOpenGLPaintEnginePrivate::strokeLines(const QPainterPath &path)
+{
+ DEBUG_ONCE_STR("QOpenGLPaintEnginePrivate::strokeLines()");
+
+ qreal penWidth = cpen.widthF();
+
+ GLuint program = qt_gl_program_cache()->getProgram(drawable.context(),
+ FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
+ QGLLineMaskGenerator maskGenerator(path, matrix, penWidth == 0 ? 1.0 : penWidth,
+ offscreen, program);
+
+ disableClipping();
+
+ QBrush temp = cbrush;
+ QPointF origin = brush_origin;
+
+ cbrush = cpen.brush();
+ brush_origin = QPointF();
+
+ addItem(qt_mask_texture_cache()->getMask(maskGenerator, this));
+
+ cbrush = temp;
+ brush_origin = origin;
+
+ enableClipping();
+}
+
+extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
+
+void QOpenGLPaintEnginePrivate::strokePath(const QPainterPath &path, bool use_cache)
+{
+ QBrush old_brush = cbrush;
+ cbrush = cpen.brush();
+
+ qreal txscale = 1;
+ if (cpen.isCosmetic() || (qt_scaleForTransform(matrix, &txscale) && txscale != 1)) {
+ QTransform temp = matrix;
+ matrix = QTransform();
+ glPushMatrix();
+
+ if (has_antialiasing) {
+ glLoadIdentity();
+ } else {
+ float offs_matrix[] =
+ { 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0.5, 0.5, 0, 1 };
+ glLoadMatrixf(offs_matrix);
+ }
+
+ QPen pen = cpen;
+ if (txscale != 1)
+ pen.setWidthF(pen.width() * txscale);
+ if (use_cache)
+ fillPath(qt_opengl_stroke_cache()->getStrokedPath(temp.map(path), pen));
+ else
+ fillPath(strokeForPath(temp.map(path), pen));
+
+ glPopMatrix();
+ matrix = temp;
+ } else if (use_cache) {
+ fillPath(qt_opengl_stroke_cache()->getStrokedPath(path, cpen));
+ } else {
+ fillPath(strokeForPath(path, cpen));
+ }
+
+ cbrush = old_brush;
+}
+
+void QOpenGLPaintEnginePrivate::strokePathFastPen(const QPainterPath &path, bool needsResolving)
+{
+#ifndef QT_OPENGL_ES
+ QRectF bounds;
+ if (needsResolving)
+ bounds = path.controlPointRect();
+ setGradientOps(cpen.brush(), bounds);
+
+ QBezier beziers[32];
+ for (int i=0; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ if (i != 0)
+ glEnd(); // GL_LINE_STRIP
+ glBegin(GL_LINE_STRIP);
+ glVertex2d(e.x, e.y);
+
+ break;
+ case QPainterPath::LineToElement:
+ glVertex2d(e.x, e.y);
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ QPointF sp = path.elementAt(i-1);
+ QPointF cp2 = path.elementAt(i+1);
+ QPointF ep = path.elementAt(i+2);
+ i+=2;
+
+ qreal inverseScaleHalf = inverseScale / 2;
+ beziers[0] = QBezier::fromPoints(sp, e, cp2, ep);
+ QBezier *b = beziers;
+ while (b >= beziers) {
+ // check if we can pop the top bezier curve from the stack
+ qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
+ qreal d;
+ if (l > inverseScale) {
+ d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2)
+ - (b->y4 - b->y1)*(b->x1 - b->x2) )
+ + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3)
+ - (b->y4 - b->y1)*(b->x1 - b->x3) );
+ d /= l;
+ } else {
+ d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
+ qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
+ }
+ if (d < inverseScaleHalf || b == beziers + 31) {
+ // good enough, we pop it off and add the endpoint
+ glVertex2d(b->x4, b->y4);
+ --b;
+ } else {
+ // split, second half of the polygon goes lower into the stack
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+ } // case CurveToElement
+ default:
+ break;
+ } // end of switch
+ }
+ glEnd(); // GL_LINE_STRIP
+#else
+ // have to use vertex arrays on embedded
+ QRectF bounds;
+ if (needsResolving)
+ bounds = path.controlPointRect();
+ setGradientOps(cpen.brush(), bounds);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ tess_points.reset();
+ QBezier beziers[32];
+ for (int i=0; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ if (i != 0) {
+ glVertexPointer(2, q_vertexTypeEnum, 0, tess_points.data());
+ glDrawArrays(GL_LINE_STRIP, 0, tess_points.size());
+ tess_points.reset();
+ }
+ tess_points.add(QPointF(e.x, e.y));
+
+ break;
+ case QPainterPath::LineToElement:
+ tess_points.add(QPointF(e.x, e.y));
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ QPointF sp = path.elementAt(i-1);
+ QPointF cp2 = path.elementAt(i+1);
+ QPointF ep = path.elementAt(i+2);
+ i+=2;
+
+ qreal inverseScaleHalf = inverseScale / 2;
+ beziers[0] = QBezier::fromPoints(sp, e, cp2, ep);
+ QBezier *b = beziers;
+ while (b >= beziers) {
+ // check if we can pop the top bezier curve from the stack
+ qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
+ qreal d;
+ if (l > inverseScale) {
+ d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2)
+ - (b->y4 - b->y1)*(b->x1 - b->x2) )
+ + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3)
+ - (b->y4 - b->y1)*(b->x1 - b->x3) );
+ d /= l;
+ } else {
+ d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
+ qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
+ }
+ if (d < inverseScaleHalf || b == beziers + 31) {
+ // good enough, we pop it off and add the endpoint
+ tess_points.add(QPointF(b->x4, b->y4));
+ --b;
+ } else {
+ // split, second half of the polygon goes lower into the stack
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+ } // case CurveToElement
+ default:
+ break;
+ } // end of switch
+ }
+ glVertexPointer(2, q_vertexTypeEnum, 0, tess_points.data());
+ glDrawArrays(GL_LINE_STRIP, 0, tess_points.size());
+ glDisableClientState(GL_VERTEX_ARRAY);
+#endif
+}
+
+static bool pathClosed(const QPainterPath &path)
+{
+ QPointF lastMoveTo = path.elementAt(0);
+ QPointF lastPoint = lastMoveTo;
+
+ for (int i = 1; i < path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ if (lastMoveTo != lastPoint)
+ return false;
+ lastMoveTo = lastPoint = e;
+ break;
+ case QPainterPath::LineToElement:
+ lastPoint = e;
+ break;
+ case QPainterPath::CurveToElement:
+ lastPoint = path.elementAt(i + 2);
+ i+=2;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return lastMoveTo == lastPoint;
+}
+
+void QOpenGLPaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (path.isEmpty())
+ return;
+
+ if (d->use_emulation) {
+ QPaintEngineEx::drawPath(path);
+ return;
+ }
+
+ QOpenGLCoordinateOffset offset(d);
+
+ if (d->has_brush) {
+ bool path_closed = pathClosed(path);
+
+ bool has_thick_pen =
+ path_closed
+ && d->has_pen
+ && d->cpen.style() == Qt::SolidLine
+ && d->cpen.isSolid()
+ && d->cpen.color().alpha() == 255
+ && d->txop < QTransform::TxProject
+ && d->cpen.widthF() >= 2 / qSqrt(qMin(d->matrix.m11() * d->matrix.m11()
+ + d->matrix.m21() * d->matrix.m21(),
+ d->matrix.m12() * d->matrix.m12()
+ + d->matrix.m22() * d->matrix.m22()));
+
+ if (has_thick_pen) {
+ DEBUG_ONCE qDebug() << "QOpenGLPaintEngine::drawPath(): Using thick pen optimization, style:" << d->cbrush.style();
+
+ d->flushDrawQueue();
+
+ bool temp = d->high_quality_antialiasing;
+ d->high_quality_antialiasing = false;
+
+ updateCompositionMode(d->composition_mode);
+
+ d->fillPath(path);
+
+ d->high_quality_antialiasing = temp;
+ updateCompositionMode(d->composition_mode);
+ } else {
+ d->fillPath(path);
+ }
+ }
+
+ if (d->has_pen) {
+ if (d->has_fast_pen && !d->high_quality_antialiasing)
+ d->strokePathFastPen(path, state()->penNeedsResolving());
+ else
+ d->strokePath(path, true);
+ }
+}
+
+void QOpenGLPaintEnginePrivate::drawImageAsPath(const QRectF &r, const QImage &img, const QRectF &sr)
+{
+ QBrush old_brush = cbrush;
+ QPointF old_brush_origin = brush_origin;
+
+ qreal scaleX = r.width() / sr.width();
+ qreal scaleY = r.height() / sr.height();
+
+ QTransform brush_matrix;
+ brush_matrix.translate(r.left(), r.top());
+ brush_matrix.scale(scaleX, scaleY);
+ brush_matrix.translate(-sr.left(), -sr.top());
+
+ cbrush = QBrush(img);
+ cbrush.setTransform(brush_matrix);
+ brush_origin = QPointF();
+
+ QPainterPath p;
+ p.addRect(r);
+ fillPath(p);
+
+ cbrush = old_brush;
+ brush_origin = old_brush_origin;
+}
+
+void QOpenGLPaintEnginePrivate::drawTiledImageAsPath(const QRectF &r, const QImage &img, qreal sx, qreal sy)
+{
+ QBrush old_brush = cbrush;
+ QPointF old_brush_origin = brush_origin;
+
+ QTransform brush_matrix;
+ brush_matrix.translate(r.left(), r.top());
+ brush_matrix.scale(sx, sy);
+
+ cbrush = QBrush(img);
+ cbrush.setTransform(brush_matrix);
+ brush_origin = QPointF();
+
+ QPainterPath p;
+ p.addRect(r);
+ fillPath(p);
+
+ cbrush = old_brush;
+ brush_origin = old_brush_origin;
+}
+
+static const QRectF scaleRect(const QRectF &r, qreal sx, qreal sy)
+{
+ return QRectF(r.x() * sx, r.y() * sy, r.width() * sx, r.height() * sy);
+}
+
+template <typename T>
+static const T qSubImage(const T &image, const QRectF &src, QRectF *srcNew)
+{
+ const int sx1 = qMax(0, qFloor(src.left()));
+ const int sy1 = qMax(0, qFloor(src.top()));
+ const int sx2 = qMin(image.width(), qCeil(src.right()));
+ const int sy2 = qMin(image.height(), qCeil(src.bottom()));
+
+ const T sub = image.copy(sx1, sy1, sx2 - sx1, sy2 - sy1);
+
+ if (srcNew)
+ *srcNew = src.translated(-sx1, -sy1);
+
+ return sub;
+}
+
+void QOpenGLPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QOpenGLPaintEngine);
+ if (pm.depth() == 1) {
+ QPixmap tpx(pm.size());
+ tpx.fill(Qt::transparent);
+ QPainter p(&tpx);
+ p.setPen(d->cpen);
+ p.drawPixmap(0, 0, pm);
+ p.end();
+ drawPixmap(r, tpx, sr);
+ return;
+ }
+
+ const int sz = d->max_texture_size;
+ if (pm.width() > sz || pm.height() > sz) {
+ QRectF subsr;
+ const QPixmap sub = qSubImage(pm, sr, &subsr);
+
+ if (sub.width() <= sz && sub.height() <= sz) {
+ drawPixmap(r, sub, subsr);
+ } else {
+ const QPixmap scaled = sub.scaled(sz, sz, Qt::KeepAspectRatio);
+ const qreal sx = scaled.width() / qreal(sub.width());
+ const qreal sy = scaled.height() / qreal(sub.height());
+
+ drawPixmap(r, scaled, scaleRect(subsr, sx, sy));
+ }
+ return;
+ }
+
+
+ if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r)))
+ d->drawImageAsPath(r, pm.toImage(), sr);
+ else {
+ GLenum target = qt_gl_preferredTextureTarget();
+ d->flushDrawQueue();
+ d->drawable.bindTexture(pm, target);
+ drawTextureRect(pm.width(), pm.height(), r, sr, target);
+ }
+}
+
+void QOpenGLPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ QImage scaled;
+ const int sz = d->max_texture_size;
+ if (pm.width() > sz || pm.height() > sz) {
+ int rw = qCeil(r.width());
+ int rh = qCeil(r.height());
+ if (rw < pm.width() && rh < pm.height()) {
+ drawTiledPixmap(r, pm.copy(0, 0, rw, rh), QPointF());
+ return;
+ }
+
+ scaled = pm.toImage().scaled(sz, sz, Qt::KeepAspectRatio);
+ }
+
+ if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r))) {
+ if (scaled.isNull())
+ d->drawTiledImageAsPath(r, pm.toImage(), 1, 1);
+ else {
+ const qreal sx = pm.width() / qreal(scaled.width());
+ const qreal sy = pm.height() / qreal(scaled.height());
+ d->drawTiledImageAsPath(r, scaled, sx, sy);
+ }
+ } else {
+ d->flushDrawQueue();
+
+ if (scaled.isNull())
+ d->drawable.bindTexture(pm);
+ else
+ d->drawable.bindTexture(scaled);
+ updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, d->use_smooth_pixmap_transform);
+
+#ifndef QT_OPENGL_ES
+ glPushAttrib(GL_CURRENT_BIT);
+ glDisable(GL_TEXTURE_GEN_S);
+#endif
+ glColor4f(d->opacity, d->opacity, d->opacity, d->opacity);
+ glEnable(GL_TEXTURE_2D);
+
+ GLdouble tc_w = r.width()/pm.width();
+ GLdouble tc_h = r.height()/pm.height();
+
+ // Rotate the texture so that it is aligned correctly and the
+ // wrapping is done correctly
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ glRotatef(180.0, 0.0, 1.0, 0.0);
+ glRotatef(180.0, 0.0, 0.0, 1.0);
+
+ q_vertexType vertexArray[4*2];
+ q_vertexType texCoordArray[4*2];
+
+ qt_add_rect_to_array(r, vertexArray);
+ qt_add_texcoords_to_array(0, 0, tc_w, tc_h, texCoordArray);
+
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glPopMatrix();
+
+ glDisable(GL_TEXTURE_2D);
+#ifndef QT_OPENGL_ES
+ glPopAttrib();
+#endif
+ }
+}
+
+void QOpenGLPaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ const int sz = d->max_texture_size;
+ if (image.width() > sz || image.height() > sz) {
+ QRectF subsr;
+ const QImage sub = qSubImage(image, sr, &subsr);
+
+ if (sub.width() <= sz && sub.height() <= sz) {
+ drawImage(r, sub, subsr, 0);
+ } else {
+ const QImage scaled = sub.scaled(sz, sz, Qt::KeepAspectRatio);
+ const qreal sx = scaled.width() / qreal(sub.width());
+ const qreal sy = scaled.height() / qreal(sub.height());
+
+ drawImage(r, scaled, scaleRect(subsr, sx, sy), 0);
+ }
+ return;
+ }
+
+ if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r)))
+ d->drawImageAsPath(r, image, sr);
+ else {
+ GLenum target = qt_gl_preferredTextureTarget();
+ d->flushDrawQueue();
+ d->drawable.bindTexture(image, target);
+ drawTextureRect(image.width(), image.height(), r, sr, target);
+ }
+}
+
+void QOpenGLPaintEngine::drawTextureRect(int tx_width, int tx_height, const QRectF &r,
+ const QRectF &sr, GLenum target)
+{
+ Q_D(QOpenGLPaintEngine);
+#ifndef QT_OPENGL_ES
+ glPushAttrib(GL_CURRENT_BIT);
+ glDisable(GL_TEXTURE_GEN_S);
+#endif
+ glColor4f(d->opacity, d->opacity, d->opacity, d->opacity);
+ glEnable(target);
+ updateTextureFilter(target, GL_CLAMP_TO_EDGE, d->use_smooth_pixmap_transform);
+
+ qreal x1, x2, y1, y2;
+ if (target == GL_TEXTURE_2D) {
+ x1 = sr.x() / tx_width;
+ x2 = x1 + sr.width() / tx_width;
+ y1 = 1.0 - (sr.bottom() / tx_height);
+ y2 = 1.0 - (sr.y() / tx_height);
+ } else {
+ x1 = sr.x();
+ x2 = sr.right();
+ y1 = tx_height - sr.bottom();
+ y2 = tx_height - sr.y();
+ }
+
+ q_vertexType vertexArray[4*2];
+ q_vertexType texCoordArray[4*2];
+
+ qt_add_rect_to_array(r, vertexArray);
+ qt_add_texcoords_to_array(x1, y2, x2, y1, texCoordArray);
+
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glDisable(target);
+#ifndef QT_OPENGL_ES
+ glPopAttrib();
+#endif
+}
+
+#ifdef Q_WS_WIN
+HDC
+#else
+Qt::HANDLE
+#endif
+QOpenGLPaintEngine::handle() const
+{
+ return 0;
+}
+
+static const int x_margin = 1;
+static const int y_margin = 0;
+
+struct QGLGlyphCoord {
+ // stores the offset and size of a glyph texture
+ qreal x;
+ qreal y;
+ qreal width;
+ qreal height;
+ qreal log_width;
+ qreal log_height;
+ QFixed x_offset;
+ QFixed y_offset;
+};
+
+struct QGLFontTexture {
+ int x_offset; // glyph offset within the
+ int y_offset;
+ GLuint texture;
+ int width;
+ int height;
+};
+
+typedef QHash<glyph_t, QGLGlyphCoord*> QGLGlyphHash;
+typedef QHash<QFontEngine*, QGLGlyphHash*> QGLFontGlyphHash;
+typedef QHash<quint64, QGLFontTexture*> QGLFontTexHash;
+typedef QHash<const QGLContext*, QGLFontGlyphHash*> QGLContextHash;
+
+class QGLGlyphCache : public QObject
+{
+ Q_OBJECT
+public:
+ QGLGlyphCache() : QObject(0) { current_cache = 0; }
+ ~QGLGlyphCache();
+ QGLGlyphCoord *lookup(QFontEngine *, glyph_t);
+ void cacheGlyphs(QGLContext *, const QTextItemInt &, const QVarLengthArray<glyph_t> &);
+ void cleanCache();
+ void allocTexture(int width, int height, GLuint texture);
+
+public slots:
+ void cleanupContext(const QGLContext *);
+ void fontEngineDestroyed(QObject *);
+ void widgetDestroyed(QObject *);
+
+protected:
+ QGLGlyphHash *current_cache;
+ QGLFontTexHash qt_font_textures;
+ QGLContextHash qt_context_cache;
+};
+
+QGLGlyphCache::~QGLGlyphCache()
+{
+// qDebug() << "cleaning out the QGLGlyphCache";
+ cleanCache();
+}
+
+void QGLGlyphCache::fontEngineDestroyed(QObject *o)
+{
+// qDebug() << "fontEngineDestroyed()";
+ QFontEngine *fe = static_cast<QFontEngine *>(o); // safe, since only the type is used
+ QList<const QGLContext *> keys = qt_context_cache.keys();
+ const QGLContext *ctx = 0;
+
+ for (int i=0; i < keys.size(); ++i) {
+ QGLFontGlyphHash *font_cache = qt_context_cache.value(keys.at(i));
+ if (font_cache->find(fe) != font_cache->end()) {
+ ctx = keys.at(i);
+ QGLGlyphHash *cache = font_cache->take(fe);
+ delete cache;
+ break;
+ }
+ }
+
+ quint64 font_key = (reinterpret_cast<quint64>(ctx) << 32) | reinterpret_cast<quint64>(fe);
+ QGLFontTexture *tex = qt_font_textures.take(font_key);
+ if (tex) {
+#ifdef Q_WS_MAC
+ if (
+# ifndef QT_MAC_USE_COCOA
+ aglGetCurrentContext() != 0
+# else
+ qt_current_nsopengl_context() != 0
+# endif
+ )
+#endif
+ glDeleteTextures(1, &tex->texture);
+ delete tex;
+ }
+}
+
+void QGLGlyphCache::widgetDestroyed(QObject *)
+{
+// qDebug() << "widget destroyed";
+ cleanCache(); // ###
+}
+
+void QGLGlyphCache::cleanupContext(const QGLContext *ctx)
+{
+// qDebug() << "==> cleaning for: " << hex << ctx;
+ QGLFontGlyphHash *font_cache = qt_context_cache.take(ctx);
+
+ if (font_cache) {
+ QList<QFontEngine *> keys = font_cache->keys();
+ for (int i=0; i < keys.size(); ++i) {
+ QFontEngine *fe = keys.at(i);
+ delete font_cache->take(fe);
+ quint64 font_key = (reinterpret_cast<quint64>(ctx) << 32) | reinterpret_cast<quint64>(fe);
+ QGLFontTexture *font_tex = qt_font_textures.take(font_key);
+ if (font_tex) {
+#ifdef Q_WS_MAC
+ if (
+# ifndef QT_MAC_USE_COCOA
+ aglGetCurrentContext() == 0
+# else
+ qt_current_nsopengl_context() != 0
+# endif
+ )
+#endif
+ glDeleteTextures(1, &font_tex->texture);
+ delete font_tex;
+ }
+ }
+ delete font_cache;
+ }
+// qDebug() << "<=== done cleaning, num tex:" << qt_font_textures.size() << "num ctx:" << qt_context_cache.size();
+}
+
+void QGLGlyphCache::cleanCache()
+{
+ QGLFontTexHash::const_iterator it = qt_font_textures.constBegin();
+ if (QGLContext::currentContext()) {
+ while (it != qt_font_textures.constEnd()) {
+#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
+ if (qt_current_nsopengl_context() == 0)
+ break;
+#endif
+ glDeleteTextures(1, &it.value()->texture);
+ ++it;
+ }
+ }
+ qDeleteAll(qt_font_textures);
+ qt_font_textures.clear();
+
+ QList<const QGLContext *> keys = qt_context_cache.keys();
+ for (int i=0; i < keys.size(); ++i) {
+ QGLFontGlyphHash *font_cache = qt_context_cache.value(keys.at(i));
+ qDeleteAll(*font_cache);
+ font_cache->clear();
+ }
+ qDeleteAll(qt_context_cache);
+ qt_context_cache.clear();
+}
+
+void QGLGlyphCache::allocTexture(int width, int height, GLuint texture)
+{
+ uchar *tex_data = (uchar *) malloc(width*height*2);
+ memset(tex_data, 0, width*height*2);
+ glBindTexture(GL_TEXTURE_2D, texture);
+#ifndef QT_OPENGL_ES
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8_ALPHA8,
+ width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
+#else
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
+ width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
+#endif
+ free(tex_data);
+}
+
+#if 0
+// useful for debugging the glyph cache
+static QImage getCurrentTexture(const QColor &color, QGLFontTexture *font_tex)
+{
+ ushort *old_tex_data = (ushort *) malloc(font_tex->width*font_tex->height*2);
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
+ QImage im(font_tex->width, font_tex->height, QImage::Format_ARGB32);
+ for (int y=0; y<font_tex->height; ++y) {
+ for (int x=0; x<font_tex->width; ++x) {
+ im.setPixel(x, y, ((*(old_tex_data+x+y*font_tex->width)) << 24) | (0x00ffffff & color.rgb()));
+ }
+ }
+ delete old_tex_data;
+ return im;
+}
+#endif
+
+void QGLGlyphCache::cacheGlyphs(QGLContext *context, const QTextItemInt &ti,
+ const QVarLengthArray<glyph_t> &glyphs)
+{
+ QGLContextHash::const_iterator dev_it = qt_context_cache.constFind(context);
+ QGLFontGlyphHash *font_cache = 0;
+ const QGLContext *context_key = 0;
+
+ if (dev_it == qt_context_cache.constEnd()) {
+ // check for shared contexts
+ QList<const QGLContext *> contexts = qt_context_cache.keys();
+ for (int i=0; i<contexts.size(); ++i) {
+ const QGLContext *ctx = contexts.at(i);
+ if (ctx != context && qgl_share_reg()->checkSharing(context, ctx)) {
+ context_key = ctx;
+ dev_it = qt_context_cache.constFind(context_key);
+ break;
+ }
+ }
+ }
+
+ if (dev_it == qt_context_cache.constEnd()) {
+ // no shared contexts either - create a new entry
+ font_cache = new QGLFontGlyphHash;
+// qDebug() << "new context" << context << font_cache;
+ qt_context_cache.insert(context, font_cache);
+ if (context->isValid() && context->device()->devType() == QInternal::Widget) {
+ QWidget *widget = static_cast<QWidget *>(context->device());
+ connect(widget, SIGNAL(destroyed(QObject*)), SLOT(widgetDestroyed(QObject*)));
+ connect(QGLSignalProxy::instance(),
+ SIGNAL(aboutToDestroyContext(const QGLContext *)),
+ SLOT(cleanupContext(const QGLContext *)));
+ }
+ } else {
+ font_cache = dev_it.value();
+ }
+ Q_ASSERT(font_cache != 0);
+
+ QGLFontGlyphHash::const_iterator cache_it = font_cache->constFind(ti.fontEngine);
+ QGLGlyphHash *cache = 0;
+ if (cache_it == font_cache->constEnd()) {
+ cache = new QGLGlyphHash;
+ font_cache->insert(ti.fontEngine, cache);
+ connect(ti.fontEngine, SIGNAL(destroyed(QObject*)), SLOT(fontEngineDestroyed(QObject*)));
+ } else {
+ cache = cache_it.value();
+ }
+ current_cache = cache;
+
+ quint64 font_key = (reinterpret_cast<quint64>(context_key ? context_key : context) << 32)
+ | reinterpret_cast<quint64>(ti.fontEngine);
+ QGLFontTexHash::const_iterator it = qt_font_textures.constFind(font_key);
+ QGLFontTexture *font_tex;
+ if (it == qt_font_textures.constEnd()) {
+ GLuint font_texture;
+ glGenTextures(1, &font_texture);
+ GLint tex_height = qt_next_power_of_two(qRound(ti.ascent.toReal() + ti.descent.toReal())+2);
+ GLint tex_width = qt_next_power_of_two(tex_height*30); // ###
+ GLint max_tex_size;
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
+ Q_ASSERT(max_tex_size > 0);
+ if (tex_width > max_tex_size)
+ tex_width = max_tex_size;
+ allocTexture(tex_width, tex_height, font_texture);
+ font_tex = new QGLFontTexture;
+ font_tex->texture = font_texture;
+ font_tex->x_offset = x_margin;
+ font_tex->y_offset = y_margin;
+ font_tex->width = tex_width;
+ font_tex->height = tex_height;
+// qDebug() << "new font tex - width:" << tex_width << "height:"<< tex_height
+// << hex << "tex id:" << font_tex->texture << "key:" << font_key << "num cached:" << qt_font_textures.size();
+ qt_font_textures.insert(font_key, font_tex);
+ } else {
+ font_tex = it.value();
+ glBindTexture(GL_TEXTURE_2D, font_tex->texture);
+ }
+
+ for (int i=0; i< glyphs.size(); ++i) {
+ QGLGlyphHash::const_iterator it = cache->constFind(glyphs[i]);
+ if (it == cache->constEnd()) {
+ // render new glyph and put it in the cache
+ glyph_metrics_t metrics = ti.fontEngine->boundingBox(glyphs[i]);
+ int glyph_width = qRound(metrics.width.toReal())+2;
+ int glyph_height = qRound(ti.ascent.toReal() + ti.descent.toReal())+2;
+
+ if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
+ int strip_height = qt_next_power_of_two(qRound(ti.ascent.toReal() + ti.descent.toReal())+2);
+ font_tex->x_offset = x_margin;
+ font_tex->y_offset += strip_height;
+ if (font_tex->y_offset >= font_tex->height) {
+ // get hold of the old font texture
+ uchar *old_tex_data = (uchar *) malloc(font_tex->width*font_tex->height*2);
+ int old_tex_height = font_tex->height;
+#ifndef QT_OPENGL_ES
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
+#endif
+
+ // realloc a larger texture
+ glDeleteTextures(1, &font_tex->texture);
+ glGenTextures(1, &font_tex->texture);
+ font_tex->height = qt_next_power_of_two(font_tex->height + strip_height);
+ allocTexture(font_tex->width, font_tex->height, font_tex->texture);
+
+ // write back the old texture data
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, font_tex->width, old_tex_height,
+ GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
+ free(old_tex_data);
+
+ // update the texture coords and the y offset for the existing glyphs in
+ // the cache, because of the texture size change
+ QGLGlyphHash::iterator it = cache->begin();
+ while (it != cache->end()) {
+ it.value()->height = (it.value()->height * old_tex_height) / font_tex->height;
+ it.value()->y = (it.value()->y * old_tex_height) / font_tex->height;
+ ++it;
+ }
+ }
+ }
+
+ QImage glyph_im(ti.fontEngine->alphaMapForGlyph(glyphs[i]).convertToFormat(QImage::Format_Indexed8));
+ glyph_width = glyph_im.width();
+ Q_ASSERT(glyph_width >= 0);
+ // pad the glyph width to an even number
+ if (glyph_width%2 != 0)
+ ++glyph_width;
+
+ QGLGlyphCoord *qgl_glyph = new QGLGlyphCoord;
+ qgl_glyph->x = qreal(font_tex->x_offset) / font_tex->width;
+ qgl_glyph->y = qreal(font_tex->y_offset) / font_tex->height;
+ qgl_glyph->width = qreal(glyph_width) / font_tex->width;
+ qgl_glyph->height = qreal(glyph_height) / font_tex->height;
+ qgl_glyph->log_width = qreal(glyph_width);
+ qgl_glyph->log_height = qgl_glyph->height * font_tex->height;
+#ifdef Q_WS_MAC
+ qgl_glyph->x_offset = -metrics.x + 1;
+ qgl_glyph->y_offset = metrics.y - 2;
+#else
+ qgl_glyph->x_offset = -metrics.x;
+ qgl_glyph->y_offset = metrics.y;
+#endif
+
+ if (!glyph_im.isNull()) {
+
+ int idx = 0;
+ uchar *tex_data = (uchar *) malloc(glyph_width*glyph_im.height()*2);
+ memset(tex_data, 0, glyph_width*glyph_im.height()*2);
+
+ for (int y=0; y<glyph_im.height(); ++y) {
+ uchar *s = (uchar *) glyph_im.scanLine(y);
+ for (int x=0; x<glyph_im.width(); ++x) {
+ uchar alpha = qAlpha(glyph_im.color(*s));
+ tex_data[idx] = alpha;
+ tex_data[idx+1] = alpha;
+ ++s;
+ idx += 2;
+ }
+ if (glyph_im.width()%2 != 0)
+ idx += 2;
+ }
+ glTexSubImage2D(GL_TEXTURE_2D, 0, font_tex->x_offset, font_tex->y_offset,
+ glyph_width, glyph_im.height(),
+ GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
+ free(tex_data);
+ }
+ if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
+ font_tex->x_offset = x_margin;
+ font_tex->y_offset += glyph_height + y_margin;
+ } else {
+ font_tex->x_offset += glyph_width + x_margin;
+ }
+
+ cache->insert(glyphs[i], qgl_glyph);
+ }
+ }
+}
+
+QGLGlyphCoord *QGLGlyphCache::lookup(QFontEngine *, glyph_t g)
+{
+ Q_ASSERT(current_cache != 0);
+ // ### careful here
+ QGLGlyphHash::const_iterator it = current_cache->constFind(g);
+ if (it == current_cache->constEnd())
+ return 0;
+ else
+ return it.value();
+}
+
+Q_GLOBAL_STATIC(QGLGlyphCache, qt_glyph_cache)
+
+//
+// assumption: the context that this is called for has to be the
+// current context
+//
+void qgl_cleanup_glyph_cache(QGLContext *ctx)
+{
+ qt_glyph_cache()->cleanupContext(ctx);
+}
+
+void QOpenGLPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+
+ // fall back to drawing a polygon if the scale factor is large, or
+ // we use a gradient pen
+ if (ti.fontEngine->fontDef.pixelSize >= 64
+ || (d->matrix.det() > 1) || (d->pen_brush_style >= Qt::LinearGradientPattern
+ && d->pen_brush_style <= Qt::ConicalGradientPattern)) {
+ QPaintEngine::drawTextItem(p, textItem);
+ return;
+ }
+
+ d->flushDrawQueue();
+
+ // add the glyphs used to the glyph texture cache
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+ matrix.translate(qRound(p.x()), qRound(p.y()));
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+
+ // make sure the glyphs we want to draw are in the cache
+ qt_glyph_cache()->cacheGlyphs(d->drawable.context(), ti, glyphs);
+
+ d->setGradientOps(Qt::SolidPattern, QRectF()); // turns off gradient ops
+ qt_glColor4ubv(d->pen_color);
+ glEnable(GL_TEXTURE_2D);
+
+#ifdef Q_WS_QWS
+ // XXX: it is necessary to disable alpha writes on GLES/embedded because we don't want
+ // text rendering to update the alpha in the window surface.
+ // XXX: This may not be needed as this behavior does seem to be caused by driver bug
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+#endif
+
+ // do the actual drawing
+ q_vertexType vertexArray[4*2];
+ q_vertexType texCoordArray[4*2];
+
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bool antialias = !(ti.fontEngine->fontDef.styleStrategy & QFont::NoAntialias);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, antialias ? GL_LINEAR : GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, antialias ? GL_LINEAR : GL_NEAREST);
+
+ for (int i=0; i< glyphs.size(); ++i) {
+ QGLGlyphCoord *g = qt_glyph_cache()->lookup(ti.fontEngine, glyphs[i]);
+
+ // we don't cache glyphs with no width/height
+ if (!g)
+ continue;
+
+ qreal x1, x2, y1, y2;
+ x1 = g->x;
+ y1 = g->y;
+ x2 = x1 + g->width;
+ y2 = y1 + g->height;
+
+ QPointF logical_pos((positions[i].x - g->x_offset).toReal(),
+ (positions[i].y + g->y_offset).toReal());
+
+ qt_add_rect_to_array(QRectF(logical_pos, QSizeF(g->log_width, g->log_height)), vertexArray);
+ qt_add_texcoords_to_array(x1, y1, x2, y2, texCoordArray);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glDisable(GL_TEXTURE_2D);
+
+#ifdef Q_WS_QWS
+ // XXX: This may not be needed as this behavior does seem to be caused by driver bug
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+#endif
+}
+
+
+void QOpenGLPaintEngine::drawEllipse(const QRectF &rect)
+{
+#ifndef Q_WS_QWS
+ Q_D(QOpenGLPaintEngine);
+
+ if (d->use_emulation) {
+ QPaintEngineEx::drawEllipse(rect);
+ return;
+ }
+
+ if (d->high_quality_antialiasing) {
+ if (d->has_brush) {
+ d->disableClipping();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ GLuint program = qt_gl_program_cache()->getProgram(d->drawable.context(),
+ FRAGMENT_PROGRAM_MASK_ELLIPSE_AA, 0, true);
+ QGLEllipseMaskGenerator maskGenerator(rect,
+ d->matrix,
+ d->offscreen,
+ program,
+ mask_variable_locations[FRAGMENT_PROGRAM_MASK_ELLIPSE_AA]);
+
+ d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
+
+ d->enableClipping();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ }
+
+ if (d->has_pen) {
+ QPainterPath path;
+ path.addEllipse(rect);
+
+ d->strokePath(path, false);
+ }
+ } else {
+ DEBUG_ONCE_STR("QOpenGLPaintEngine::drawEllipse(): falling back to drawPath()");
+
+ QPainterPath path;
+ path.addEllipse(rect);
+ drawPath(path);
+ }
+#else
+ QPaintEngineEx::drawEllipse(rect);
+#endif
+}
+
+
+void QOpenGLPaintEnginePrivate::updateFragmentProgramData(int locations[])
+{
+#ifdef Q_WS_QWS
+ Q_UNUSED(locations);
+#else
+ QGL_FUNC_CONTEXT;
+
+ QSize sz = offscreen.offscreenSize();
+
+ float inv_mask_size_data[4] = { 1.0f / sz.width(), 1.0f / sz.height(), 0.0f, 0.0f };
+
+ sz = drawable_texture_size;
+
+ float inv_dst_size_data[4] = { 1.0f / sz.width(), 1.0f / sz.height(), 0.0f, 0.0f };
+
+ // default inv size 0.125f == 1.0f / 8.0f for pattern brushes
+ float inv_brush_texture_size_data[4] = { 0.125f, 0.125f };
+
+ // texture patterns have their own size
+ if (current_style == Qt::TexturePattern) {
+ QSize sz = cbrush.texture().size();
+
+ inv_brush_texture_size_data[0] = 1.0f / sz.width();
+ inv_brush_texture_size_data[1] = 1.0f / sz.height();
+ }
+
+ for (unsigned int i = 0; i < num_fragment_variables; ++i) {
+ int location = locations[i];
+
+ if (location < 0)
+ continue;
+
+ switch (i) {
+ case VAR_ANGLE:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, angle_data);
+ break;
+ case VAR_LINEAR:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, linear_data);
+ break;
+ case VAR_FMP:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, fmp_data);
+ break;
+ case VAR_FMP2_M_RADIUS2:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, fmp2_m_radius2_data);
+ break;
+ case VAR_INV_MASK_SIZE:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_mask_size_data);
+ break;
+ case VAR_INV_DST_SIZE:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_dst_size_data);
+ break;
+ case VAR_INV_MATRIX_M0:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[0]);
+ break;
+ case VAR_INV_MATRIX_M1:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[1]);
+ break;
+ case VAR_INV_MATRIX_M2:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[2]);
+ break;
+ case VAR_PORTERDUFF_AB:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, porterduff_ab_data);
+ break;
+ case VAR_PORTERDUFF_XYZ:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, porterduff_xyz_data);
+ break;
+ case VAR_INV_BRUSH_TEXTURE_SIZE:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_brush_texture_size_data);
+ break;
+ case VAR_MASK_OFFSET:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, mask_offset_data);
+ break;
+ case VAR_MASK_CHANNEL:
+ glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, mask_channel_data);
+ break;
+ case VAR_DST_TEXTURE:
+ case VAR_MASK_TEXTURE:
+ case VAR_PALETTE:
+ case VAR_BRUSH_TEXTURE:
+ // texture variables, not handled here
+ break;
+ default:
+ qDebug() << "QOpenGLPaintEnginePrivate: Unhandled fragment variable:" << i;
+ }
+ }
+#endif
+}
+
+
+void QOpenGLPaintEnginePrivate::copyDrawable(const QRectF &rect)
+{
+#ifdef Q_WS_QWS
+ Q_UNUSED(rect);
+#else
+ ensureDrawableTexture();
+
+ DEBUG_ONCE qDebug() << "Refreshing drawable_texture for rectangle" << rect;
+ QRectF screen_rect = rect.adjusted(-1, -1, 1, 1);
+
+ int left = qMax(0, static_cast<int>(screen_rect.left()));
+ int width = qMin(drawable.size().width() - left, static_cast<int>(screen_rect.width()) + 1);
+
+ int bottom = qMax(0, static_cast<int>(drawable.size().height() - screen_rect.bottom()));
+ int height = qMin(drawable.size().height() - bottom, static_cast<int>(screen_rect.height()) + 1);
+
+ glBindTexture(GL_TEXTURE_2D, drawable_texture);
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, left, bottom, left, bottom, width, height);
+#endif
+}
+
+
+void QOpenGLPaintEnginePrivate::composite(const QRectF &rect, const QPoint &maskOffset)
+{
+#ifdef Q_WS_QWS
+ Q_UNUSED(rect);
+ Q_UNUSED(maskOffset);
+#else
+ q_vertexType vertexArray[8];
+ qt_add_rect_to_array(rect, vertexArray);
+
+ composite(GL_TRIANGLE_FAN, vertexArray, 4, maskOffset);
+#endif
+}
+
+
+void QOpenGLPaintEnginePrivate::composite(GLuint primitive, const q_vertexType *vertexArray, int vertexCount, const QPoint &maskOffset)
+{
+#ifdef QT_OPENGL_ES
+ Q_UNUSED(primitive);
+ Q_UNUSED(vertexArray);
+ Q_UNUSED(vertexCount);
+ Q_UNUSED(maskOffset);
+#else
+ Q_Q(QOpenGLPaintEngine);
+ QGL_FUNC_CONTEXT;
+
+ DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Using compositing program: fragment_brush ="
+ << fragment_brush << ", fragment_composition_mode =" << fragment_composition_mode;
+
+ if (has_fast_composition_mode)
+ q->updateCompositionMode(composition_mode);
+ else {
+ qreal minX = 1e9, minY = 1e9, maxX = -1e9, maxY = -1e9;
+
+ for (int i = 0; i < vertexCount; ++i) {
+ qreal x = vt2f(vertexArray[2 * i]);
+ qreal y = vt2f(vertexArray[2 * i + 1]);
+
+ qreal tx, ty;
+ matrix.map(x, y, &tx, &ty);
+
+ minX = qMin(minX, tx);
+ minY = qMin(minY, ty);
+ maxX = qMax(maxX, tx);
+ maxY = qMax(maxY, ty);
+ }
+
+ QRectF r(minX, minY, maxX - minX, maxY - minY);
+ copyDrawable(r);
+
+ glBlendFunc(GL_ONE, GL_ZERO);
+ }
+
+ int *locations = painter_variable_locations[fragment_brush][fragment_composition_mode];
+
+ int texture_locations[] = { locations[VAR_DST_TEXTURE],
+ locations[VAR_MASK_TEXTURE],
+ locations[VAR_PALETTE] };
+
+ int brush_texture_location = locations[VAR_BRUSH_TEXTURE];
+
+ GLuint texture_targets[] = { GL_TEXTURE_2D,
+ GL_TEXTURE_2D,
+ GL_TEXTURE_1D };
+
+ GLuint textures[] = { drawable_texture,
+ offscreen.offscreenTexture(),
+ grad_palette };
+
+ const int num_textures = sizeof(textures) / sizeof(*textures);
+
+ Q_ASSERT(num_textures == sizeof(texture_locations) / sizeof(*texture_locations));
+ Q_ASSERT(num_textures == sizeof(texture_targets) / sizeof(*texture_targets));
+
+ for (int i = 0; i < num_textures; ++i)
+ if (texture_locations[i] >= 0) {
+ glActiveTexture(GL_TEXTURE0 + texture_locations[i]);
+ glBindTexture(texture_targets[i], textures[i]);
+ }
+
+ if (brush_texture_location >= 0) {
+ glActiveTexture(GL_TEXTURE0 + brush_texture_location);
+
+ if (current_style == Qt::TexturePattern)
+ drawable.bindTexture(cbrush.textureImage());
+ else
+ drawable.bindTexture(qt_imageForBrush(current_style, true));
+
+ updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, use_smooth_pixmap_transform);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glEnable(GL_FRAGMENT_PROGRAM_ARB);
+ GLuint program = qt_gl_program_cache()->getProgram(drawable.context(),
+ fragment_brush,
+ fragment_composition_mode, false);
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program);
+
+ mask_offset_data[0] = maskOffset.x();
+ mask_offset_data[1] = -maskOffset.y();
+
+ updateFragmentProgramData(locations);
+
+ glDrawArrays(primitive, 0, vertexCount);
+
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ for (int i = 0; i < num_textures; ++i)
+ if (texture_locations[i] >= 0) {
+ glActiveTexture(GL_TEXTURE0 + texture_locations[i]);
+ glBindTexture(texture_targets[i], 0);
+ }
+
+ if (brush_texture_location >= 0) {
+ glActiveTexture(GL_TEXTURE0 + brush_texture_location);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ glActiveTexture(GL_TEXTURE0);
+
+ if (!has_fast_composition_mode)
+ q->updateCompositionMode(composition_mode);
+#endif
+}
+
+void QOpenGLPaintEnginePrivate::cacheItemErased(int channel, const QRect &rect)
+{
+ bool isInDrawQueue = false;
+
+ foreach (const QDrawQueueItem &item, drawQueue) {
+ if (item.location.channel == channel && item.location.rect == rect) {
+ isInDrawQueue = true;
+ break;
+ }
+ }
+
+ if (isInDrawQueue)
+ flushDrawQueue();
+}
+
+void QOpenGLPaintEnginePrivate::addItem(const QGLMaskTextureCache::CacheLocation &location)
+{
+ drawQueue << QDrawQueueItem(opacity, cbrush, brush_origin, composition_mode, matrix, location);
+}
+
+void QOpenGLPaintEnginePrivate::drawItem(const QDrawQueueItem &item)
+{
+ Q_Q(QOpenGLPaintEngine);
+
+ opacity = item.opacity;
+ brush_origin = item.brush_origin;
+ q->updateCompositionMode(item.composition_mode);
+ matrix = item.matrix;
+ cbrush = item.brush;
+ brush_style = item.brush.style();
+
+ mask_channel_data[0] = item.location.channel == 0;
+ mask_channel_data[1] = item.location.channel == 1;
+ mask_channel_data[2] = item.location.channel == 2;
+ mask_channel_data[3] = item.location.channel == 3;
+
+ setGradientOps(item.brush, item.location.screen_rect);
+
+ composite(item.location.screen_rect, item.location.rect.topLeft() - item.location.screen_rect.topLeft()
+ - QPoint(0, offscreen.offscreenSize().height() - drawable.size().height()));
+}
+
+void QOpenGLPaintEnginePrivate::flushDrawQueue()
+{
+#ifndef QT_OPENGL_ES
+ Q_Q(QOpenGLPaintEngine);
+
+ offscreen.release();
+
+ if (!drawQueue.isEmpty()) {
+ DEBUG_ONCE qDebug() << "QOpenGLPaintEngine::flushDrawQueue():" << drawQueue.size() << "items";
+
+ glPushMatrix();
+ glLoadIdentity();
+ qreal old_opacity = opacity;
+ QPointF old_brush_origin = brush_origin;
+ QPainter::CompositionMode old_composition_mode = composition_mode;
+ QTransform old_matrix = matrix;
+ QBrush old_brush = cbrush;
+
+ bool hqaa_old = high_quality_antialiasing;
+
+ high_quality_antialiasing = true;
+
+ foreach (const QDrawQueueItem &item, drawQueue)
+ drawItem(item);
+
+ opacity = old_opacity;
+ brush_origin = old_brush_origin;
+ q->updateCompositionMode(old_composition_mode);
+ matrix = old_matrix;
+ cbrush = old_brush;
+ brush_style = old_brush.style();
+
+ high_quality_antialiasing = hqaa_old;
+
+ setGLBrush(old_brush.color());
+ qt_glColor4ubv(brush_color);
+
+ drawQueue.clear();
+
+ glPopMatrix();
+ }
+#endif
+}
+
+void QOpenGLPaintEngine::clipEnabledChanged()
+{
+ Q_D(QOpenGLPaintEngine);
+
+ d->updateDepthClip();
+}
+
+void QOpenGLPaintEngine::penChanged()
+{
+ updatePen(state()->pen);
+}
+
+void QOpenGLPaintEngine::brushChanged()
+{
+ updateBrush(state()->brush, state()->brushOrigin);
+}
+
+void QOpenGLPaintEngine::brushOriginChanged()
+{
+ updateBrush(state()->brush, state()->brushOrigin);
+}
+
+void QOpenGLPaintEngine::opacityChanged()
+{
+ Q_D(QOpenGLPaintEngine);
+ QPainterState *s = state();
+ d->opacity = s->opacity;
+ updateBrush(s->brush, s->brushOrigin);
+ updatePen(s->pen);
+}
+
+void QOpenGLPaintEngine::compositionModeChanged()
+{
+ updateCompositionMode(state()->composition_mode);
+}
+
+void QOpenGLPaintEngine::renderHintsChanged()
+{
+ updateRenderHints(state()->renderHints);
+}
+
+void QOpenGLPaintEngine::transformChanged()
+{
+ updateMatrix(state()->matrix);
+}
+
+static QPainterPath painterPathFromVectorPath(const QVectorPath &path)
+{
+ const qreal *points = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+
+ QPainterPath p;
+ if (types) {
+ int id = 0;
+ for (int i=0; i<path.elementCount(); ++i) {
+ switch(types[i]) {
+ case QPainterPath::MoveToElement:
+ p.moveTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ break;
+ case QPainterPath::LineToElement:
+ p.lineTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ break;
+ case QPainterPath::CurveToElement: {
+ QPointF p1(points[id], points[id+1]);
+ QPointF p2(points[id+2], points[id+3]);
+ QPointF p3(points[id+4], points[id+5]);
+ p.cubicTo(p1, p2, p3);
+ id+=6;
+ break;
+ }
+ case QPainterPath::CurveToDataElement:
+ ;
+ break;
+ }
+ }
+ } else {
+ p.moveTo(QPointF(points[0], points[1]));
+ int id = 2;
+ for (int i=1; i<path.elementCount(); ++i) {
+ p.lineTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ }
+ }
+ if (path.hints() & QVectorPath::WindingFill)
+ p.setFillRule(Qt::WindingFill);
+
+ return p;
+}
+
+void QOpenGLPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ if (brush.style() == Qt::NoBrush)
+ return;
+
+ if (!d->use_fragment_programs && needsEmulation(brush.style())) {
+ QPainter *p = painter();
+ QBrush oldBrush = p->brush();
+ p->setBrush(brush);
+ qt_draw_helper(p->d_ptr, painterPathFromVectorPath(path), QPainterPrivate::FillDraw);
+ p->setBrush(oldBrush);
+ return;
+ }
+
+ QBrush old_brush = state()->brush;
+ updateBrush(brush, state()->brushOrigin);
+
+ const qreal *points = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+ if (!types && path.shape() == QVectorPath::RectangleHint) {
+ QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
+ QPen old_pen = state()->pen;
+ updatePen(Qt::NoPen);
+ drawRects(&r, 1);
+ updatePen(old_pen);
+ } else {
+ d->fillPath(painterPathFromVectorPath(path));
+ }
+
+ updateBrush(old_brush, state()->brushOrigin);
+}
+
+template <typename T> static inline bool isRect(const T *pts, int elementCount) {
+ return (elementCount == 5 // 5-point polygon, check for closed rect
+ && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
+ && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
+ && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
+ ) ||
+ (elementCount == 4 // 4-point polygon, check for unclosed rect
+ && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
+ && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
+ );
+}
+
+void QOpenGLPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+ const qreal *points = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+ if (!types && path.shape() == QVectorPath::RectangleHint) {
+ QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
+ updateClipRegion(QRegion(r.toRect()), op);
+ return;
+ }
+
+ QPainterPath p;
+ if (types) {
+ int id = 0;
+ for (int i=0; i<path.elementCount(); ++i) {
+ switch(types[i]) {
+ case QPainterPath::MoveToElement:
+ p.moveTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ break;
+ case QPainterPath::LineToElement:
+ p.lineTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ break;
+ case QPainterPath::CurveToElement: {
+ QPointF p1(points[id], points[id+1]);
+ QPointF p2(points[id+2], points[id+3]);
+ QPointF p3(points[id+4], points[id+5]);
+ p.cubicTo(p1, p2, p3);
+ id+=6;
+ break;
+ }
+ case QPainterPath::CurveToDataElement:
+ ;
+ break;
+ }
+ }
+ } else if (!path.isEmpty()) {
+ p.moveTo(QPointF(points[0], points[1]));
+ int id = 2;
+ for (int i=1; i<path.elementCount(); ++i) {
+ p.lineTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ }
+ }
+ if (path.hints() & QVectorPath::WindingFill)
+ p.setFillRule(Qt::WindingFill);
+
+ updateClipRegion(QRegion(p.toFillPolygon().toPolygon(), p.fillRule()), op);
+ return;
+}
+
+void QOpenGLPaintEngine::setState(QPainterState *s)
+{
+ Q_D(QOpenGLPaintEngine);
+ QPaintEngineEx::setState(s);
+ if (isActive()) {
+ d->updateDepthClip();
+ penChanged();
+ brushChanged();
+ opacityChanged();
+ compositionModeChanged();
+ renderHintsChanged();
+ transformChanged();
+ }
+}
+
+QPainterState *QOpenGLPaintEngine::createState(QPainterState *orig) const
+{
+ QOpenGLPaintEngineState *s;
+ if (!orig)
+ s = new QOpenGLPaintEngineState();
+ else
+ s = new QOpenGLPaintEngineState(*static_cast<QOpenGLPaintEngineState *>(orig));
+
+ return s;
+}
+
+//
+// QOpenGLPaintEngineState
+//
+
+QOpenGLPaintEngineState::QOpenGLPaintEngineState(QOpenGLPaintEngineState &other)
+ : QPainterState(other)
+{
+ clipRegion = other.clipRegion;
+ hasClipping = other.hasClipping;
+ fastClip = other.fastClip;
+}
+
+QOpenGLPaintEngineState::QOpenGLPaintEngineState()
+{
+ hasClipping = false;
+}
+
+QOpenGLPaintEngineState::~QOpenGLPaintEngineState()
+{
+}
+
+void QOpenGLPaintEnginePrivate::ensureDrawableTexture()
+{
+ if (!dirty_drawable_texture)
+ return;
+
+ dirty_drawable_texture = false;
+
+#ifndef QT_OPENGL_ES
+ glGenTextures(1, &drawable_texture);
+ glBindTexture(GL_TEXTURE_2D, drawable_texture);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
+ drawable_texture_size.width(),
+ drawable_texture_size.height(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+#endif
+}
+
+QPixmapFilter *QOpenGLPaintEngine::createPixmapFilter(int type) const
+{
+ if (QGLContext::currentContext())
+ return QGLContext::currentContext()->d_func()->createPixmapFilter(type);
+ else
+ return 0;
+}
+
+
+QT_END_NAMESPACE
+
+#include "qpaintengine_opengl.moc"
diff --git a/src/opengl/qpaintengine_opengl_p.h b/src/opengl/qpaintengine_opengl_p.h
new file mode 100644
index 0000000000..ad5d56bda8
--- /dev/null
+++ b/src/opengl/qpaintengine_opengl_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** 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 QPAINTENGINE_OPENGL_P_H
+#define QPAINTENGINE_OPENGL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qpaintengineex_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLPaintEnginePrivate;
+
+class QOpenGLPaintEngineState : public QPainterState
+{
+public:
+ QOpenGLPaintEngineState(QOpenGLPaintEngineState &other);
+ QOpenGLPaintEngineState();
+ ~QOpenGLPaintEngineState();
+
+ QRegion clipRegion;
+ bool hasClipping;
+ QRect fastClip;
+};
+
+class QOpenGLPaintEngine : public QPaintEngineEx
+{
+ Q_DECLARE_PRIVATE(QOpenGLPaintEngine)
+public:
+ QOpenGLPaintEngine();
+ ~QOpenGLPaintEngine();
+
+ bool begin(QPaintDevice *pdev);
+ bool end();
+
+ // new stuff
+ void clipEnabledChanged();
+ void penChanged();
+ void brushChanged();
+ void brushOriginChanged();
+ void opacityChanged();
+ void compositionModeChanged();
+ void renderHintsChanged();
+ void transformChanged();
+
+ void fill(const QVectorPath &path, const QBrush &brush);
+ void clip(const QVectorPath &path, Qt::ClipOperation op);
+
+ void setState(QPainterState *s);
+ QPainterState *createState(QPainterState *orig) const;
+ inline QOpenGLPaintEngineState *state() {
+ return static_cast<QOpenGLPaintEngineState *>(QPaintEngineEx::state());
+ }
+ inline const QOpenGLPaintEngineState *state() const {
+ return static_cast<const QOpenGLPaintEngineState *>(QPaintEngineEx::state());
+ }
+
+
+ // old stuff
+ void updateState(const QPaintEngineState &state);
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush, const QPointF &pt);
+ void updateFont(const QFont &font);
+ void updateMatrix(const QTransform &matrix);
+ void updateClipRegion(const QRegion &region, Qt::ClipOperation op);
+ void updateRenderHints(QPainter::RenderHints hints);
+ void updateCompositionMode(QPainter::CompositionMode composition_mode);
+
+ void drawRects(const QRectF *r, int rectCount);
+ void drawLines(const QLineF *lines, int lineCount);
+ void drawPoints(const QPointF *p, int pointCount);
+ void drawRects(const QRect *r, int rectCount);
+ void drawLines(const QLine *lines, int lineCount);
+ void drawPoints(const QPoint *p, int pointCount);
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+
+ void drawPath(const QPainterPath &path);
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ void drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags conversionFlags);
+ void drawTextItem(const QPointF &p, const QTextItem &ti);
+
+ void drawEllipse(const QRectF &rect);
+
+ QPixmapFilter *createPixmapFilter(int type) const;
+
+#ifdef Q_WS_WIN
+ HDC handle() const;
+#else
+ Qt::HANDLE handle() const;
+#endif
+ inline Type type() const { return QPaintEngine::OpenGL; }
+
+private:
+ void drawPolyInternal(const QPolygonF &pa, bool close = true);
+ void drawTextureRect(int tx_width, int tx_height, const QRectF &r, const QRectF &sr, GLenum target);
+ Q_DISABLE_COPY(QOpenGLPaintEngine)
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_OPENGL_P_H
diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp
new file mode 100644
index 0000000000..5d668cdc79
--- /dev/null
+++ b/src/opengl/qpixmapdata_gl.cpp
@@ -0,0 +1,313 @@
+/****************************************************************************
+**
+** 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 "qpixmap.h"
+
+#include <private/qpaintengine_raster_p.h>
+
+#include "qpixmapdata_gl_p.h"
+
+#include <private/qgl_p.h>
+#include <private/qdrawhelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern QGLWidget* qt_gl_share_widget();
+
+class QGLShareContextScope
+{
+public:
+ QGLShareContextScope(const QGLContext *ctx)
+ : m_oldContext(0)
+ , m_ctx(const_cast<QGLContext *>(ctx))
+ {
+ const QGLContext *currentContext = QGLContext::currentContext();
+ if (currentContext != ctx && !qgl_share_reg()->checkSharing(ctx, currentContext)) {
+ m_oldContext = const_cast<QGLContext *>(currentContext);
+ m_ctx->makeCurrent();
+ }
+ }
+
+ operator QGLContext *()
+ {
+ return m_ctx;
+ }
+
+ QGLContext *operator->()
+ {
+ return m_ctx;
+ }
+
+ ~QGLShareContextScope()
+ {
+ if (m_oldContext)
+ m_oldContext->makeCurrent();
+ }
+
+private:
+ QGLContext *m_oldContext;
+ QGLContext *m_ctx;
+};
+
+void qt_gl_convertFromGLImage(QImage *img)
+{
+ const int w = img->width();
+ const int h = img->height();
+
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ uint *p = (uint*)img->bits();
+ uint *end = p + w*h;
+
+ while (p < end) {
+ uint a = *p << 24;
+ *p = (*p >> 8) | a;
+ p++;
+ }
+
+ *img = img->mirrored();
+ } else {
+ // mirror image
+ uint *data = (uint *)img->bits();
+
+ const int mid = h/2;
+
+ for (int y = 0; y < mid; ++y) {
+ uint *p = data + y * w;
+ uint *end = p + w;
+ uint *q = data + (h - y - 1) * w;
+
+ while (p < end)
+ qSwap(*p++, *q++);
+ }
+ }
+}
+
+
+static int qt_gl_pixmap_serial = 0;
+
+QGLPixmapData::QGLPixmapData(PixelType type)
+ : QPixmapData(type, OpenGLClass)
+ , m_width(0)
+ , m_height(0)
+ , m_texture(0)
+ , m_dirty(false)
+{
+ setSerialNumber(++qt_gl_pixmap_serial);
+}
+
+QGLPixmapData::~QGLPixmapData()
+{
+ if (m_texture && qt_gl_share_widget()) {
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ glDeleteTextures(1, &m_texture);
+ }
+}
+
+bool QGLPixmapData::isValid() const
+{
+ return m_width > 0 && m_height > 0;
+}
+
+bool QGLPixmapData::isValidContext(const QGLContext *ctx) const
+{
+ const QGLContext *share_ctx = qt_gl_share_widget()->context();
+ return ctx == share_ctx || qgl_share_reg()->checkSharing(ctx, share_ctx);
+}
+
+void QGLPixmapData::resize(int width, int height)
+{
+ if (width == m_width && height == m_height)
+ return;
+
+ m_width = width;
+ m_height = height;
+
+ m_source = QImage();
+ m_dirty = isValid();
+ setSerialNumber(++qt_gl_pixmap_serial);
+}
+
+void QGLPixmapData::ensureCreated() const
+{
+ if (!m_dirty)
+ return;
+
+ m_dirty = false;
+
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+
+ const GLenum format = qt_gl_preferredTextureFormat();
+ const GLenum target = qt_gl_preferredTextureTarget();
+
+ if (!m_texture)
+ glGenTextures(1, &m_texture);
+
+ glBindTexture(target, m_texture);
+
+ if (m_source.isNull()) {
+ glTexImage2D(target, 0, GL_RGBA, m_width, m_height, 0, format, GL_UNSIGNED_BYTE, 0);
+ } else {
+ const QImage tx = ctx->d_func()->convertToGLFormat(m_source, true, format);
+
+ glBindTexture(target, m_texture);
+ glTexImage2D(target, 0, GL_RGBA, m_width, m_height, 0, format,
+ GL_UNSIGNED_BYTE, tx.bits());
+
+ m_source = QImage();
+ }
+}
+
+void QGLPixmapData::fromImage(const QImage &image,
+ Qt::ImageConversionFlags)
+{
+ if (image.size() == QSize(m_width, m_height))
+ setSerialNumber(++qt_gl_pixmap_serial);
+ resize(image.width(), image.height());
+ m_source = image;
+ m_dirty = true;
+}
+
+void QGLPixmapData::fill(const QColor &color)
+{
+ if (!isValid())
+ return;
+
+ if (!m_source.isNull()) {
+ m_source.fill(PREMUL(color.rgba()));
+ } else {
+ // ## TODO: improve performance here
+ QImage img(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
+ img.fill(PREMUL(color.rgba()));
+
+ fromImage(img, 0);
+ }
+}
+
+bool QGLPixmapData::hasAlphaChannel() const
+{
+ return true;
+}
+
+QImage QGLPixmapData::toImage() const
+{
+ if (!isValid())
+ return QImage();
+
+ if (!m_source.isNull())
+ return m_source;
+ else if (m_dirty)
+ return QImage(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
+
+ ensureCreated();
+
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ QImage img(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
+
+ GLenum format = qt_gl_preferredTextureFormat();
+ GLenum target = qt_gl_preferredTextureTarget();
+
+ glBindTexture(target, m_texture);
+#ifndef QT_OPENGL_ES
+ glGetTexImage(target, 0, format, GL_UNSIGNED_BYTE, img.bits());
+#else
+ // XXX - cannot download textures this way on OpenGL/ES.
+#endif
+
+ qt_gl_convertFromGLImage(&img);
+
+ return img;
+}
+
+QPaintEngine* QGLPixmapData::paintEngine() const
+{
+ if (!isValid())
+ return 0;
+
+ m_source = toImage();
+ m_dirty = true;
+
+ return m_source.paintEngine();
+}
+
+GLuint QGLPixmapData::bind() const
+{
+ ensureCreated();
+ glBindTexture(qt_gl_preferredTextureTarget(), m_texture);
+ return m_texture;
+}
+
+GLuint QGLPixmapData::textureId() const
+{
+ ensureCreated();
+ return m_texture;
+}
+
+extern int qt_defaultDpiX();
+extern int qt_defaultDpiY();
+
+int QGLPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ switch (metric) {
+ case QPaintDevice::PdmWidth:
+ return m_width;
+ case QPaintDevice::PdmHeight:
+ return m_height;
+ case QPaintDevice::PdmNumColors:
+ return 0;
+ case QPaintDevice::PdmDepth:
+ return pixelType() == QPixmapData::PixmapType ? 32 : 1;
+ case QPaintDevice::PdmWidthMM:
+ return qRound(m_width * 25.4 / qt_defaultDpiX());
+ case QPaintDevice::PdmHeightMM:
+ return qRound(m_height * 25.4 / qt_defaultDpiY());
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX:
+ return qt_defaultDpiX();
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return qt_defaultDpiY();
+ default:
+ qWarning("QGLPixmapData::metric(): Invalid metric");
+ return 0;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h
new file mode 100644
index 0000000000..63703fd1e1
--- /dev/null
+++ b/src/opengl/qpixmapdata_gl_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** 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 QPIXMAPDATA_GL_P_H
+#define QPIXMAPDATA_GL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgl.h"
+
+#include "private/qpixmapdata_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintEngine;
+
+class QGLPixmapData : public QPixmapData
+{
+public:
+ QGLPixmapData(PixelType type);
+ ~QGLPixmapData();
+
+ bool isValid() const;
+
+ void resize(int width, int height);
+ void fromImage(const QImage &image,
+ Qt::ImageConversionFlags flags);
+
+ void fill(const QColor &color);
+ bool hasAlphaChannel() const;
+ QImage toImage() const;
+ QPaintEngine* paintEngine() const;
+
+ GLuint bind() const;
+ GLuint textureId() const;
+
+ bool isValidContext(const QGLContext *ctx) const;
+
+ void ensureCreated() const;
+
+protected:
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+
+private:
+ QGLPixmapData(const QGLPixmapData &other);
+ QGLPixmapData &operator=(const QGLPixmapData &other);
+
+ int m_width;
+ int m_height;
+
+ mutable GLuint m_texture;
+ mutable bool m_dirty;
+ mutable QImage m_source;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPDATA_GL_P_H
+
+
diff --git a/src/opengl/qwindowsurface_gl.cpp b/src/opengl/qwindowsurface_gl.cpp
new file mode 100644
index 0000000000..3dd30649df
--- /dev/null
+++ b/src/opengl/qwindowsurface_gl.cpp
@@ -0,0 +1,661 @@
+/****************************************************************************
+**
+** 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 <QtGui/QApplication>
+#include <QtGui/QColormap>
+#include <QtGui/QDesktopWidget>
+#include <QtGui/QPaintDevice>
+#include <QtGui/QWidget>
+
+#include <qglframebufferobject.h>
+#include <qglpixelbuffer.h>
+#include <qcolormap.h>
+#include <qdesktopwidget.h>
+#include "qdebug.h"
+
+#ifdef Q_WS_X11
+#include <private/qt_x11_p.h>
+#include <qx11info_x11.h>
+#include <private/qwidget_p.h>
+
+#ifndef QT_OPENGL_ES
+#include <GL/glx.h>
+#include <X11/Xlib.h>
+#endif
+#endif //Q_WS_X11
+
+#include <private/qglextensions_p.h>
+#include <private/qwindowsurface_gl_p.h>
+
+#include <private/qgl_p.h>
+
+#include <private/qglpixelbuffer_p.h>
+#include <private/qgraphicssystem_gl_p.h>
+#include <private/qpaintengine_opengl_p.h>
+
+#ifndef GLX_ARB_multisample
+#define GLX_SAMPLE_BUFFERS_ARB 100000
+#define GLX_SAMPLES_ARB 100001
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//
+// QGLGraphicsSystem
+//
+#ifdef Q_WS_WIN
+Q_GUI_EXPORT bool qt_win_owndc_required;
+#endif
+QGLGraphicsSystem::QGLGraphicsSystem()
+ : QGraphicsSystem()
+{
+#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
+ // only override the system defaults if the user hasn't already
+ // picked a visual
+ if (X11->visual == 0 && X11->visual_id == -1 && X11->visual_class == -1) {
+ // find a double buffered, RGBA visual that supports OpenGL
+ // and set that as the default visual for windows in Qt
+ int i = 0;
+ int spec[16];
+ spec[i++] = GLX_RGBA;
+#if 0
+ spec[i++] = GLX_DOUBLEBUFFER;
+ spec[i++] = GLX_DEPTH_SIZE;
+ spec[i++] = 8;
+ spec[i++] = GLX_STENCIL_SIZE;
+ spec[i++] = 8;
+ spec[i++] = GLX_SAMPLE_BUFFERS_ARB;
+ spec[i++] = 1;
+ spec[i++] = GLX_SAMPLES_ARB;
+ spec[i++] = 4;
+#endif
+ spec[i++] = XNone;
+
+ XVisualInfo *vi = glXChooseVisual(X11->display, X11->defaultScreen, spec);
+ if (vi) {
+ X11->visual_id = vi->visualid;
+ X11->visual_class = vi->c_class;
+
+ QGLFormat format;
+ int res;
+ glXGetConfig(X11->display, vi, GLX_LEVEL, &res);
+ format.setPlane(res);
+ glXGetConfig(X11->display, vi, GLX_DOUBLEBUFFER, &res);
+ format.setDoubleBuffer(res);
+ glXGetConfig(X11->display, vi, GLX_DEPTH_SIZE, &res);
+ format.setDepth(res);
+ if (format.depth())
+ format.setDepthBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_RGBA, &res);
+ format.setRgba(res);
+ glXGetConfig(X11->display, vi, GLX_RED_SIZE, &res);
+ format.setRedBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_GREEN_SIZE, &res);
+ format.setGreenBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_BLUE_SIZE, &res);
+ format.setBlueBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_ALPHA_SIZE, &res);
+ format.setAlpha(res);
+ if (format.alpha())
+ format.setAlphaBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_ACCUM_RED_SIZE, &res);
+ format.setAccum(res);
+ if (format.accum())
+ format.setAccumBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_STENCIL_SIZE, &res);
+ format.setStencil(res);
+ if (format.stencil())
+ format.setStencilBufferSize(res);
+ glXGetConfig(X11->display, vi, GLX_STEREO, &res);
+ format.setStereo(res);
+ glXGetConfig(X11->display, vi, GLX_SAMPLE_BUFFERS_ARB, &res);
+ format.setSampleBuffers(res);
+ if (format.sampleBuffers()) {
+ glXGetConfig(X11->display, vi, GLX_SAMPLES_ARB, &res);
+ format.setSamples(res);
+ }
+
+ QGLWindowSurface::surfaceFormat = format;
+ XFree(vi);
+
+ printf("using visual class %x, id %x\n", X11->visual_class, X11->visual_id);
+ }
+ }
+#elif defined(Q_WS_WIN)
+ QGLWindowSurface::surfaceFormat.setDoubleBuffer(false);
+
+ qt_win_owndc_required = true;
+#endif
+}
+
+//
+// QGLWindowSurface
+//
+
+class QGLGlobalShareWidget
+{
+public:
+ QGLGlobalShareWidget() : widget(0) {}
+
+ QGLWidget *shareWidget() {
+ if (!widget && !cleanedUp) {
+ widget = new QGLWidget;
+ }
+ return widget;
+ }
+
+ void cleanup() {
+ delete widget;
+ widget = 0;
+ cleanedUp = true;
+ }
+
+ static bool cleanedUp;
+
+private:
+ QGLWidget *widget;
+};
+
+bool QGLGlobalShareWidget::cleanedUp = false;
+
+static void qt_cleanup_gl_share_widget();
+Q_GLOBAL_STATIC_WITH_INITIALIZER(QGLGlobalShareWidget, _qt_gl_share_widget,
+ {
+ qAddPostRoutine(qt_cleanup_gl_share_widget);
+ })
+
+static void qt_cleanup_gl_share_widget()
+{
+ _qt_gl_share_widget()->cleanup();
+}
+
+QGLWidget* qt_gl_share_widget()
+{
+ if (QGLGlobalShareWidget::cleanedUp)
+ return 0;
+ return _qt_gl_share_widget()->shareWidget();
+}
+
+struct QGLWindowSurfacePrivate
+{
+ QGLFramebufferObject *fbo;
+ QGLPixelBuffer *pb;
+ GLuint tex_id;
+ GLuint pb_tex_id;
+
+ int tried_fbo : 1;
+ int tried_pb : 1;
+
+ QGLContext *ctx;
+
+ QList<QGLContext **> contexts;
+
+ QRegion paintedRegion;
+ QSize size;
+
+ QList<QImage> buffers;
+};
+
+QGLFormat QGLWindowSurface::surfaceFormat;
+
+QGLWindowSurface::QGLWindowSurface(QWidget *window)
+ : QWindowSurface(window), d_ptr(new QGLWindowSurfacePrivate)
+{
+ Q_ASSERT(window->isTopLevel());
+ QGLExtensions::init();
+ d_ptr->pb = 0;
+ d_ptr->fbo = 0;
+ d_ptr->ctx = 0;
+ d_ptr->tried_fbo = false;
+ d_ptr->tried_pb = false;
+}
+
+QGLWindowSurface::~QGLWindowSurface()
+{
+ if (d_ptr->ctx)
+ glDeleteTextures(1, &d_ptr->tex_id);
+ foreach(QGLContext **ctx, d_ptr->contexts) {
+ delete *ctx;
+ *ctx = 0;
+ }
+
+ delete d_ptr->pb;
+ delete d_ptr->fbo;
+ delete d_ptr;
+}
+
+void QGLWindowSurface::hijackWindow(QWidget *widget)
+{
+ QWidgetPrivate *widgetPrivate = widget->d_func();
+ widgetPrivate->createExtra();
+ if (widgetPrivate->extraData()->glContext)
+ return;
+
+ QGLContext *ctx = new QGLContext(surfaceFormat, widget);
+ ctx->create(qt_gl_share_widget()->context());
+#ifdef Q_WS_MAC
+ ctx->updatePaintDevice();
+#endif
+ widgetPrivate->extraData()->glContext = ctx;
+
+ union { QGLContext **ctxPtr; void **voidPtr; };
+
+ voidPtr = &widgetPrivate->extraData()->glContext;
+ d_ptr->contexts << ctxPtr;
+ qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size();
+}
+
+#if !defined(QT_OPENGL_ES_2)
+Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_window_surface_paintengine)
+#endif
+
+/*! \reimp */
+QPaintEngine *QGLWindowSurface::paintEngine() const
+{
+#if !defined(QT_OPENGL_ES_2)
+ return qt_gl_window_surface_paintengine();
+#else
+ return 0;
+#endif
+}
+
+int QGLWindowSurface::metric(PaintDeviceMetric m) const
+{
+ return window()->metric(m);
+}
+
+QGLContext *QGLWindowSurface::context() const
+{
+ return d_ptr->ctx;
+}
+
+QPaintDevice *QGLWindowSurface::paintDevice()
+{
+ if (d_ptr->pb)
+ return d_ptr->pb;
+
+ if (d_ptr->ctx)
+ return this;
+
+ QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
+ ctx->makeCurrent();
+ return d_ptr->fbo;
+}
+
+static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &src = QRectF());
+
+void QGLWindowSurface::beginPaint(const QRegion &)
+{
+}
+
+void QGLWindowSurface::endPaint(const QRegion &rgn)
+{
+ if (context())
+ d_ptr->paintedRegion |= rgn;
+
+ d_ptr->buffers.clear();
+}
+
+void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
+{
+ QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget();
+ Q_ASSERT(parent);
+
+ hijackWindow(parent);
+
+ QRect br = rgn.boundingRect().translated(offset);
+ br = br.intersected(window()->rect());
+ QPoint wOffset = qt_qwidget_data(parent)->wrect.topLeft();
+ QRect rect = br.translated(-offset - wOffset);
+
+ const GLenum target = qt_gl_preferredTextureTarget();
+
+ if (context()) {
+ context()->makeCurrent();
+
+ if (context()->format().doubleBuffer()) {
+ glBindTexture(target, d_ptr->tex_id);
+
+ QVector<QRect> rects = d_ptr->paintedRegion.rects();
+ for (int i = 0; i < rects.size(); ++i) {
+ QRect br = rects.at(i);
+ if (br.isEmpty())
+ continue;
+
+ const uint bottom = window()->height() - (br.y() + br.height());
+ glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
+ }
+
+ glBindTexture(target, 0);
+
+ QRegion dirtyRegion = QRegion(window()->rect()) - d_ptr->paintedRegion;
+
+ if (!dirtyRegion.isEmpty()) {
+ context()->makeCurrent();
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+#ifndef QT_OPENGL_ES
+ glOrtho(0, window()->width(), window()->height(), 0, -999999, 999999);
+#else
+ glOrthof(0, window()->width(), window()->height(), 0, -999999, 999999);
+#endif
+ glViewport(0, 0, window()->width(), window()->height());
+
+ QVector<QRect> rects = dirtyRegion.rects();
+ glColor4f(1, 1, 1, 1);
+ for (int i = 0; i < rects.size(); ++i) {
+ QRect rect = rects.at(i);
+ if (rect.isEmpty())
+ continue;
+
+ drawTexture(rect, d_ptr->tex_id, window()->size(), rect);
+ }
+ }
+ d_ptr->paintedRegion = QRegion();
+
+ context()->swapBuffers();
+ } else {
+ glFlush();
+ }
+
+ return;
+ }
+
+ QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext);
+ GLuint texture;
+ if (d_ptr->fbo) {
+ texture = d_ptr->fbo->texture();
+ } else {
+ d_ptr->pb->makeCurrent();
+ glBindTexture(target, d_ptr->pb_tex_id);
+ const uint bottom = window()->height() - (br.y() + br.height());
+ glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
+ texture = d_ptr->pb_tex_id;
+ glBindTexture(target, 0);
+ }
+
+ QSize size = widget->rect().size();
+ if (ctx->format().doubleBuffer()) {
+ rect = parent->rect();
+ br = rect.translated(wOffset);
+ size = parent->size();
+ }
+
+ ctx->makeCurrent();
+#ifdef Q_WS_MAC
+ ctx->updatePaintDevice();
+#endif
+ if (d_ptr->fbo)
+ d_ptr->fbo->release();
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+#ifndef QT_OPENGL_ES
+ glOrtho(0, size.width(), size.height(), 0, -999999, 999999);
+#else
+ glOrthof(0, size.width(), size.height(), 0, -999999, 999999);
+#endif
+ glViewport(0, 0, size.width(), size.height());
+
+ glColor4f(1, 1, 1, 1);
+ drawTexture(rect, texture, window()->size(), br);
+
+ if (ctx->format().doubleBuffer())
+ ctx->swapBuffers();
+ else
+ glFlush();
+
+ if (d_ptr->fbo)
+ d_ptr->fbo->bind();
+}
+
+void QGLWindowSurface::setGeometry(const QRect &rect)
+{
+ QWindowSurface::setGeometry(rect);
+
+ const GLenum target = qt_gl_preferredTextureTarget();
+
+ if (rect.width() <= 0 || rect.height() <= 0)
+ return;
+
+ if (d_ptr->size == rect.size())
+ return;
+
+ d_ptr->size = rect.size();
+
+ if (d_ptr->ctx) {
+ glBindTexture(target, d_ptr->tex_id);
+ glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
+ glBindTexture(target, 0);
+ return;
+ }
+
+ if (d_ptr->pb || !d_ptr->tried_pb) {
+ d_ptr->tried_pb = true;
+
+ if (d_ptr->pb) {
+ d_ptr->pb->makeCurrent();
+ glDeleteTextures(1, &d_ptr->pb_tex_id);
+ }
+
+ delete d_ptr->pb;
+
+ d_ptr->pb = new QGLPixelBuffer(rect.width(), rect.height(),
+ QGLFormat(QGL::SampleBuffers | QGL::StencilBuffer | QGL::DepthBuffer),
+ qt_gl_share_widget());
+
+ if (d_ptr->pb->isValid()) {
+ qDebug() << "PB Sample buffers:" << d_ptr->pb->format().sampleBuffers();
+ d_ptr->pb->makeCurrent();
+
+ glGenTextures(1, &d_ptr->pb_tex_id);
+ glBindTexture(target, d_ptr->pb_tex_id);
+ glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+
+ glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glBindTexture(target, 0);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+#ifndef QT_OPENGL_ES
+ glOrtho(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999);
+#else
+ glOrthof(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999);
+#endif
+
+ d_ptr->pb->d_ptr->qctx->d_func()->internal_context = true;
+ return;
+ } else {
+ qDebug() << "QGLWindowSurface: Failed to create valid pixelbuffer, falling back";
+ delete d_ptr->pb;
+ d_ptr->pb = 0;
+ }
+ }
+
+ if ((QGLExtensions::glExtensions & QGLExtensions::FramebufferObject) && (d_ptr->fbo || !d_ptr->tried_fbo)) {
+ d_ptr->tried_fbo = true;
+ hijackWindow(window());
+ QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
+ ctx->d_ptr->internal_context = true;
+ ctx->makeCurrent();
+ delete d_ptr->fbo;
+ d_ptr->fbo = new QGLFramebufferObject(rect.size(), QGLFramebufferObject::CombinedDepthStencil,
+ GLenum(target), GLenum(GL_RGBA));
+
+ d_ptr->fbo->bind();
+ if (d_ptr->fbo->isValid()) {
+ return;
+ } else {
+ qDebug() << "QGLWindowSurface: Failed to create valid FBO, falling back";
+ delete d_ptr->fbo;
+ d_ptr->fbo = 0;
+ }
+ }
+
+ hijackWindow(window());
+ QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
+ ctx->makeCurrent();
+
+ glGenTextures(1, &d_ptr->tex_id);
+ glBindTexture(target, d_ptr->tex_id);
+ glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
+
+ glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glBindTexture(target, 0);
+
+ qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;;
+ d_ptr->ctx = ctx;
+ d_ptr->ctx->d_ptr->internal_context = true;
+}
+
+bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ // this code randomly fails currently for unknown reasons
+ return false;
+
+ if (!d_ptr->pb)
+ return false;
+
+ d_ptr->pb->makeCurrent();
+
+ QRect br = area.boundingRect();
+
+#if 0
+ // ## workaround driver issue (scrolling by these deltas is unbearably slow for some reason)
+ // ## maybe we should use glCopyTexSubImage insteadk
+ if (dx == 1 || dx == -1 || dy == 1 || dy == -1 || dy == 2)
+ return false;
+
+ glRasterPos2i(br.x() + dx, br.y() + br.height() + dy);
+ glCopyPixels(br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), GL_COLOR);
+ return true;
+#endif
+
+ const GLenum target = qt_gl_preferredTextureTarget();
+
+ glBindTexture(target, d_ptr->tex_id);
+ glCopyTexImage2D(target, 0, GL_RGBA, br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), 0);
+ glBindTexture(target, 0);
+
+ drawTexture(br.translated(dx, dy), d_ptr->tex_id, window()->size());
+
+ return true;
+}
+
+static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &br)
+{
+ const GLenum target = qt_gl_preferredTextureTarget();
+ QRectF src = br.isEmpty()
+ ? QRectF(QPointF(), texSize)
+ : QRectF(QPointF(br.x(), texSize.height() - br.bottom()), br.size());
+
+ if (target == GL_TEXTURE_2D) {
+ qreal width = texSize.width();
+ qreal height = texSize.height();
+
+ src.setLeft(src.left() / width);
+ src.setRight(src.right() / width);
+ src.setTop(src.top() / height);
+ src.setBottom(src.bottom() / height);
+ }
+
+ const q_vertexType tx1 = f2vt(src.left());
+ const q_vertexType tx2 = f2vt(src.right());
+ const q_vertexType ty1 = f2vt(src.top());
+ const q_vertexType ty2 = f2vt(src.bottom());
+
+ q_vertexType texCoordArray[4*2] = {
+ tx1, ty2, tx2, ty2, tx2, ty1, tx1, ty1
+ };
+
+ q_vertexType vertexArray[4*2];
+ extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array); // qpaintengine_opengl.cpp
+ qt_add_rect_to_array(rect, vertexArray);
+
+ glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
+
+ glBindTexture(target, tex_id);
+ glEnable(target);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ glDisable(target);
+ glBindTexture(target, 0);
+}
+
+QImage *QGLWindowSurface::buffer(const QWidget *widget)
+{
+ QImage image;
+
+ if (d_ptr->pb)
+ image = d_ptr->pb->toImage();
+ else if (d_ptr->fbo)
+ image = d_ptr->fbo->toImage();
+
+ if (image.isNull())
+ return 0;
+
+ QRect rect = widget->rect();
+ rect.translate(widget->mapTo(widget->window(), QPoint()));
+
+ QImage subImage = image.copy(rect);
+ d_ptr->buffers << subImage;
+ return &d_ptr->buffers.last();
+}
+
+
+
+QT_END_NAMESPACE
+
diff --git a/src/opengl/qwindowsurface_gl_p.h b/src/opengl/qwindowsurface_gl_p.h
new file mode 100644
index 0000000000..01943787bd
--- /dev/null
+++ b/src/opengl/qwindowsurface_gl_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** 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 QWINDOWSURFACE_GL_P_H
+#define QWINDOWSURFACE_GL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include <qgl.h>
+#include <private/qwindowsurface_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+class QPoint;
+class QRegion;
+class QWidget;
+struct QGLWindowSurfacePrivate;
+
+class QGLWindowSurface : public QWindowSurface, public QPaintDevice
+{
+public:
+ QGLWindowSurface(QWidget *window);
+ ~QGLWindowSurface();
+
+ QPaintDevice *paintDevice();
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+ void setGeometry(const QRect &rect);
+ bool scroll(const QRegion &area, int dx, int dy);
+
+ void beginPaint(const QRegion &region);
+ void endPaint(const QRegion &region);
+
+ QImage *buffer(const QWidget *widget);
+
+ QGLContext *context() const;
+
+ static QGLFormat surfaceFormat;
+
+ QPaintEngine *paintEngine() const;
+
+protected:
+ int metric(PaintDeviceMetric metric) const;
+
+private:
+ void hijackWindow(QWidget *widget);
+
+ QGLWindowSurfacePrivate *d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_GL_P_H
+
diff --git a/src/opengl/util/README-GLSL b/src/opengl/util/README-GLSL
new file mode 100644
index 0000000000..ff20eb45b2
--- /dev/null
+++ b/src/opengl/util/README-GLSL
@@ -0,0 +1,18 @@
+Use of GLSL for vertex and fragment programs in Qt
+---------------------------------------------------
+
+We don't compile the *.glsl files because we don't want the build process of
+Qt to require cgc from nVidia to build the fragment programs.
+
+The script src/opengl/util/glsl_to_include.sh will compile a GLSL program to a file
+that can be included in a C(++) program. The file is the output from cgc
+quoted as a string.
+
+This can be done manually by:
+
+./glsl_to_include.sh radial.glsl
+./glsl_to_include.sh conical.glsl
+
+This will produce the files radial.frag and radial.glsl_quoted.
+(and also conical.frag and conical.glsl_quoted)
+These files are included by qpaintengine_opengl.cpp
diff --git a/src/opengl/util/brush_painter.glsl b/src/opengl/util/brush_painter.glsl
new file mode 100644
index 0000000000..6c4acdf924
--- /dev/null
+++ b/src/opengl/util/brush_painter.glsl
@@ -0,0 +1,7 @@
+// fast brush painter for composition modes which can be implemented with blendfuncs
+// no mask, used for fast filling of aliased primitives (or multisampled)
+
+void main()
+{
+ gl_FragColor = brush();
+}
diff --git a/src/opengl/util/brushes.conf b/src/opengl/util/brushes.conf
new file mode 100644
index 0000000000..93e2b3b295
--- /dev/null
+++ b/src/opengl/util/brushes.conf
@@ -0,0 +1,6 @@
+FRAGMENT_PROGRAM_BRUSH_SOLID solid_brush.glsl
+FRAGMENT_PROGRAM_BRUSH_RADIAL radial_brush.glsl
+FRAGMENT_PROGRAM_BRUSH_CONICAL conical_brush.glsl
+FRAGMENT_PROGRAM_BRUSH_LINEAR linear_brush.glsl
+FRAGMENT_PROGRAM_BRUSH_TEXTURE texture_brush.glsl
+FRAGMENT_PROGRAM_BRUSH_PATTERN pattern_brush.glsl
diff --git a/src/opengl/util/composition_mode_colorburn.glsl b/src/opengl/util/composition_mode_colorburn.glsl
new file mode 100644
index 0000000000..a5a153f892
--- /dev/null
+++ b/src/opengl/util/composition_mode_colorburn.glsl
@@ -0,0 +1,13 @@
+// Dca' = Sca.Da + Dca.Sa <= Sa.Da ?
+// Sca.(1 - Da) + Dca.(1 - Sa)
+// Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = mix(src.rgb * (1 - dst.a) + dst.rgb * (1 - src.a),
+ src.a * (src.rgb * dst.a + dst.rgb * src.a - src.a * dst.a) / max(src.rgb, 0.00001) + src.rgb * (1 - dst.a) + dst.rgb * (1 - src.a),
+ step(src.a * dst.a, src.rgb * dst.a + dst.rgb * src.a));
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_colordodge.glsl b/src/opengl/util/composition_mode_colordodge.glsl
new file mode 100644
index 0000000000..c19444169b
--- /dev/null
+++ b/src/opengl/util/composition_mode_colordodge.glsl
@@ -0,0 +1,15 @@
+// Dca' = Sca.Da + Dca.Sa <= Sa.Da ?
+// Dca.Sa/(1 - Sca/Sa) + Sca.(1 - Da) + Dca.(1 - Sa) :
+// Sa.Da + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ vec3 temp = src.rgb * (1 - dst.a) + dst.rgb * (1 - src.a);
+ result.rgb = mix(dst.rgb * src.a / max(1 - src.rgb / max(src.a, 0.000001), 0.000001) + temp,
+ src.a * dst.a + temp,
+ step(src.a * dst.a, src.rgb * dst.a + dst.rgb * src.a));
+
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_darken.glsl b/src/opengl/util/composition_mode_darken.glsl
new file mode 100644
index 0000000000..c1e83fd9e7
--- /dev/null
+++ b/src/opengl/util/composition_mode_darken.glsl
@@ -0,0 +1,9 @@
+// Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = min(src.rgb * dst.a, dst.rgb * src.a) + src.rgb * (1 - dst.a) + dst.rgb * (1 - src.a);
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_difference.glsl b/src/opengl/util/composition_mode_difference.glsl
new file mode 100644
index 0000000000..ca13ce70a0
--- /dev/null
+++ b/src/opengl/util/composition_mode_difference.glsl
@@ -0,0 +1,9 @@
+// Dca' = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = src.rgb + dst.rgb - 2 * min(src.rgb * dst.a, dst.rgb * src.a);
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_exclusion.glsl b/src/opengl/util/composition_mode_exclusion.glsl
new file mode 100644
index 0000000000..ccd1183096
--- /dev/null
+++ b/src/opengl/util/composition_mode_exclusion.glsl
@@ -0,0 +1,9 @@
+// Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = (src.rgb * dst.a + dst.rgb * src.a - 2 * src.rgb * dst.rgb) + src.rgb * (1 - dst.a) + dst.rgb * (1 - src.a);
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_hardlight.glsl b/src/opengl/util/composition_mode_hardlight.glsl
new file mode 100644
index 0000000000..9dd4de3cd9
--- /dev/null
+++ b/src/opengl/util/composition_mode_hardlight.glsl
@@ -0,0 +1,14 @@
+// Dca' = 2.Sca < Sa ?
+// 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) :
+// Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = mix(2 * src.rgb * dst.rgb + src.rgb * (1 - dst.a) + dst.rgb * (1 - src.a),
+ src.a * dst.a - 2 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1 - dst.a) + dst.rgb * (1 - src.a),
+ step(src.a, 2 * src.rgb));
+ result.a = src.a + dst.a - src.a * dst.a;
+
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_lighten.glsl b/src/opengl/util/composition_mode_lighten.glsl
new file mode 100644
index 0000000000..1fbd27a045
--- /dev/null
+++ b/src/opengl/util/composition_mode_lighten.glsl
@@ -0,0 +1,9 @@
+// Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = max(src.rgb * dst.a, dst.rgb * src.a) + src.rgb * (1 - dst.a) + dst.rgb * (1 - src.a);
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_multiply.glsl b/src/opengl/util/composition_mode_multiply.glsl
new file mode 100644
index 0000000000..268345a87a
--- /dev/null
+++ b/src/opengl/util/composition_mode_multiply.glsl
@@ -0,0 +1,9 @@
+// Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = src.rgb * dst.rgb + src.rgb * (1 - dst.a) + dst.rgb * (1 - src.a);
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_overlay.glsl b/src/opengl/util/composition_mode_overlay.glsl
new file mode 100644
index 0000000000..a9b72267a1
--- /dev/null
+++ b/src/opengl/util/composition_mode_overlay.glsl
@@ -0,0 +1,13 @@
+// Dca' = 2.Dca < Da ?
+// 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+// Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa)
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ result.rgb = mix(2 * src.rgb * dst.rgb + src.rgb * (1 - dst.a) + dst.rgb * (1 - src.a),
+ src.a * dst.a - 2 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1 - dst.a) + dst.rgb * (1 - src.a),
+ step(dst.a, 2 * dst.rgb));
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_mode_screen.glsl b/src/opengl/util/composition_mode_screen.glsl
new file mode 100644
index 0000000000..8f4f010032
--- /dev/null
+++ b/src/opengl/util/composition_mode_screen.glsl
@@ -0,0 +1,6 @@
+// Dca' = Sca + Dca - Sca.Dca
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ return src + dst - src * dst;
+}
diff --git a/src/opengl/util/composition_mode_softlight.glsl b/src/opengl/util/composition_mode_softlight.glsl
new file mode 100644
index 0000000000..0237827ca1
--- /dev/null
+++ b/src/opengl/util/composition_mode_softlight.glsl
@@ -0,0 +1,18 @@
+// Dca' = 2.Sca < Sa ?
+// Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa)) + Sca.(1 - Da) + Dca.(1 - Sa) :
+// (8.Dca <= Da ?
+// Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa).(3 - 8.Dca/Da)) + Sca.(1 - Da) + Dca.(1 - Sa) :
+// (Dca.Sa + ((Dca/Da)^(0.5).Da - Dca).(2.Sca - Sa)) + Sca.(1 - Da) + Dca.(1 - Sa))
+// Da' = Sa + Da - Sa.Da
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+ float da = max(dst.a, 0.00001);
+ result.rgb = mix(dst.rgb * (src.a - (1 - dst.rgb / da) * (2 * src.rgb - src.a)),
+ mix(dst.rgb * (src.a - (1 - dst.rgb / da) * (2 * src.rgb - src.a) * (3 - 8 * dst.rgb / da)),
+ (dst.rgb * src.a + (sqrt(dst.rgb / da) * dst.a - dst.rgb) * (2 * src.rgb - src.a)),
+ step(dst.a, 8 * dst.rgb)),
+ step(src.a, 2 * src.rgb)) + src.rgb * (1 - dst.a) + dst.rgb * (1 - src.a);
+ result.a = src.a + dst.a - src.a * dst.a;
+ return result;
+}
diff --git a/src/opengl/util/composition_modes.conf b/src/opengl/util/composition_modes.conf
new file mode 100644
index 0000000000..df52830b99
--- /dev/null
+++ b/src/opengl/util/composition_modes.conf
@@ -0,0 +1,12 @@
+COMPOSITION_MODES_SIMPLE_PORTER_DUFF simple_porter_duff.glsl
+COMPOSITION_MODES_MULTIPLY composition_mode_multiply.glsl
+COMPOSITION_MODES_SCREEN composition_mode_screen.glsl
+COMPOSITION_MODES_OVERLAY composition_mode_overlay.glsl
+COMPOSITION_MODES_DARKEN composition_mode_darken.glsl
+COMPOSITION_MODES_LIGHTEN composition_mode_lighten.glsl
+COMPOSITION_MODES_COLORDODGE composition_mode_colordodge.glsl
+COMPOSITION_MODES_COLORBURN composition_mode_colorburn.glsl
+COMPOSITION_MODES_HARDLIGHT composition_mode_hardlight.glsl
+COMPOSITION_MODES_SOFTLIGHT composition_mode_softlight.glsl
+COMPOSITION_MODES_DIFFERENCE composition_mode_difference.glsl
+COMPOSITION_MODES_EXCLUSION composition_mode_exclusion.glsl
diff --git a/src/opengl/util/conical_brush.glsl b/src/opengl/util/conical_brush.glsl
new file mode 100644
index 0000000000..83ee2f5b69
--- /dev/null
+++ b/src/opengl/util/conical_brush.glsl
@@ -0,0 +1,27 @@
+// conical gradient shader
+#define M_PI 3.14159265358979323846
+uniform sampler1D palette;
+uniform float angle;
+uniform vec3 inv_matrix_m0;
+uniform vec3 inv_matrix_m1;
+uniform vec3 inv_matrix_m2;
+
+vec4 brush()
+{
+ mat3 mat;
+
+ mat[0] = inv_matrix_m0;
+ mat[1] = inv_matrix_m1;
+ mat[2] = inv_matrix_m2;
+
+ vec3 hcoords = mat * vec3(gl_FragCoord.xy, 1);
+ vec2 A = hcoords.xy / hcoords.z;
+
+/* float val = fmod((atan2(-A.y, A.x) + angle) / (2.0 * M_PI), 1); */
+ if (abs(A.y) == abs(A.x))
+ A.y += 0.002;
+ float t = (atan2(-A.y, A.x) + angle) / (2.0 * M_PI);
+ float val = t - floor(t);
+ return texture1D(palette, val);
+}
+
diff --git a/src/opengl/util/ellipse.glsl b/src/opengl/util/ellipse.glsl
new file mode 100644
index 0000000000..860ae77e68
--- /dev/null
+++ b/src/opengl/util/ellipse.glsl
@@ -0,0 +1,6 @@
+#include "ellipse_functions.glsl"
+
+void main()
+{
+ gl_FragColor = ellipse();
+}
diff --git a/src/opengl/util/ellipse_aa.glsl b/src/opengl/util/ellipse_aa.glsl
new file mode 100644
index 0000000000..f7a6454ae5
--- /dev/null
+++ b/src/opengl/util/ellipse_aa.glsl
@@ -0,0 +1,6 @@
+#include "ellipse_functions.glsl"
+
+void main()
+{
+ gl_FragColor = ellipse_aa();
+}
diff --git a/src/opengl/util/ellipse_aa_copy.glsl b/src/opengl/util/ellipse_aa_copy.glsl
new file mode 100644
index 0000000000..5372f585ea
--- /dev/null
+++ b/src/opengl/util/ellipse_aa_copy.glsl
@@ -0,0 +1,11 @@
+uniform vec2 r; // r_x and r_y
+
+uniform sampler2D texture;
+uniform vec2 inv_texture_size;
+
+#include "ellipse_functions.glsl"
+
+void main()
+{
+ gl_FragColor = ellipse_aa() * texture2D(texture, gl_FragCoord.xy * inv_texture_size);
+}
diff --git a/src/opengl/util/ellipse_aa_radial.glsl b/src/opengl/util/ellipse_aa_radial.glsl
new file mode 100644
index 0000000000..0878f9997e
--- /dev/null
+++ b/src/opengl/util/ellipse_aa_radial.glsl
@@ -0,0 +1,24 @@
+#include "ellipse_functions.glsl"
+
+uniform sampler1D palette;
+uniform vec2 fmp;
+uniform float fmp2_m_radius2;
+uniform vec4 inv_matrix;
+uniform vec2 inv_matrix_offset;
+
+void main()
+{
+ // float2 A = frag_coord.xy;//mul(inv_matrix, frag_coord.xy) + inv_matrix_offset;
+ mat2 mat;
+ mat[0][0] = inv_matrix.x;
+ mat[0][1] = inv_matrix.y;
+ mat[1][0] = inv_matrix.z;
+ mat[1][1] = inv_matrix.w;
+ vec2 A = gl_FragCoord.xy * mat + inv_matrix_offset;
+ vec2 B = fmp;
+ float a = fmp2_m_radius2;
+ float b = 2.0*dot(A, B);
+ float c = -dot(A, A);
+ float val = (-b + sqrt(b*b - 4.0*a*c)) / (2.0*a);
+ gl_FragColor = texture1D(palette, val) * ellipse_aa();
+}
diff --git a/src/opengl/util/ellipse_functions.glsl b/src/opengl/util/ellipse_functions.glsl
new file mode 100644
index 0000000000..eed18e8580
--- /dev/null
+++ b/src/opengl/util/ellipse_functions.glsl
@@ -0,0 +1,63 @@
+uniform vec3 inv_matrix_m0;
+uniform vec3 inv_matrix_m1;
+uniform vec3 inv_matrix_m2;
+
+uniform vec2 ellipse_offset;
+
+float ellipse()
+{
+ vec2 st = gl_TexCoord[0].st;
+
+ if (dot(st, st) > 1)
+ discard;
+
+ return 1.0;
+}
+
+// ellipse equation
+
+// s^2/a^2 + t^2/b^2 = 1
+//
+// implicit equation:
+// g(s,t) = 1 - s^2/r_s^2 - t^2/r_t^2
+
+// distance from ellipse:
+// grad = [dg/dx dg/dy]
+// d(s, t) ~= g(s, t) / |grad|
+
+// dg/dx = dg/ds * ds/dx + dg/dt * dt/dx
+// dg/dy = dg/ds * ds/dy + dg/dt * dt/dy
+
+float ellipse_aa()
+{
+ mat3 mat;
+
+ mat[0] = inv_matrix_m0;
+ mat[1] = inv_matrix_m1;
+ mat[2] = inv_matrix_m2;
+
+ vec3 hcoords = mat * vec3(gl_FragCoord.xy + ellipse_offset, 1);
+ float inv_w = 1.0 / hcoords.z;
+ vec2 st = hcoords.xy * inv_w;
+
+ vec4 xy = vec4(mat[0].xy, mat[1].xy);
+ vec2 h = vec2(mat[0].z, mat[1].z);
+
+ vec4 dstdxy = (xy.xzyw - h.xyxy * st.xxyy) * inv_w;
+
+ //dstdxy.x = (mat[0].x - mat[0].z * st.x) * inv_w; // ds/dx
+ //dstdxy.y = (mat[1].x - mat[1].z * st.x) * inv_w; // ds/dy
+ //dstdxy.z = (mat[0].y - mat[0].z * st.y) * inv_w; // dt/dx
+ //dstdxy.w = (mat[1].y - mat[1].z * st.y) * inv_w; // dt/dy
+
+ vec2 inv_r = gl_TexCoord[0].xy;
+ vec2 n = st * inv_r;
+ float g = 1.0 - dot(n, n);
+
+ vec2 dgdst = -2.0 * n * inv_r;
+
+ vec2 grad = vec2(dot(dgdst, dstdxy.xz),
+ dot(dgdst, dstdxy.yw));
+
+ return smoothstep(-0.5, 0.5, g * inversesqrt(dot(grad, grad)));
+}
diff --git a/src/opengl/util/fast_painter.glsl b/src/opengl/util/fast_painter.glsl
new file mode 100644
index 0000000000..63f5e5f3be
--- /dev/null
+++ b/src/opengl/util/fast_painter.glsl
@@ -0,0 +1,19 @@
+// fast painter for composition modes which can be implemented with blendfuncs
+
+uniform sampler2D mask_texture;
+uniform vec2 inv_mask_size;
+uniform vec2 mask_offset;
+uniform vec4 mask_channel;
+
+float mask()
+{
+ return dot(mask_channel, texture2D(mask_texture, (gl_FragCoord.xy + mask_offset) * inv_mask_size));
+}
+
+void main()
+{
+ // combine clip and coverage channels
+ float mask_alpha = mask();
+
+ gl_FragColor = brush() * mask_alpha;
+}
diff --git a/src/opengl/util/fragmentprograms_p.h b/src/opengl/util/fragmentprograms_p.h
new file mode 100644
index 0000000000..ecf0bf8bab
--- /dev/null
+++ b/src/opengl/util/fragmentprograms_p.h
@@ -0,0 +1,7372 @@
+/****************************************************************************
+**
+** 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 FRAGMENTPROGRAMS_H
+#define FRAGMENTPROGRAMS_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+enum FragmentVariable {
+ VAR_BRUSH_TEXTURE,
+ VAR_LINEAR,
+ VAR_INV_MATRIX_M1,
+ VAR_INV_MASK_SIZE,
+ VAR_INV_MATRIX_M2,
+ VAR_PORTERDUFF_AB,
+ VAR_MASK_CHANNEL,
+ VAR_ELLIPSE_OFFSET,
+ VAR_PORTERDUFF_XYZ,
+ VAR_INV_DST_SIZE,
+ VAR_MASK_TEXTURE,
+ VAR_DST_TEXTURE,
+ VAR_PALETTE,
+ VAR_MASK_OFFSET,
+ VAR_INV_BRUSH_TEXTURE_SIZE,
+ VAR_FMP2_M_RADIUS2,
+ VAR_FMP,
+ VAR_INV_MATRIX_M0,
+ VAR_ANGLE,
+};
+
+enum FragmentBrushType {
+ FRAGMENT_PROGRAM_BRUSH_SOLID,
+ FRAGMENT_PROGRAM_BRUSH_RADIAL,
+ FRAGMENT_PROGRAM_BRUSH_CONICAL,
+ FRAGMENT_PROGRAM_BRUSH_LINEAR,
+ FRAGMENT_PROGRAM_BRUSH_TEXTURE,
+ FRAGMENT_PROGRAM_BRUSH_PATTERN,
+};
+
+enum FragmentCompositionModeType {
+ COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ COMPOSITION_MODES_MULTIPLY,
+ COMPOSITION_MODES_SCREEN,
+ COMPOSITION_MODES_OVERLAY,
+ COMPOSITION_MODES_DARKEN,
+ COMPOSITION_MODES_LIGHTEN,
+ COMPOSITION_MODES_COLORDODGE,
+ COMPOSITION_MODES_COLORBURN,
+ COMPOSITION_MODES_HARDLIGHT,
+ COMPOSITION_MODES_SOFTLIGHT,
+ COMPOSITION_MODES_DIFFERENCE,
+ COMPOSITION_MODES_EXCLUSION,
+ COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ COMPOSITION_MODES_MULTIPLY_NOMASK,
+ COMPOSITION_MODES_SCREEN_NOMASK,
+ COMPOSITION_MODES_OVERLAY_NOMASK,
+ COMPOSITION_MODES_DARKEN_NOMASK,
+ COMPOSITION_MODES_LIGHTEN_NOMASK,
+ COMPOSITION_MODES_COLORDODGE_NOMASK,
+ COMPOSITION_MODES_COLORBURN_NOMASK,
+ COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ COMPOSITION_MODES_EXCLUSION_NOMASK,
+ COMPOSITION_MODE_BLEND_MODE_MASK,
+ COMPOSITION_MODE_BLEND_MODE_NOMASK,
+};
+
+enum FragmentMaskType {
+ FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA,
+ FRAGMENT_PROGRAM_MASK_ELLIPSE_AA,
+};
+
+static const unsigned int num_fragment_variables = 19;
+
+static const unsigned int num_fragment_brushes = 6;
+static const unsigned int num_fragment_composition_modes = 26;
+static const unsigned int num_fragment_masks = 2;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA =
+ "!!ARBfp1.0\n"
+ "PARAM c[1] = { { 0.5, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "ADD R4.x, fragment.position, c[0];\n"
+ "ADD R0.y, fragment.position, -c[0].x;\n"
+ "MAX R2.x, R0.y, fragment.texcoord[0].y;\n"
+ "ADD R0.x, fragment.position.y, c[0];\n"
+ "MIN R2.y, R0.x, fragment.texcoord[0].x;\n"
+ "ADD R3.x, fragment.position, -c[0];\n"
+ "ADD R1.zw, -fragment.texcoord[0], -fragment.texcoord[0];\n"
+ "MOV R3.y, R4.x;\n"
+ "MOV R0.yw, R2.x;\n"
+ "MOV R0.xz, R2.y;\n"
+ "MAD R0, fragment.texcoord[1].xxzz, R0, fragment.texcoord[1].yyww;\n"
+ "MAD R1.xy, fragment.position.x, c[0].y, -R0.zwzw;\n"
+ "MOV R0.w, R1.x;\n"
+ "MOV R1.x, R0.y;\n"
+ "MOV R0.z, R0.x;\n"
+ "SGE R2.zw, R1.xyxy, R0;\n"
+ "MAX R0.xy, R0.zwzw, R1;\n"
+ "MIN R0.zw, R0, R1.xyxy;\n"
+ "MAD R2.zw, R2, R1, fragment.texcoord[0];\n"
+ "ADD R1, R3.xyxy, -R0.zzww;\n"
+ "MAD R1, R1, R2.zzww, R2.x;\n"
+ "ADD R3.zw, R0.xyxy, R0;\n"
+ "ADD R3.y, R2, -R2.x;\n"
+ "ADD R2.zw, R1.xyyw, -R2.x;\n"
+ "ADD R4.zw, R4.x, -R0;\n"
+ "MUL R2.zw, R4, R2;\n"
+ "ADD R4.zw, R1.xyyw, R1.xyxz;\n"
+ "ADD R1.xz, R2.y, -R1;\n"
+ "MAD R2.zw, -R2, c[0].x, R3.y;\n"
+ "MAD R3.zw, R3, c[0].x, -R3.x;\n"
+ "MAD R3.zw, R3, R3.y, -R2;\n"
+ "ADD R1.y, R4.x, -R3.x;\n"
+ "MAD R4.zw, -R4, c[0].x, R2.y;\n"
+ "MUL R4.zw, R4, R1.y;\n"
+ "ADD R1.yw, R0.xxzy, -R3.x;\n"
+ "MUL R1.xy, R1.xzzw, R1.ywzw;\n"
+ "MAD R1.zw, R1.xyxy, c[0].x, -R4;\n"
+ "SGE R1.xy, R4.x, R0;\n"
+ "MUL R1.zw, R1.xyxy, R1;\n"
+ "MAD R1.xy, R1, R3.zwzw, R2.zwzw;\n"
+ "SGE R2.zw, R3.x, R0;\n"
+ "ADD R1.zw, R4, R1;\n"
+ "ADD R1.zw, R1, -R1.xyxy;\n"
+ "MAD R1.xy, R2.zwzw, R1.zwzw, R1;\n"
+ "ADD R1.xy, R1, -R3.y;\n"
+ "SGE R0.zw, R4.x, R0;\n"
+ "MAD R0.zw, R0, R1.xyxy, R3.y;\n"
+ "SGE R0.xy, R0, R3.x;\n"
+ "MUL R0.xy, R0.zwzw, R0;\n"
+ "ADD R0.x, R3.y, -R0;\n"
+ "SGE R0.z, R2.y, R2.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "MUL result.color, R0.x, R0.z;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_MASK_ELLIPSE_AA =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..3],\n"
+ " { -2, 1, -0.5, 2 },\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R1.xyz, R0.y, c[2];\n"
+ "MAD R0.xyz, R0.x, c[1], R1;\n"
+ "ADD R0.xyz, R0, c[3];\n"
+ "RCP R2.z, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R2.z;\n"
+ "MUL R2.xy, R0.zwzw, fragment.texcoord[0];\n"
+ "MOV R1.xy, c[1];\n"
+ "MOV R1.zw, c[2].xyxy;\n"
+ "MOV R0.x, c[1].z;\n"
+ "MOV R0.y, c[2].z;\n"
+ "MAD R0, -R0.xyxy, R0.zzww, R1.xzyw;\n"
+ "MUL R1.xy, R2, fragment.texcoord[0];\n"
+ "MUL R0, R0, R2.z;\n"
+ "MUL R1.xy, R1, c[4].x;\n"
+ "MUL R1.zw, R1.xyxy, R0.xyxz;\n"
+ "MUL R0.xy, R1, R0.ywzw;\n"
+ "ADD R0.w, R0.x, R0.y;\n"
+ "MUL R0.xy, R2, R2;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "ADD R0.z, R1, R1.w;\n"
+ "MUL R0.zw, R0, R0;\n"
+ "ADD R0.y, R0.z, R0.w;\n"
+ "RSQ R0.y, R0.y;\n"
+ "ADD R0.x, -R0, c[4].y;\n"
+ "MAD_SAT R0.x, R0.y, R0, -c[4].z;\n"
+ "MUL R0.y, -R0.x, c[4].w;\n"
+ "ADD R0.y, R0, c[5].x;\n"
+ "MUL R0.x, R0, R0;\n"
+ "MUL result.color, R0.x, R0.y;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SIMPLE_PORTER_DUFF =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..3],\n"
+ " { 1 },\n"
+ " program.local[5..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "MUL R0.xyz, R1, c[6].y;\n"
+ "MUL R2.xyz, R0, fragment.color.primary.w;\n"
+ "MUL R0.xyz, fragment.color.primary, c[6].x;\n"
+ "MAD R2.xyz, R0, R1.w, R2;\n"
+ "ADD R3.xy, fragment.position, c[0];\n"
+ "ADD R0.w, -R1, c[4].x;\n"
+ "MUL R0.xyz, fragment.color.primary, c[5].y;\n"
+ "MAD R2.xyz, R0, R0.w, R2;\n"
+ "MUL R0.xyz, R1, c[5].z;\n"
+ "ADD R0.w, -fragment.color.primary, c[4].x;\n"
+ "MAD R2.xyz, R0, R0.w, R2;\n"
+ "ADD R0.y, -R1.w, c[4].x;\n"
+ "MUL R0.x, fragment.color.primary.w, R1.w;\n"
+ "MUL R0.y, fragment.color.primary.w, R0;\n"
+ "MUL R0.z, R1.w, R0.w;\n"
+ "DP3 R2.w, R0, c[5];\n"
+ "MUL R3.xy, R3, c[1];\n"
+ "TEX R0, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_MULTIPLY =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "ADD R0.x, -R1.w, c[4];\n"
+ "MUL R0.xyz, fragment.color.primary, R0.x;\n"
+ "MAD R0.xyz, fragment.color.primary, R1, R0;\n"
+ "ADD R0.w, -fragment.color.primary, c[4].x;\n"
+ "MAD R2.xyz, R1, R0.w, R0;\n"
+ "ADD R0.z, fragment.color.primary.w, R1.w;\n"
+ "MAD R2.w, -fragment.color.primary, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SCREEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[4] = { program.local[0..3] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "ADD R2, fragment.color.primary, R1;\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "MAD R2, -fragment.color.primary, R1, R2;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_OVERLAY =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "ADD R0.w, -R1, c[4].y;\n"
+ "MUL R3.xyz, fragment.color.primary, R0.w;\n"
+ "ADD R2.xyz, fragment.color.primary.w, -fragment.color.primary;\n"
+ "ADD R0.xyz, R1.w, -R1;\n"
+ "MUL R0.xyz, R0, R2;\n"
+ "MUL R0.xyz, R0, c[4].x;\n"
+ "MAD R0.xyz, fragment.color.primary.w, R1.w, -R0;\n"
+ "MAD R0.xyz, fragment.color.primary, R0.w, R0;\n"
+ "MUL R2.xyz, fragment.color.primary, R1;\n"
+ "MAD R2.xyz, R2, c[4].x, R3;\n"
+ "ADD R0.w, -fragment.color.primary, c[4].y;\n"
+ "MAD R3.xyz, R1, R0.w, R0;\n"
+ "MAD R2.xyz, R1, R0.w, R2;\n"
+ "MUL R0.xyz, R1, c[4].x;\n"
+ "SGE R0.xyz, R0, R1.w;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "MAD R2.xyz, R0, R3, R2;\n"
+ "ADD R0.z, fragment.color.primary.w, R1.w;\n"
+ "MAD R2.w, -fragment.color.primary, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DARKEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, fragment.color.primary.w;\n"
+ "MUL R0.xyz, fragment.color.primary, R1.w;\n"
+ "MIN R0.xyz, R0, R2;\n"
+ "ADD R0.w, -R1, c[4].x;\n"
+ "MAD R0.xyz, fragment.color.primary, R0.w, R0;\n"
+ "ADD R0.w, -fragment.color.primary, c[4].x;\n"
+ "MAD R2.xyz, R1, R0.w, R0;\n"
+ "ADD R0.z, fragment.color.primary.w, R1.w;\n"
+ "MAD R2.w, -fragment.color.primary, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_LIGHTEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, fragment.color.primary.w;\n"
+ "MUL R0.xyz, fragment.color.primary, R1.w;\n"
+ "MAX R0.xyz, R0, R2;\n"
+ "ADD R0.w, -R1, c[4].x;\n"
+ "MAD R0.xyz, fragment.color.primary, R0.w, R0;\n"
+ "ADD R0.w, -fragment.color.primary, c[4].x;\n"
+ "MAD R2.xyz, R1, R0.w, R0;\n"
+ "ADD R0.z, fragment.color.primary.w, R1.w;\n"
+ "MAD R2.w, -fragment.color.primary, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORDODGE =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 1, 1e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.y, -fragment.color.primary.w, c[4].x;\n"
+ "MAX R1.x, fragment.color.primary.w, c[4].y;\n"
+ "MUL R2.xyz, R0, R1.y;\n"
+ "ADD R1.w, -R0, c[4].x;\n"
+ "MAD R3.xyz, fragment.color.primary, R1.w, R2;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R1.xyz, -fragment.color.primary, R1.x, c[4].x;\n"
+ "MAX R1.xyz, R1, c[4].y;\n"
+ "MUL R2.xyz, R0, fragment.color.primary.w;\n"
+ "MUL R1.w, fragment.color.primary, R0;\n"
+ "RCP R1.x, R1.x;\n"
+ "RCP R1.y, R1.y;\n"
+ "RCP R1.z, R1.z;\n"
+ "MAD R1.xyz, R2, R1, R3;\n"
+ "MAD R3.xyz, fragment.color.primary.w, R0.w, R3;\n"
+ "MAD R2.xyz, fragment.color.primary, R0.w, R2;\n"
+ "ADD R3.xyz, R3, -R1;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "MAD R2.xyz, R2, R3, R1;\n"
+ "ADD R1.z, fragment.color.primary.w, R0.w;\n"
+ "MAD R2.w, -fragment.color.primary, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[0];\n"
+ "MUL R1.xy, R1, c[1];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[2];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORBURN =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 1, 9.9999997e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.w, -R0, c[4].x;\n"
+ "MUL R1.xyz, R0, fragment.color.primary.w;\n"
+ "MAD R2.xyz, fragment.color.primary, R0.w, R1;\n"
+ "MAD R1.xyz, -fragment.color.primary.w, R0.w, R2;\n"
+ "MUL R3.xyz, fragment.color.primary.w, R1;\n"
+ "MAX R1.xyz, fragment.color.primary, c[4].y;\n"
+ "MUL R4.xyz, fragment.color.primary, R1.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "RCP R1.y, R1.y;\n"
+ "RCP R1.z, R1.z;\n"
+ "MAD R3.xyz, R3, R1, R4;\n"
+ "ADD R2.w, -fragment.color.primary, c[4].x;\n"
+ "MUL R1.xyz, R0, R2.w;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R2.w, -fragment.color.primary, c[4].x;\n"
+ "MAD R3.xyz, R0, R2.w, R3;\n"
+ "MUL R1.w, fragment.color.primary, R0;\n"
+ "ADD R3.xyz, R3, -R1;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "MAD R2.xyz, R2, R3, R1;\n"
+ "ADD R1.z, fragment.color.primary.w, R0.w;\n"
+ "MAD R2.w, -fragment.color.primary, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[0];\n"
+ "MUL R1.xy, R1, c[1];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[2];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_HARDLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "ADD R0.w, -R1, c[4].y;\n"
+ "MUL R3.xyz, fragment.color.primary, R0.w;\n"
+ "ADD R2.xyz, fragment.color.primary.w, -fragment.color.primary;\n"
+ "ADD R0.xyz, R1.w, -R1;\n"
+ "MUL R0.xyz, R0, R2;\n"
+ "MUL R0.xyz, R0, c[4].x;\n"
+ "MAD R0.xyz, fragment.color.primary.w, R1.w, -R0;\n"
+ "MAD R0.xyz, fragment.color.primary, R0.w, R0;\n"
+ "MUL R2.xyz, fragment.color.primary, R1;\n"
+ "MAD R2.xyz, R2, c[4].x, R3;\n"
+ "ADD R0.w, -fragment.color.primary, c[4].y;\n"
+ "MAD R3.xyz, R1, R0.w, R0;\n"
+ "MAD R2.xyz, R1, R0.w, R2;\n"
+ "MUL R0.xyz, fragment.color.primary, c[4].x;\n"
+ "SGE R0.xyz, R0, fragment.color.primary.w;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "MAD R2.xyz, R0, R3, R2;\n"
+ "ADD R0.z, fragment.color.primary.w, R1.w;\n"
+ "MAD R2.w, -fragment.color.primary, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SOFTLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..3],\n"
+ " { 1, 9.9999997e-06, 2, 8 },\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MAX R1.x, R0.w, c[4].y;\n"
+ "RCP R1.w, R1.x;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R1.xyz, -R2, c[4].w;\n"
+ "RSQ R2.w, R2.x;\n"
+ "ADD R4.xyz, R1, c[5].x;\n"
+ "MAD R1.xyz, -R0, R1.w, c[4].x;\n"
+ "RSQ R2.z, R2.z;\n"
+ "RSQ R2.y, R2.y;\n"
+ "RCP R2.x, R2.w;\n"
+ "RCP R2.z, R2.z;\n"
+ "RCP R2.y, R2.y;\n"
+ "MAD R5.xyz, R2, R0.w, -R0;\n"
+ "MAD R2.xyz, fragment.color.primary, c[4].z, -fragment.color.primary.w;\n"
+ "MUL R3.xyz, R1, R2;\n"
+ "MAD R3.xyz, -R3, R4, fragment.color.primary.w;\n"
+ "MUL R4.xyz, R5, R2;\n"
+ "MAD R1.xyz, -R1, R2, fragment.color.primary.w;\n"
+ "MAD R5.xyz, R0, fragment.color.primary.w, R4;\n"
+ "MUL R3.xyz, R0, R3;\n"
+ "MUL R4.xyz, R0, c[4].w;\n"
+ "ADD R5.xyz, R5, -R3;\n"
+ "SGE R4.xyz, R4, R0.w;\n"
+ "MUL R4.xyz, R4, R5;\n"
+ "ADD R2.xyz, R3, R4;\n"
+ "MUL R1.xyz, R0, R1;\n"
+ "MUL R3.xyz, fragment.color.primary, c[4].z;\n"
+ "ADD R2.xyz, R2, -R1;\n"
+ "SGE R3.xyz, R3, fragment.color.primary.w;\n"
+ "MUL R2.xyz, R3, R2;\n"
+ "ADD R1.xyz, R1, R2;\n"
+ "ADD R1.w, -R0, c[4].x;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, -fragment.color.primary, c[4].x;\n"
+ "MAD R2.xyz, R0, R1.w, R1;\n"
+ "ADD R1.z, fragment.color.primary.w, R0.w;\n"
+ "MAD R2.w, -fragment.color.primary, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[0];\n"
+ "MUL R1.xy, R1, c[1];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[2];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DIFFERENCE =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, fragment.color.primary.w;\n"
+ "MUL R0.xyz, fragment.color.primary, R1.w;\n"
+ "MIN R0.xyz, R0, R2;\n"
+ "ADD R3.xyz, fragment.color.primary, R1;\n"
+ "MAD R2.xyz, -R0, c[4].x, R3;\n"
+ "ADD R0.z, fragment.color.primary.w, R1.w;\n"
+ "MAD R2.w, -fragment.color.primary, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_EXCLUSION =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..3],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[3];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "MUL R0.xyz, R1, fragment.color.primary.w;\n"
+ "MAD R2.xyz, fragment.color.primary, R1.w, R0;\n"
+ "MUL R0.xyz, fragment.color.primary, R1;\n"
+ "MAD R0.xyz, -R0, c[4].y, R2;\n"
+ "ADD R0.w, -R1, c[4].x;\n"
+ "MAD R0.xyz, fragment.color.primary, R0.w, R0;\n"
+ "ADD R0.w, -fragment.color.primary, c[4].x;\n"
+ "MAD R2.xyz, R1, R0.w, R0;\n"
+ "ADD R0.z, fragment.color.primary.w, R1.w;\n"
+ "MAD R2.w, -fragment.color.primary, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[4] = { program.local[0],\n"
+ " { 1 },\n"
+ " program.local[2..3] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.xyz, R0, c[3].y;\n"
+ "MUL R2.xyz, R1, fragment.color.primary.w;\n"
+ "MUL R1.xyz, fragment.color.primary, c[3].x;\n"
+ "MAD R2.xyz, R1, R0.w, R2;\n"
+ "MUL R0.xyz, R0, c[2].z;\n"
+ "ADD R1.w, -R0, c[1].x;\n"
+ "MUL R1.xyz, fragment.color.primary, c[2].y;\n"
+ "MAD R1.xyz, R1, R1.w, R2;\n"
+ "ADD R1.w, -fragment.color.primary, c[1].x;\n"
+ "MAD result.color.xyz, R0, R1.w, R1;\n"
+ "ADD R0.y, -R0.w, c[1].x;\n"
+ "MUL R0.x, fragment.color.primary.w, R0.w;\n"
+ "MUL R0.z, R0.w, R1.w;\n"
+ "MUL R0.y, fragment.color.primary.w, R0;\n"
+ "DP3 result.color.w, R0, c[2];\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_MULTIPLY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, -R0.w, c[1];\n"
+ "MUL R1.xyz, fragment.color.primary, R1.x;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "MAD R1.xyz, fragment.color.primary, R0, R1;\n"
+ "ADD R2.x, -fragment.color.primary.w, c[1];\n"
+ "MAD result.color.xyz, R0, R2.x, R1;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SCREEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[1] = { program.local[0] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1, fragment.color.primary, R0;\n"
+ "MAD result.color, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_OVERLAY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.w, -R0, c[1].y;\n"
+ "ADD R2.xyz, fragment.color.primary.w, -fragment.color.primary;\n"
+ "ADD R1.xyz, R0.w, -R0;\n"
+ "MUL R1.xyz, R1, R2;\n"
+ "MUL R1.xyz, R1, c[1].x;\n"
+ "MAD R1.xyz, fragment.color.primary.w, R0.w, -R1;\n"
+ "MUL R3.xyz, fragment.color.primary, R1.w;\n"
+ "MUL R2.xyz, fragment.color.primary, R0;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, -fragment.color.primary, c[1].y;\n"
+ "MAD R2.xyz, R2, c[1].x, R3;\n"
+ "MAD R2.xyz, R0, R1.w, R2;\n"
+ "MAD R1.xyz, R0, R1.w, R1;\n"
+ "MUL R0.xyz, R0, c[1].x;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "ADD R1.xyz, R1, -R2;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD result.color.xyz, R0, R1, R2;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DARKEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, R0, fragment.color.primary.w;\n"
+ "MUL R1.xyz, fragment.color.primary, R0.w;\n"
+ "MIN R1.xyz, R1, R2;\n"
+ "ADD R1.w, -R0, c[1].x;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "ADD R2.x, -fragment.color.primary.w, c[1];\n"
+ "MAD result.color.xyz, R0, R2.x, R1;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_LIGHTEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, R0, fragment.color.primary.w;\n"
+ "MUL R1.xyz, fragment.color.primary, R0.w;\n"
+ "MAX R1.xyz, R1, R2;\n"
+ "ADD R1.w, -R0, c[1].x;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "ADD R2.x, -fragment.color.primary.w, c[1];\n"
+ "MAD result.color.xyz, R0, R2.x, R1;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORDODGE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 1, 1e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MAX R1.y, fragment.color.primary.w, c[1];\n"
+ "RCP R2.x, R1.y;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, -fragment.color.primary.w, c[1];\n"
+ "MUL R1.xyz, R0, R1.x;\n"
+ "ADD R1.w, -R0, c[1].x;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "MAD R2.xyz, -fragment.color.primary, R2.x, c[1].x;\n"
+ "MAX R2.xyz, R2, c[1].y;\n"
+ "MUL R0.xyz, R0, fragment.color.primary.w;\n"
+ "MUL R1.w, fragment.color.primary, R0;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R0, R2, R1;\n"
+ "MAD R1.xyz, fragment.color.primary.w, R0.w, R1;\n"
+ "MAD R0.xyz, fragment.color.primary, R0.w, R0;\n"
+ "SGE R0.xyz, R0, R1.w;\n"
+ "ADD R1.xyz, R1, -R2;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "MAD result.color.xyz, R0, R1, R2;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORBURN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 1, 9.9999997e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.xyz, R0, fragment.color.primary.w;\n"
+ "MAD R2.xyz, fragment.color.primary, R0.w, R1;\n"
+ "MAD R1.xyz, -fragment.color.primary.w, R0.w, R2;\n"
+ "MUL R3.xyz, fragment.color.primary.w, R1;\n"
+ "MAX R1.xyz, fragment.color.primary, c[1].y;\n"
+ "ADD R1.w, -R0, c[1].x;\n"
+ "MUL R4.xyz, fragment.color.primary, R1.w;\n"
+ "ADD R2.w, -fragment.color.primary, c[1].x;\n"
+ "RCP R1.x, R1.x;\n"
+ "RCP R1.y, R1.y;\n"
+ "RCP R1.z, R1.z;\n"
+ "MAD R1.xyz, R3, R1, R4;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "ADD R2.w, -fragment.color.primary, c[1].x;\n"
+ "MAD R0.xyz, R0, R2.w, R1;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R3;\n"
+ "MUL R1.w, fragment.color.primary, R0;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "ADD R0.xyz, R0, -R1;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "MAD result.color.xyz, R2, R0, R1;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_HARDLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.w, -R0, c[1].y;\n"
+ "ADD R2.xyz, fragment.color.primary.w, -fragment.color.primary;\n"
+ "ADD R1.xyz, R0.w, -R0;\n"
+ "MUL R1.xyz, R1, R2;\n"
+ "MUL R1.xyz, R1, c[1].x;\n"
+ "MAD R1.xyz, fragment.color.primary.w, R0.w, -R1;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "MUL R3.xyz, fragment.color.primary, R1.w;\n"
+ "MUL R2.xyz, fragment.color.primary, R0;\n"
+ "ADD R1.w, -fragment.color.primary, c[1].y;\n"
+ "MAD R2.xyz, R2, c[1].x, R3;\n"
+ "MAD R2.xyz, R0, R1.w, R2;\n"
+ "MAD R0.xyz, R0, R1.w, R1;\n"
+ "ADD R1.xyz, R0, -R2;\n"
+ "MUL R0.xyz, fragment.color.primary, c[1].x;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "SGE R0.xyz, R0, fragment.color.primary.w;\n"
+ "MAD result.color.xyz, R0, R1, R2;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SOFTLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[3] = { program.local[0],\n"
+ " { 1, 9.9999997e-06, 2, 8 },\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MAX R1.x, R0.w, c[1].y;\n"
+ "RCP R1.w, R1.x;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R1.xyz, -R2, c[1].w;\n"
+ "ADD R4.xyz, R1, c[2].x;\n"
+ "MAD R1.xyz, -R0, R1.w, c[1].x;\n"
+ "RSQ R2.w, R2.x;\n"
+ "RSQ R2.z, R2.z;\n"
+ "RSQ R2.y, R2.y;\n"
+ "RCP R2.x, R2.w;\n"
+ "RCP R2.z, R2.z;\n"
+ "RCP R2.y, R2.y;\n"
+ "MAD R5.xyz, R2, R0.w, -R0;\n"
+ "MAD R2.xyz, fragment.color.primary, c[1].z, -fragment.color.primary.w;\n"
+ "MUL R3.xyz, R1, R2;\n"
+ "MAD R3.xyz, -R3, R4, fragment.color.primary.w;\n"
+ "MUL R4.xyz, R5, R2;\n"
+ "MAD R1.xyz, -R1, R2, fragment.color.primary.w;\n"
+ "MAD R5.xyz, R0, fragment.color.primary.w, R4;\n"
+ "MUL R3.xyz, R0, R3;\n"
+ "MUL R4.xyz, R0, c[1].w;\n"
+ "ADD R5.xyz, R5, -R3;\n"
+ "SGE R4.xyz, R4, R0.w;\n"
+ "MUL R4.xyz, R4, R5;\n"
+ "ADD R2.xyz, R3, R4;\n"
+ "MUL R1.xyz, R0, R1;\n"
+ "MUL R3.xyz, fragment.color.primary, c[1].z;\n"
+ "ADD R2.xyz, R2, -R1;\n"
+ "SGE R3.xyz, R3, fragment.color.primary.w;\n"
+ "MUL R2.xyz, R3, R2;\n"
+ "ADD R1.xyz, R1, R2;\n"
+ "ADD R1.w, -R0, c[1].x;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "ADD R2.x, -fragment.color.primary.w, c[1];\n"
+ "MAD result.color.xyz, R0, R2.x, R1;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DIFFERENCE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, R0, fragment.color.primary.w;\n"
+ "MUL R1.xyz, fragment.color.primary, R0.w;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "MIN R1.xyz, R1, R2;\n"
+ "ADD R0.xyz, fragment.color.primary, R0;\n"
+ "MAD result.color.xyz, -R1, c[1].x, R0;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_EXCLUSION_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[2] = { program.local[0],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.xyz, R0, fragment.color.primary.w;\n"
+ "MAD R2.xyz, fragment.color.primary, R0.w, R1;\n"
+ "MUL R1.xyz, fragment.color.primary, R0;\n"
+ "MAD R1.xyz, -R1, c[1].y, R2;\n"
+ "ADD R1.w, -R0, c[1].x;\n"
+ "MAD R1.xyz, fragment.color.primary, R1.w, R1;\n"
+ "ADD R1.w, fragment.color.primary, R0;\n"
+ "ADD R2.x, -fragment.color.primary.w, c[1];\n"
+ "MAD result.color.xyz, R0, R2.x, R1;\n"
+ "MAD result.color.w, -fragment.color.primary, R0, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODE_BLEND_MODE_MASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[3] = { program.local[0..2] };\n"
+ "TEMP R0;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MUL result.color, fragment.color.primary, R0.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODE_BLEND_MODE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[1] = { program.local[0] };\n"
+ "MOV result.color, fragment.color.primary;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF =
+ "!!ARBfp1.0\n"
+ "PARAM c[12] = { program.local[0..6],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[8..11] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[8].x, -R0;\n"
+ "MUL R0.y, R0.z, c[7];\n"
+ "MUL R0.x, R0, c[7];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "RSQ R0.z, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.x, -R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[9].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MOV R0.y, c[7].x;\n"
+ "MUL R0.y, c[8].x, R0;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R2.xyz, R1, c[11].y;\n"
+ "MUL R3.xyz, R2, R0.w;\n"
+ "MUL R2.xyz, R0, c[11].x;\n"
+ "MAD R2.xyz, R2, R1.w, R3;\n"
+ "ADD R3.xy, fragment.position, c[0];\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "MUL R0.xyz, R0, c[10].y;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R0.xyz, R1, c[10].z;\n"
+ "ADD R3.z, -R0.w, c[7];\n"
+ "MAD R2.xyz, R0, R3.z, R2;\n"
+ "MUL R0.y, R0.w, R2.w;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.z, R1.w, R3;\n"
+ "DP3 R2.w, R0, c[10];\n"
+ "MUL R3.xy, R3, c[1];\n"
+ "TEX R0, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_MULTIPLY =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..6],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[8..9] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[8].x, -R0;\n"
+ "MUL R0.y, R0.z, c[7];\n"
+ "MUL R0.x, R0, c[7];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.z, R0.y;\n"
+ "ADD R0.x, -R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[9].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MOV R0.y, c[7].x;\n"
+ "MUL R0.y, c[8].x, R0;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R2.x, -R1.w, c[7].z;\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, -R0.w, c[7].z;\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SCREEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..6],\n"
+ " { 2, 4 },\n"
+ " program.local[8..9] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[8].x, -R0;\n"
+ "MUL R0.y, R0.z, c[7];\n"
+ "MUL R0.x, R0, c[7];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[7].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[8].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[9];\n"
+ "ADD R3.xy, fragment.position, c[0];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R2, R0, R1;\n"
+ "MAD R2, -R0, R1, R2;\n"
+ "MUL R3.xy, R3, c[1];\n"
+ "TEX R0, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_OVERLAY =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..6],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[8..9] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[8].x, -R0;\n"
+ "MUL R0.y, R0.z, c[7];\n"
+ "MUL R0.x, R0, c[7];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[7].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[8].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[9];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[7].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[7].z;\n"
+ "MAD R3.xyz, R3, c[7].x, R4;\n"
+ "MAD R3.xyz, R1, R2.x, R3;\n"
+ "MAD R0.xyz, R1, R2.x, R0;\n"
+ "MUL R2.xyz, R1, c[7].x;\n"
+ "ADD R0.xyz, R0, -R3;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "MAD R2.xyz, R2, R0, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DARKEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..6],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[8..9] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[8].x, -R0;\n"
+ "MUL R0.y, R0.z, c[7];\n"
+ "MUL R0.x, R0, c[7];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[7].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[8].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[9];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[7].z;\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_LIGHTEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..6],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[8..9] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[8].x, -R0;\n"
+ "MUL R0.y, R0.z, c[7];\n"
+ "MUL R0.x, R0, c[7];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[7].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[8].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[9];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[7].z;\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORDODGE =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..6],\n"
+ " { 2, 4, 1, 1e-06 },\n"
+ " program.local[8..9] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[8].x, -R0;\n"
+ "MUL R0.y, R0.z, c[7];\n"
+ "MUL R0.x, R0, c[7];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[7].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[8].x, R0;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MAX R1.x, R0.w, c[7].w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R1.xyz, -R0, R1.x, c[7].z;\n"
+ "MAX R2.xyz, R1, c[7].w;\n"
+ "MUL R1.xy, fragment.position, c[9];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[7].z;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "MAD R4.xyz, R0, R2.w, R3;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R0.xyz, R0, R1.w, R3;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R3, R2, R4;\n"
+ "MAD R4.xyz, R0.w, R1.w, R4;\n"
+ "ADD R4.xyz, R4, -R2;\n"
+ "MAD R2.xyz, R0, R4, R2;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORBURN =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..6],\n"
+ " { 2, 4, 1, 9.9999997e-06 },\n"
+ " program.local[8..9] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[8].x, -R0;\n"
+ "MUL R0.y, R0.z, c[7];\n"
+ "MUL R0.x, R0, c[7];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[7].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[8].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[9];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[7].w;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[7].z;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R2.xyz, R1, R3.w, R2;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R3.xyz, R3, R2.w;\n"
+ "MAD R2.xyz, R3, R2, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_HARDLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..6],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[8..9] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[8].x, -R0;\n"
+ "MUL R0.y, R0.z, c[7];\n"
+ "MUL R0.x, R0, c[7];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[7].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[8].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[9];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[7].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[7].z;\n"
+ "MAD R3.xyz, R3, c[7].x, R4;\n"
+ "MUL R0.xyz, R0, c[7].x;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD R3.xyz, R1, R2.w, R3;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MAD R2.xyz, R0, R2, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SOFTLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..6],\n"
+ " { 2, 4, 1, 9.9999997e-06 },\n"
+ " program.local[8..9],\n"
+ " { 8, 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[8].x, -R0;\n"
+ "MUL R0.y, R0.z, c[7];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[9].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[7];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MAX R0.z, R1.w, c[7].w;\n"
+ "RCP R2.w, R0.z;\n"
+ "MUL R2.xyz, R1, R2.w;\n"
+ "MUL R6.xyz, -R2, c[10].x;\n"
+ "MAD R3.xyz, -R1, R2.w, c[7].z;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "MOV R0.y, c[7].x;\n"
+ "MUL R0.y, c[8].x, R0;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MAD R4.xyz, R0, c[7].x, -R0.w;\n"
+ "MUL R5.xyz, R3, R4;\n"
+ "MAD R3.xyz, -R3, R4, R0.w;\n"
+ "ADD R6.xyz, R6, c[10].y;\n"
+ "RSQ R2.x, R2.x;\n"
+ "RSQ R2.z, R2.z;\n"
+ "RSQ R2.y, R2.y;\n"
+ "MAD R5.xyz, -R5, R6, R0.w;\n"
+ "MUL R3.xyz, R1, R3;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.z, R2.z;\n"
+ "RCP R2.y, R2.y;\n"
+ "MAD R2.xyz, R2, R1.w, -R1;\n"
+ "MUL R6.xyz, R2, R4;\n"
+ "MUL R2.xyz, R1, R5;\n"
+ "MAD R6.xyz, R1, R0.w, R6;\n"
+ "MUL R4.xyz, R0, c[7].x;\n"
+ "MUL R5.xyz, R1, c[10].x;\n"
+ "ADD R6.xyz, R6, -R2;\n"
+ "SGE R5.xyz, R5, R1.w;\n"
+ "MUL R5.xyz, R5, R6;\n"
+ "ADD R2.xyz, R2, R5;\n"
+ "SGE R4.xyz, R4, R0.w;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MUL R2.xyz, R4, R2;\n"
+ "ADD R2.xyz, R3, R2;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[7].z;\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DIFFERENCE =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..6],\n"
+ " { 2, 4 },\n"
+ " program.local[8..9] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[8].x, -R0;\n"
+ "MUL R0.y, R0.z, c[7];\n"
+ "MUL R0.x, R0, c[7];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "RSQ R0.z, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.x, -R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[9].xyxy;\n"
+ "MOV R0.y, c[7].x;\n"
+ "MUL R0.y, c[8].x, R0;\n"
+ "RCP R0.y, R0.y;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R3.xyz, R0, R1;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MUL R0.xyz, R0, R1.w;\n"
+ "MIN R0.xyz, R0, R2;\n"
+ "MAD R2.xyz, -R0, c[7].x, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_EXCLUSION =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..6],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[8..9] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[8].x, -R0;\n"
+ "MUL R0.y, R0.z, c[7];\n"
+ "MUL R0.x, R0, c[7];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[7].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[8].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[9];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[7].x, R3;\n"
+ "ADD R2.w, -R1, c[7].z;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[7].z;\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..3],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[5..8] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "RSQ R0.z, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.x, -R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[6].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, c[8].y;\n"
+ "MOV R0.y, c[4].x;\n"
+ "MUL R0.y, c[5].x, R0;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R3.xyz, R2, R0.w;\n"
+ "MUL R2.xyz, R0, c[8].x;\n"
+ "MAD R2.xyz, R2, R1.w, R3;\n"
+ "ADD R2.w, -R1, c[4].z;\n"
+ "MUL R0.xyz, R0, c[7].y;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[4].z;\n"
+ "MUL R1.xyz, R1, c[7].z;\n"
+ "MAD result.color.xyz, R1, R2.x, R0;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.z, R1.w, R2.x;\n"
+ "MUL R0.y, R0.w, R2.w;\n"
+ "DP3 result.color.w, R0, c[7];\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_MULTIPLY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..3],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[5..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.z, R0.y;\n"
+ "ADD R0.x, -R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[6].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MOV R0.y, c[4].x;\n"
+ "MUL R0.y, c[5].x, R0;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2.x, -R1.w, c[4].z;\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[4].z;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SCREEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..3],\n"
+ " { 2, 4 },\n"
+ " program.local[5..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "RSQ R0.z, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.x, -R0, R0.z;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[6].xyxy;\n"
+ "MOV R0.y, c[4].x;\n"
+ "MUL R0.y, c[5].x, R0;\n"
+ "RCP R0.y, R0.y;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2, R0, R1;\n"
+ "MAD result.color, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_OVERLAY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..3],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[5..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[4].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[5].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[6];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[4].z;\n"
+ "MUL R2.xyz, R2, c[4].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "MUL R0.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[4].z;\n"
+ "MAD R0.xyz, R0, c[4].x, R3;\n"
+ "MAD R0.xyz, R1, R2.w, R0;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R1.xyz, R1, c[4].x;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R1.xyz, R1, R1.w;\n"
+ "MAD result.color.xyz, R1, R2, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DARKEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..3],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[5..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[4].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[5].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[6];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[4].z;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[4].z;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_LIGHTEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..3],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[5..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[4].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[5].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[6];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[4].z;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[4].z;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORDODGE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..3],\n"
+ " { 2, 4, 1, 1e-06 },\n"
+ " program.local[5..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[4].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[5].x, R0;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MAX R1.x, R0.w, c[4].w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R1.xyz, -R0, R1.x, c[4].z;\n"
+ "MAX R2.xyz, R1, c[4].w;\n"
+ "MUL R1.xy, fragment.position, c[6];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[4].z;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[4].z;\n"
+ "MAD R3.xyz, R0, R2.w, R3;\n"
+ "MUL R1.xyz, R1, R0.w;\n"
+ "MAD R0.xyz, R0, R1.w, R1;\n"
+ "MUL R2.w, R0, R1;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R1, R2, R3;\n"
+ "MAD R3.xyz, R0.w, R1.w, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "MAD result.color.xyz, R0, R3, R2;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORBURN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..3],\n"
+ " { 2, 4, 1, 9.9999997e-06 },\n"
+ " program.local[5..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[4].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[5].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[6];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "ADD R2.w, -R1, c[4].z;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[4].w;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[4].z;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R1.xyz, R1, R3.w, R2;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.x, R0.w, R1.w;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R1.xyz, R1, -R0;\n"
+ "SGE R2.xyz, R3, R2.x;\n"
+ "MAD result.color.xyz, R2, R1, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_HARDLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..3],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[5..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[4].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[5].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[6];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2.w, -R1, c[4].z;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[4].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R2.w, -R0, c[4].z;\n"
+ "MUL R0.xyz, R0, c[4].x;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MAD R3.xyz, R3, c[4].x, R4;\n"
+ "MAD R1.xyz, R1, R2.w, R3;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R1;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD result.color.xyz, R0, R2, R1;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SOFTLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..3],\n"
+ " { 2, 4, 1, 9.9999997e-06 },\n"
+ " program.local[5..6],\n"
+ " { 8, 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[6].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MAX R0.z, R1.w, c[4].w;\n"
+ "RCP R2.w, R0.z;\n"
+ "MUL R2.xyz, R1, R2.w;\n"
+ "MUL R6.xyz, -R2, c[7].x;\n"
+ "MAD R3.xyz, -R1, R2.w, c[4].z;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "MOV R0.y, c[4].x;\n"
+ "MUL R0.y, c[5].x, R0;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.x, R0, R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MAD R4.xyz, R0, c[4].x, -R0.w;\n"
+ "MUL R5.xyz, R3, R4;\n"
+ "MAD R3.xyz, -R3, R4, R0.w;\n"
+ "ADD R6.xyz, R6, c[7].y;\n"
+ "RSQ R2.x, R2.x;\n"
+ "RSQ R2.z, R2.z;\n"
+ "RSQ R2.y, R2.y;\n"
+ "MAD R5.xyz, -R5, R6, R0.w;\n"
+ "MUL R3.xyz, R1, R3;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.z, R2.z;\n"
+ "RCP R2.y, R2.y;\n"
+ "MAD R2.xyz, R2, R1.w, -R1;\n"
+ "MUL R6.xyz, R2, R4;\n"
+ "MUL R2.xyz, R1, R5;\n"
+ "MUL R4.xyz, R0, c[4].x;\n"
+ "MAD R6.xyz, R1, R0.w, R6;\n"
+ "MUL R5.xyz, R1, c[7].x;\n"
+ "ADD R6.xyz, R6, -R2;\n"
+ "SGE R5.xyz, R5, R1.w;\n"
+ "MUL R5.xyz, R5, R6;\n"
+ "ADD R2.xyz, R2, R5;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "SGE R4.xyz, R4, R0.w;\n"
+ "MUL R2.xyz, R4, R2;\n"
+ "ADD R2.xyz, R3, R2;\n"
+ "ADD R2.w, -R1, c[4].z;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[4].z;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DIFFERENCE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..3],\n"
+ " { 2, 4 },\n"
+ " program.local[5..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[4].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[5].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[6];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "ADD R0.xyz, R0, R1;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "MAD result.color.xyz, -R2, c[4].x, R0;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_EXCLUSION_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..3],\n"
+ " { 2, 4, 1 },\n"
+ " program.local[5..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[4].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "MUL R0.z, c[5].x, R0;\n"
+ "MUL R1.xy, fragment.position, c[6];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[4].x, R3;\n"
+ "ADD R2.w, -R1, c[4].z;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[4].z;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODE_BLEND_MODE_MASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..3],\n"
+ " { 2, 4 },\n"
+ " program.local[5..8] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "RSQ R0.y, R0.y;\n"
+ "RCP R0.y, R0.y;\n"
+ "ADD R1.x, -R0, R0.y;\n"
+ "MOV R0.z, c[4].x;\n"
+ "MUL R0.z, c[5].x, R0;\n"
+ "RCP R1.y, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[6];\n"
+ "MUL R0.xy, R0, c[7];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1.x, R1, R1.y;\n"
+ "DP4 R1.y, R0, c[8];\n"
+ "TEX R0, R1, texture[1], 1D;\n"
+ "MUL result.color, R0, R1.y;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODE_BLEND_MODE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..3],\n"
+ " { 2, 4 },\n"
+ " program.local[5] };\n"
+ "TEMP R0;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.xyxy;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "ADD R0.z, R0, R0.w;\n"
+ "MUL R0.z, c[5].x, -R0;\n"
+ "MUL R0.y, R0.z, c[4];\n"
+ "MUL R0.x, R0, c[4];\n"
+ "MAD R0.y, R0.x, R0.x, -R0;\n"
+ "MOV R0.z, c[4].x;\n"
+ "RSQ R0.y, R0.y;\n"
+ "MUL R0.z, c[5].x, R0;\n"
+ "RCP R0.y, R0.y;\n"
+ "RCP R0.z, R0.z;\n"
+ "ADD R0.x, -R0, R0.y;\n"
+ "MUL R0.x, R0, R0.z;\n"
+ "TEX result.color, R0, texture[0], 1D;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF =
+ "!!ARBfp1.0\n"
+ "PARAM c[13] = { program.local[0..5],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[8],\n"
+ " { 0.15915494 },\n"
+ " program.local[10..12] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[6].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[6].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[7].y;\n"
+ "CMP R0.x, R0, c[7], R1;\n"
+ "MAD R0.w, R0, c[6].z, -c[6];\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "CMP R0.y, -R0, c[7].z, c[7].w;\n"
+ "MAD R0.x, R0, R0.y, c[8];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "FLR R0.y, R0.x;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[10].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R2.xyz, R1, c[12].y;\n"
+ "MUL R3.xyz, R2, R0.w;\n"
+ "MUL R2.xyz, R0, c[12].x;\n"
+ "MAD R2.xyz, R2, R1.w, R3;\n"
+ "ADD R3.xy, fragment.position, c[0];\n"
+ "ADD R2.w, -R1, c[7];\n"
+ "MUL R0.xyz, R0, c[11].y;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R0.xyz, R1, c[11].z;\n"
+ "ADD R3.z, -R0.w, c[7].w;\n"
+ "MAD R2.xyz, R0, R3.z, R2;\n"
+ "MUL R0.y, R0.w, R2.w;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.z, R1.w, R3;\n"
+ "DP3 R2.w, R0, c[11];\n"
+ "MUL R3.xy, R3, c[1];\n"
+ "TEX R0, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_MULTIPLY =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..5],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[8],\n"
+ " { 0.15915494 },\n"
+ " program.local[10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[6].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[6].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[7].y;\n"
+ "CMP R0.x, R0, c[7], R1;\n"
+ "MAD R0.w, R0, c[6].z, -c[6];\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "CMP R0.y, -R0, c[7].z, c[7].w;\n"
+ "MAD R0.x, R0, R0.y, c[8];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "FLR R0.y, R0.x;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[10].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R2.x, -R1.w, c[7].w;\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, -R0.w, c[7].w;\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SCREEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..5],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[8],\n"
+ " { 0.15915494 },\n"
+ " program.local[10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ADD R3.xy, fragment.position, c[0];\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[6].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[6].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[7].y;\n"
+ "CMP R0.x, R0, c[7], R1;\n"
+ "MAD R0.w, R0, c[6].z, -c[6];\n"
+ "MUL R1.xy, fragment.position, c[10];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "CMP R0.y, -R0, c[7].z, c[7].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[8];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R2, R0, R1;\n"
+ "MAD R2, -R0, R1, R2;\n"
+ "MUL R3.xy, R3, c[1];\n"
+ "TEX R0, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_OVERLAY =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..5],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[8],\n"
+ " { 0.15915494, 2 },\n"
+ " program.local[10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[6].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[6].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[7].y;\n"
+ "CMP R0.x, R0, c[7], R1;\n"
+ "MAD R0.w, R0, c[6].z, -c[6];\n"
+ "MUL R1.xy, fragment.position, c[10];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[7];\n"
+ "CMP R0.y, -R0, c[7].z, c[7].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[8];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[9].y;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[7].w;\n"
+ "MAD R3.xyz, R3, c[9].y, R4;\n"
+ "MAD R3.xyz, R1, R2.x, R3;\n"
+ "MAD R0.xyz, R1, R2.x, R0;\n"
+ "MUL R2.xyz, R1, c[9].y;\n"
+ "ADD R0.xyz, R0, -R3;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "MAD R2.xyz, R2, R0, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DARKEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..5],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[8],\n"
+ " { 0.15915494 },\n"
+ " program.local[10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[6].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[6].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[7].y;\n"
+ "CMP R0.x, R0, c[7], R1;\n"
+ "MAD R0.w, R0, c[6].z, -c[6];\n"
+ "MUL R1.xy, fragment.position, c[10];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "CMP R0.y, -R0, c[7].z, c[7].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[8];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[7];\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[7].w;\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_LIGHTEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..5],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[8],\n"
+ " { 0.15915494 },\n"
+ " program.local[10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[6].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[6].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[7].y;\n"
+ "CMP R0.x, R0, c[7], R1;\n"
+ "MAD R0.w, R0, c[6].z, -c[6];\n"
+ "MUL R1.xy, fragment.position, c[10];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "CMP R0.y, -R0, c[7].z, c[7].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[8];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[7];\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[7].w;\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORDODGE =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..5],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[8],\n"
+ " { 0.15915494, 1e-06 },\n"
+ " program.local[10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[6].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[6].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[7].y;\n"
+ "CMP R0.x, R0, c[7], R1;\n"
+ "MAD R0.w, R0, c[6].z, -c[6];\n"
+ "CMP R0.y, -R0, c[7].z, c[7].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[8];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MAX R1.x, R0.w, c[9].y;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R1.xyz, -R0, R1.x, c[7].w;\n"
+ "MAX R2.xyz, R1, c[9].y;\n"
+ "MUL R1.xy, fragment.position, c[10];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[7];\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[7];\n"
+ "MAD R4.xyz, R0, R2.w, R3;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R0.xyz, R0, R1.w, R3;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R3, R2, R4;\n"
+ "MAD R4.xyz, R0.w, R1.w, R4;\n"
+ "ADD R4.xyz, R4, -R2;\n"
+ "MAD R2.xyz, R0, R4, R2;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORBURN =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..5],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[8],\n"
+ " { 0.15915494, 9.9999997e-06 },\n"
+ " program.local[10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[6].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[6].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[7].y;\n"
+ "CMP R0.x, R0, c[7], R1;\n"
+ "MAD R0.w, R0, c[6].z, -c[6];\n"
+ "MUL R1.xy, fragment.position, c[10];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[7];\n"
+ "CMP R0.y, -R0, c[7].z, c[7].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[8];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[9].y;\n"
+ "ADD R3.w, -R0, c[7];\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R2.xyz, R1, R3.w, R2;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R3.xyz, R3, R2.w;\n"
+ "MAD R2.xyz, R3, R2, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_HARDLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..5],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[8],\n"
+ " { 0.15915494, 2 },\n"
+ " program.local[10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[6].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[6].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[7].y;\n"
+ "CMP R0.x, R0, c[7], R1;\n"
+ "MAD R0.w, R0, c[6].z, -c[6];\n"
+ "MUL R1.xy, fragment.position, c[10];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[7];\n"
+ "CMP R0.y, -R0, c[7].z, c[7].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[8];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[9].y;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[7];\n"
+ "MAD R3.xyz, R3, c[9].y, R4;\n"
+ "MUL R0.xyz, R0, c[9].y;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD R3.xyz, R1, R2.w, R3;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MAD R2.xyz, R0, R2, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SOFTLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[12] = { program.local[0..5],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[8],\n"
+ " { 0.15915494, 9.9999997e-06, 2, 8 },\n"
+ " program.local[10],\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[6].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[6].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MOV R1.x, c[7].y;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "CMP R0.x, R0, c[7], R1;\n"
+ "MAD R0.w, R0, c[6].z, -c[6];\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[10].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "CMP R0.y, -R0, c[7].z, c[7].w;\n"
+ "MAD R0.x, R0, R0.y, c[8];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "FLR R0.y, R0.x;\n"
+ "MAX R0.z, R1.w, c[9].y;\n"
+ "RCP R2.w, R0.z;\n"
+ "MUL R2.xyz, R1, R2.w;\n"
+ "RSQ R3.w, R2.x;\n"
+ "RSQ R4.y, R2.z;\n"
+ "RCP R4.x, R3.w;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MAD R3.xyz, R0, c[9].z, -R0.w;\n"
+ "RSQ R3.w, R2.y;\n"
+ "RCP R4.z, R4.y;\n"
+ "RCP R4.y, R3.w;\n"
+ "MAD R4.xyz, R4, R1.w, -R1;\n"
+ "MUL R6.xyz, R4, R3;\n"
+ "MUL R4.xyz, -R2, c[9].w;\n"
+ "MAD R2.xyz, -R1, R2.w, c[7].w;\n"
+ "ADD R5.xyz, R4, c[11].x;\n"
+ "MUL R4.xyz, R2, R3;\n"
+ "MAD R4.xyz, -R4, R5, R0.w;\n"
+ "MAD R2.xyz, -R2, R3, R0.w;\n"
+ "MAD R5.xyz, R1, R0.w, R6;\n"
+ "MUL R4.xyz, R1, R4;\n"
+ "MUL R6.xyz, R1, c[9].w;\n"
+ "ADD R5.xyz, R5, -R4;\n"
+ "SGE R6.xyz, R6, R1.w;\n"
+ "MUL R5.xyz, R6, R5;\n"
+ "ADD R3.xyz, R4, R5;\n"
+ "MUL R2.xyz, R1, R2;\n"
+ "MUL R4.xyz, R0, c[9].z;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "SGE R4.xyz, R4, R0.w;\n"
+ "MUL R3.xyz, R4, R3;\n"
+ "ADD R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[7];\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[7].w;\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DIFFERENCE =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..5],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[8],\n"
+ " { 0.15915494, 2 },\n"
+ " program.local[10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[6].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[6].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[7].y;\n"
+ "CMP R0.x, R0, c[7], R1;\n"
+ "MAD R0.w, R0, c[6].z, -c[6];\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "CMP R0.y, -R0, c[7].z, c[7].w;\n"
+ "MAD R0.x, R0, R0.y, c[8];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "FLR R0.y, R0.x;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[10].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R3.xyz, R0, R1;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MUL R0.xyz, R0, R1.w;\n"
+ "MIN R0.xyz, R0, R2;\n"
+ "MAD R2.xyz, -R0, c[9].y, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_EXCLUSION =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..5],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[8],\n"
+ " { 0.15915494, 2 },\n"
+ " program.local[10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[6].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[6].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[7].y;\n"
+ "CMP R0.x, R0, c[7], R1;\n"
+ "MAD R0.w, R0, c[6].z, -c[6];\n"
+ "MUL R1.xy, fragment.position, c[10];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "CMP R0.y, -R0, c[7].z, c[7].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[8];\n"
+ "MUL R0.x, R0, c[9];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[9].y, R3;\n"
+ "ADD R2.w, -R1, c[7];\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[7].w;\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494 },\n"
+ " program.local[7..9] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, c[9].y;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R3.xyz, R2, R0.w;\n"
+ "MUL R2.xyz, R0, c[9].x;\n"
+ "MAD R2.xyz, R2, R1.w, R3;\n"
+ "ADD R2.w, -R1, c[4];\n"
+ "MUL R0.xyz, R0, c[8].y;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[4].w;\n"
+ "MUL R1.xyz, R1, c[8].z;\n"
+ "MAD result.color.xyz, R1, R2.x, R0;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.z, R1.w, R2.x;\n"
+ "MUL R0.y, R0.w, R2.w;\n"
+ "DP3 result.color.w, R0, c[8];\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_MULTIPLY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494 },\n"
+ " program.local[7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2.x, -R1.w, c[4].w;\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[4].w;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SCREEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494 },\n"
+ " program.local[7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2, R0, R1;\n"
+ "MAD result.color, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_OVERLAY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494, 2 },\n"
+ " program.local[7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[4];\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[6].y;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "MUL R0.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[4];\n"
+ "MAD R0.xyz, R0, c[6].y, R3;\n"
+ "MAD R0.xyz, R1, R2.w, R0;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R1.xyz, R1, c[6].y;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R1.xyz, R1, R1.w;\n"
+ "MAD result.color.xyz, R1, R2, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DARKEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494 },\n"
+ " program.local[7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[4];\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[4].w;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_LIGHTEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494 },\n"
+ " program.local[7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[4];\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[4].w;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORDODGE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494, 1e-06 },\n"
+ " program.local[7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MAX R1.x, R0.w, c[6].y;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R1.xyz, -R0, R1.x, c[4].w;\n"
+ "MAX R2.xyz, R1, c[6].y;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[4];\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[4];\n"
+ "MAD R3.xyz, R0, R2.w, R3;\n"
+ "MUL R1.xyz, R1, R0.w;\n"
+ "MAD R0.xyz, R0, R1.w, R1;\n"
+ "MUL R2.w, R0, R1;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R1, R2, R3;\n"
+ "MAD R3.xyz, R0.w, R1.w, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "MAD result.color.xyz, R0, R3, R2;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORBURN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494, 9.9999997e-06 },\n"
+ " program.local[7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[4];\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[6].y;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[4];\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R1.xyz, R1, R3.w, R2;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.x, R0.w, R1.w;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R1.xyz, R1, -R0;\n"
+ "SGE R2.xyz, R3, R2.x;\n"
+ "MAD result.color.xyz, R2, R1, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_HARDLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494, 2 },\n"
+ " program.local[7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[4];\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[6].y;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R2.w, -R0, c[4];\n"
+ "MUL R0.xyz, R0, c[6].y;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MAD R3.xyz, R3, c[6].y, R4;\n"
+ "MAD R1.xyz, R1, R2.w, R3;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R1;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD result.color.xyz, R0, R2, R1;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SOFTLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494, 9.9999997e-06, 2, 8 },\n"
+ " program.local[7],\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MOV R1.x, c[4].y;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "MAX R0.z, R1.w, c[6].y;\n"
+ "RCP R2.w, R0.z;\n"
+ "MUL R2.xyz, R1, R2.w;\n"
+ "RSQ R3.w, R2.x;\n"
+ "RSQ R4.y, R2.z;\n"
+ "RCP R4.x, R3.w;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MAD R3.xyz, R0, c[6].z, -R0.w;\n"
+ "RSQ R3.w, R2.y;\n"
+ "RCP R4.z, R4.y;\n"
+ "RCP R4.y, R3.w;\n"
+ "MAD R4.xyz, R4, R1.w, -R1;\n"
+ "MUL R6.xyz, R4, R3;\n"
+ "MUL R4.xyz, -R2, c[6].w;\n"
+ "MAD R2.xyz, -R1, R2.w, c[4].w;\n"
+ "ADD R5.xyz, R4, c[8].x;\n"
+ "MUL R4.xyz, R2, R3;\n"
+ "MAD R4.xyz, -R4, R5, R0.w;\n"
+ "MAD R2.xyz, -R2, R3, R0.w;\n"
+ "MAD R5.xyz, R1, R0.w, R6;\n"
+ "MUL R4.xyz, R1, R4;\n"
+ "MUL R6.xyz, R1, c[6].w;\n"
+ "ADD R5.xyz, R5, -R4;\n"
+ "SGE R6.xyz, R6, R1.w;\n"
+ "MUL R5.xyz, R6, R5;\n"
+ "ADD R3.xyz, R4, R5;\n"
+ "MUL R2.xyz, R1, R2;\n"
+ "MUL R4.xyz, R0, c[6].z;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "SGE R4.xyz, R4, R0.w;\n"
+ "MUL R3.xyz, R4, R3;\n"
+ "ADD R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[4];\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[4].w;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DIFFERENCE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494, 2 },\n"
+ " program.local[7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "ADD R0.xyz, R0, R1;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "MAD result.color.xyz, -R2, c[6].y, R0;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_EXCLUSION_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494, 2 },\n"
+ " program.local[7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[6].y, R3;\n"
+ "ADD R2.w, -R1, c[4];\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[4].w;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODE_BLEND_MODE_MASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494 },\n"
+ " program.local[7..9] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.y, R0.w;\n"
+ "RCP R1.x, R1.x;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.z, R0.x, R0.y, c[5].x;\n"
+ "MUL R1.x, R0.z, c[6];\n"
+ "FLR R1.y, R1.x;\n"
+ "ADD R0.xy, fragment.position, c[7];\n"
+ "MUL R0.xy, R0, c[8];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R1.x, R1, -R1.y;\n"
+ "DP4 R1.y, R0, c[9];\n"
+ "TEX R0, R1, texture[1], 1D;\n"
+ "MUL result.color, R0, R1.y;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODE_BLEND_MODE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..2],\n"
+ " { 0.0020000001, 9.9999997e-10, 0.1963, 0.9817 },\n"
+ " { 2.3561945, 0.78539819, -1, 1 },\n"
+ " program.local[5],\n"
+ " { 0.15915494 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "ABS R0.w, R0.x;\n"
+ "ABS R0.z, R0.y;\n"
+ "ADD R0.z, R0, -R0.w;\n"
+ "ADD R0.w, R0.y, c[3].x;\n"
+ "ABS R0.z, R0;\n"
+ "CMP R0.y, -R0.z, R0, R0.w;\n"
+ "ABS R0.z, -R0.y;\n"
+ "ADD R0.z, R0, c[3].y;\n"
+ "ADD R0.w, R0.x, R0.z;\n"
+ "ADD R1.x, R0.z, -R0;\n"
+ "RCP R1.x, R1.x;\n"
+ "RCP R1.y, R0.w;\n"
+ "MUL R0.w, R0, R1.x;\n"
+ "ADD R0.z, R0.x, -R0;\n"
+ "MUL R0.z, R0, R1.y;\n"
+ "CMP R0.z, R0.x, R0.w, R0;\n"
+ "MUL R0.w, R0.z, R0.z;\n"
+ "MOV R1.x, c[4].y;\n"
+ "CMP R0.y, -R0, c[4].z, c[4].w;\n"
+ "MAD R0.w, R0, c[3].z, -c[3];\n"
+ "CMP R0.x, R0, c[4], R1;\n"
+ "MAD R0.x, R0.w, R0.z, R0;\n"
+ "MAD R0.x, R0, R0.y, c[5];\n"
+ "MUL R0.x, R0, c[6];\n"
+ "FLR R0.y, R0.x;\n"
+ "ADD R0.x, R0, -R0.y;\n"
+ "TEX result.color, R0, texture[0], 1D;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SIMPLE_PORTER_DUFF =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 1 },\n"
+ " program.local[9..10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[6].z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R2.xyz, R1, c[10].y;\n"
+ "MUL R3.xyz, R2, R0.w;\n"
+ "MUL R2.xyz, R0, c[10].x;\n"
+ "MAD R2.xyz, R2, R1.w, R3;\n"
+ "ADD R3.xy, fragment.position, c[0];\n"
+ "MUL R0.xyz, R0, c[9].y;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R0.xyz, R1, c[9].z;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R0.y, -R1.w, c[8].x;\n"
+ "MUL R0.z, R1.w, R2.w;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.y, R0.w, R0;\n"
+ "DP3 R2.w, R0, c[9];\n"
+ "MUL R3.xy, R3, c[1];\n"
+ "TEX R0, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_MULTIPLY =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[6].z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SCREEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[6].z;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "ADD R3.xy, fragment.position, c[0];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R2, R0, R1;\n"
+ "MAD R2, -R0, R1, R2;\n"
+ "MUL R3.xy, R3, c[1];\n"
+ "TEX R0, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_OVERLAY =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[6].z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[8].y;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[8].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8].y;\n"
+ "MAD R3.xyz, R3, c[8].x, R4;\n"
+ "MAD R3.xyz, R1, R2.x, R3;\n"
+ "MAD R0.xyz, R1, R2.x, R0;\n"
+ "MUL R2.xyz, R1, c[8].x;\n"
+ "ADD R0.xyz, R0, -R3;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "MAD R2.xyz, R2, R0, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DARKEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[6].z;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_LIGHTEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[6].z;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORDODGE =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 1e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[6].z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MAX R1.x, R0.w, c[8].y;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R2.xyz, -R0, R1.x, c[8].x;\n"
+ "MAX R2.xyz, R2, c[8].y;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R4.xyz, R0, R2.w, R3;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R0.xyz, R0, R1.w, R3;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R3, R2, R4;\n"
+ "MAD R4.xyz, R0.w, R1.w, R4;\n"
+ "ADD R4.xyz, R4, -R2;\n"
+ "MAD R2.xyz, R0, R4, R2;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORBURN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 9.9999997e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[6].z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[8].y;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[8].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.w, R0, R1;\n"
+ "ADD R3.w, -R0, c[8].x;\n"
+ "MAD R2.xyz, R1, R3.w, R2;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R3.xyz, R3, R2.w;\n"
+ "MAD R2.xyz, R3, R2, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_HARDLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[6].z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[8].y;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[8].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[8].y;\n"
+ "MAD R3.xyz, R3, c[8].x, R4;\n"
+ "MUL R0.xyz, R0, c[8].x;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD R3.xyz, R1, R2.w, R3;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MAD R2.xyz, R0, R2, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SOFTLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..7],\n"
+ " { 1, 9.9999997e-06, 2, 8 },\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R2.xyz, fragment.position.y, c[4];\n"
+ "MAD R3.xyz, fragment.position.x, c[3], R2;\n"
+ "MUL R0.xy, fragment.position, c[7];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "MAX R0.x, R1.w, c[8].y;\n"
+ "RCP R2.w, R0.x;\n"
+ "MUL R0.xyz, R1, R2.w;\n"
+ "RSQ R0.w, R0.x;\n"
+ "RSQ R2.y, R0.y;\n"
+ "ADD R3.xyz, R3, c[5];\n"
+ "RCP R2.x, R0.w;\n"
+ "RCP R0.w, R3.z;\n"
+ "MUL R3.xy, R3, R0.w;\n"
+ "RSQ R0.w, R0.z;\n"
+ "RCP R2.z, R0.w;\n"
+ "RCP R2.y, R2.y;\n"
+ "MAD R6.xyz, R2, R1.w, -R1;\n"
+ "MUL R2.xyz, -R0, c[8].w;\n"
+ "ADD R5.xyz, R2, c[9].x;\n"
+ "MAD R2.xyz, -R1, R2.w, c[8].x;\n"
+ "MUL R3.xy, R3, c[6];\n"
+ "ADD R0.w, R3.x, R3.y;\n"
+ "MUL R0.w, R0, c[6].z;\n"
+ "TEX R0, R0.w, texture[2], 1D;\n"
+ "MAD R3.xyz, R0, c[8].z, -R0.w;\n"
+ "MUL R4.xyz, R2, R3;\n"
+ "MAD R4.xyz, -R4, R5, R0.w;\n"
+ "MUL R5.xyz, R6, R3;\n"
+ "MAD R2.xyz, -R2, R3, R0.w;\n"
+ "MAD R6.xyz, R1, R0.w, R5;\n"
+ "MUL R4.xyz, R1, R4;\n"
+ "MUL R5.xyz, R1, c[8].w;\n"
+ "ADD R6.xyz, R6, -R4;\n"
+ "SGE R5.xyz, R5, R1.w;\n"
+ "MUL R5.xyz, R5, R6;\n"
+ "ADD R3.xyz, R4, R5;\n"
+ "MUL R2.xyz, R1, R2;\n"
+ "MUL R4.xyz, R0, c[8].z;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "SGE R4.xyz, R4, R0.w;\n"
+ "MUL R3.xyz, R4, R3;\n"
+ "ADD R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DIFFERENCE =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[6].z;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "ADD R3.xyz, R0, R1;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MUL R0.xyz, R0, R1.w;\n"
+ "MIN R0.xyz, R0, R2;\n"
+ "MAD R2.xyz, -R0, c[8].x, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_EXCLUSION =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[6].z;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[2], 1D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[8].y, R3;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 1 },\n"
+ " program.local[6..7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, c[7].y;\n"
+ "MUL R0.x, R0, c[3].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R3.xyz, R2, R0.w;\n"
+ "MUL R2.xyz, R0, c[7].x;\n"
+ "MAD R2.xyz, R2, R1.w, R3;\n"
+ "MUL R0.xyz, R0, c[6].y;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[5];\n"
+ "MUL R1.xyz, R1, c[6].z;\n"
+ "MAD result.color.xyz, R1, R2.x, R0;\n"
+ "ADD R0.y, -R1.w, c[5].x;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.z, R1.w, R2.x;\n"
+ "MUL R0.y, R0.w, R0;\n"
+ "DP3 result.color.w, R0, c[6];\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_MULTIPLY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[3].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2.x, -R1.w, c[5];\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SCREEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..4] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R0.x, R0, c[3].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "ADD R2, R0, R1;\n"
+ "MAD result.color, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_OVERLAY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[3].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].y;\n"
+ "MUL R2.xyz, R2, c[5].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "MUL R0.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[5].y;\n"
+ "MAD R0.xyz, R0, c[5].x, R3;\n"
+ "MAD R0.xyz, R1, R2.w, R0;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R1.xyz, R1, c[5].x;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R1.xyz, R1, R1.w;\n"
+ "MAD result.color.xyz, R1, R2, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DARKEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[3].z;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_LIGHTEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[3].z;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORDODGE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 1e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[3].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MAX R1.x, R0.w, c[5].y;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R3.xyz, -R0, R1.x, c[5].x;\n"
+ "MAX R3.xyz, R3, c[5].y;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.x, -R0.w, c[5];\n"
+ "MUL R2.xyz, R1, R2.x;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R1.xyz, R1, R0.w;\n"
+ "MAD R0.xyz, R0, R1.w, R1;\n"
+ "MUL R2.w, R0, R1;\n"
+ "RCP R3.x, R3.x;\n"
+ "RCP R3.y, R3.y;\n"
+ "RCP R3.z, R3.z;\n"
+ "MAD R3.xyz, R1, R3, R2;\n"
+ "MAD R2.xyz, R0.w, R1.w, R2;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "MAD result.color.xyz, R0, R2, R3;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORBURN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 9.9999997e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[3].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[5].y;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[5].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "ADD R3.w, -R0, c[5].x;\n"
+ "MAD R1.xyz, R1, R3.w, R2;\n"
+ "MUL R2.x, R0.w, R1.w;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R1.xyz, R1, -R0;\n"
+ "SGE R2.xyz, R3, R2.x;\n"
+ "MAD result.color.xyz, R2, R1, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_HARDLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[3].z;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[5].y;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[5].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MUL R0.xyz, R0, c[5].x;\n"
+ "ADD R2.w, -R0, c[5].y;\n"
+ "MAD R3.xyz, R3, c[5].x, R4;\n"
+ "MAD R3.xyz, R1, R2.w, R3;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R1.xyz, R1, -R3;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD result.color.xyz, R0, R1, R3;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SOFTLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..4],\n"
+ " { 1, 9.9999997e-06, 2, 8 },\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R2.xyz, fragment.position.y, c[1];\n"
+ "MAD R3.xyz, fragment.position.x, c[0], R2;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R1, R0, texture[0], 2D;\n"
+ "MAX R0.x, R1.w, c[5].y;\n"
+ "RCP R2.w, R0.x;\n"
+ "MUL R0.xyz, R1, R2.w;\n"
+ "RSQ R0.w, R0.x;\n"
+ "RSQ R2.y, R0.y;\n"
+ "ADD R3.xyz, R3, c[2];\n"
+ "RCP R2.x, R0.w;\n"
+ "RCP R0.w, R3.z;\n"
+ "MUL R3.xy, R3, R0.w;\n"
+ "RSQ R0.w, R0.z;\n"
+ "RCP R2.z, R0.w;\n"
+ "RCP R2.y, R2.y;\n"
+ "MAD R6.xyz, R2, R1.w, -R1;\n"
+ "MUL R2.xyz, -R0, c[5].w;\n"
+ "ADD R5.xyz, R2, c[6].x;\n"
+ "MAD R2.xyz, -R1, R2.w, c[5].x;\n"
+ "MUL R3.xy, R3, c[3];\n"
+ "ADD R0.w, R3.x, R3.y;\n"
+ "MUL R0.w, R0, c[3].z;\n"
+ "TEX R0, R0.w, texture[1], 1D;\n"
+ "MAD R3.xyz, R0, c[5].z, -R0.w;\n"
+ "MUL R4.xyz, R2, R3;\n"
+ "MAD R4.xyz, -R4, R5, R0.w;\n"
+ "MUL R5.xyz, R6, R3;\n"
+ "MAD R2.xyz, -R2, R3, R0.w;\n"
+ "MAD R6.xyz, R1, R0.w, R5;\n"
+ "MUL R4.xyz, R1, R4;\n"
+ "MUL R5.xyz, R1, c[5].w;\n"
+ "ADD R6.xyz, R6, -R4;\n"
+ "SGE R5.xyz, R5, R1.w;\n"
+ "MUL R5.xyz, R5, R6;\n"
+ "ADD R3.xyz, R4, R5;\n"
+ "MUL R2.xyz, R1, R2;\n"
+ "MUL R4.xyz, R0, c[5].z;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "SGE R4.xyz, R4, R0.w;\n"
+ "MUL R3.xyz, R4, R3;\n"
+ "ADD R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DIFFERENCE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[3].z;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "ADD R0.xyz, R0, R1;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "MAD result.color.xyz, -R2, c[5].x, R0;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_EXCLUSION_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[3].z;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[1], 1D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[5].y, R3;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODE_BLEND_MODE_MASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.z;\n"
+ "MUL R0.zw, R0, c[3].xyxy;\n"
+ "ADD R1.x, R0.z, R0.w;\n"
+ "ADD R0.xy, fragment.position, c[4];\n"
+ "MUL R0.xy, R0, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "DP4 R1.y, R0, c[6];\n"
+ "MUL R1.x, R1, c[3].z;\n"
+ "TEX R0, R1, texture[1], 1D;\n"
+ "MUL result.color, R0, R1.y;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODE_BLEND_MODE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[4] = { program.local[0..3] };\n"
+ "TEMP R0;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "ADD R0.x, R0, R0.y;\n"
+ "MUL R0.x, R0, c[3].z;\n"
+ "TEX result.color, R0, texture[0], 1D;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SIMPLE_PORTER_DUFF =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 1 },\n"
+ " program.local[9..10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MUL R2.xyz, R1, c[10].y;\n"
+ "MUL R3.xyz, R2, R0.w;\n"
+ "MUL R2.xyz, R0, c[10].x;\n"
+ "MAD R2.xyz, R2, R1.w, R3;\n"
+ "ADD R3.xy, fragment.position, c[0];\n"
+ "MUL R0.xyz, R0, c[9].y;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MUL R0.xyz, R1, c[9].z;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R0.y, -R1.w, c[8].x;\n"
+ "MUL R0.z, R1.w, R2.w;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.y, R0.w, R0;\n"
+ "DP3 R2.w, R0, c[9];\n"
+ "MUL R3.xy, R3, c[1];\n"
+ "TEX R0, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_MULTIPLY =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R1.x, R0.z;\n"
+ "MUL R0.xy, R0, R1.x;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SCREEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "ADD R3.xy, fragment.position, c[0];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "ADD R2, R0, R1;\n"
+ "MAD R2, -R0, R1, R2;\n"
+ "MUL R3.xy, R3, c[1];\n"
+ "TEX R0, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_OVERLAY =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[8].y;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[8].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8].y;\n"
+ "MAD R3.xyz, R3, c[8].x, R4;\n"
+ "MAD R3.xyz, R1, R2.x, R3;\n"
+ "MAD R0.xyz, R1, R2.x, R0;\n"
+ "MUL R2.xyz, R1, c[8].x;\n"
+ "ADD R0.xyz, R0, -R3;\n"
+ "SGE R2.xyz, R2, R1.w;\n"
+ "MAD R2.xyz, R2, R0, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DARKEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_LIGHTEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORDODGE =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 1e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MAX R1.x, R0.w, c[8].y;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R1.xyz, -R0, R1.x, c[8].x;\n"
+ "MAX R2.xyz, R1, c[8].y;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R4.xyz, R0, R2.w, R3;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.w, R0, R1;\n"
+ "MAD R0.xyz, R0, R1.w, R3;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R3, R2, R4;\n"
+ "MAD R4.xyz, R0.w, R1.w, R4;\n"
+ "ADD R4.xyz, R4, -R2;\n"
+ "MAD R2.xyz, R0, R4, R2;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORBURN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 9.9999997e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[8].y;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[8].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "MUL R2.w, R0, R1;\n"
+ "ADD R3.w, -R0, c[8].x;\n"
+ "MAD R2.xyz, R1, R3.w, R2;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R3.xyz, R3, R2.w;\n"
+ "MAD R2.xyz, R3, R2, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_HARDLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[8].y;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[8].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[8].y;\n"
+ "MAD R3.xyz, R3, c[8].x, R4;\n"
+ "MUL R0.xyz, R0, c[8].x;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD R3.xyz, R1, R2.w, R3;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MAD R2.xyz, R0, R2, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SOFTLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..7],\n"
+ " { 1, 9.9999997e-06, 2, 8 },\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MAX R0.z, R1.w, c[8].y;\n"
+ "RCP R2.w, R0.z;\n"
+ "MUL R2.xyz, R1, R2.w;\n"
+ "MUL R6.xyz, -R2, c[8].w;\n"
+ "MAD R3.xyz, -R1, R2.w, c[8].x;\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MAD R4.xyz, R0, c[8].z, -R0.w;\n"
+ "MUL R5.xyz, R3, R4;\n"
+ "MAD R3.xyz, -R3, R4, R0.w;\n"
+ "ADD R6.xyz, R6, c[9].x;\n"
+ "RSQ R2.x, R2.x;\n"
+ "RSQ R2.z, R2.z;\n"
+ "RSQ R2.y, R2.y;\n"
+ "MAD R5.xyz, -R5, R6, R0.w;\n"
+ "MUL R3.xyz, R1, R3;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.z, R2.z;\n"
+ "RCP R2.y, R2.y;\n"
+ "MAD R2.xyz, R2, R1.w, -R1;\n"
+ "MUL R6.xyz, R2, R4;\n"
+ "MUL R2.xyz, R1, R5;\n"
+ "MAD R6.xyz, R1, R0.w, R6;\n"
+ "MUL R4.xyz, R0, c[8].z;\n"
+ "MUL R5.xyz, R1, c[8].w;\n"
+ "ADD R6.xyz, R6, -R2;\n"
+ "SGE R5.xyz, R5, R1.w;\n"
+ "MUL R5.xyz, R5, R6;\n"
+ "ADD R2.xyz, R2, R5;\n"
+ "SGE R4.xyz, R4, R0.w;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MUL R2.xyz, R4, R2;\n"
+ "ADD R2.xyz, R3, R2;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DIFFERENCE =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "ADD R3.xyz, R0, R1;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MUL R0.xyz, R0, R1.w;\n"
+ "MIN R0.xyz, R0, R2;\n"
+ "MAD R2.xyz, -R0, c[8].x, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_EXCLUSION =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[2], 2D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[8].y, R3;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 1 },\n"
+ " program.local[6..7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, c[7].y;\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R3.xyz, R2, R0.w;\n"
+ "MUL R2.xyz, R0, c[7].x;\n"
+ "MAD R2.xyz, R2, R1.w, R3;\n"
+ "MUL R0.xyz, R0, c[6].y;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[5];\n"
+ "MUL R1.xyz, R1, c[6].z;\n"
+ "MAD result.color.xyz, R1, R2.x, R0;\n"
+ "ADD R0.y, -R1.w, c[5].x;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.z, R1.w, R2.x;\n"
+ "MUL R0.y, R0.w, R0;\n"
+ "DP3 result.color.w, R0, c[6];\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_MULTIPLY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R1.x, R0.z;\n"
+ "MUL R0.xy, R0, R1.x;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2.x, -R1.w, c[5];\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SCREEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..4] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[4].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R0, R1;\n"
+ "MAD result.color, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_OVERLAY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].y;\n"
+ "MUL R2.xyz, R2, c[5].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "MUL R0.xyz, R0, R1;\n"
+ "ADD R2.w, -R0, c[5].y;\n"
+ "MAD R0.xyz, R0, c[5].x, R3;\n"
+ "MAD R0.xyz, R1, R2.w, R0;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R1.xyz, R1, c[5].x;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R1.xyz, R1, R1.w;\n"
+ "MAD result.color.xyz, R1, R2, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DARKEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_LIGHTEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORDODGE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 1e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MAX R1.x, R0.w, c[5].y;\n"
+ "RCP R1.x, R1.x;\n"
+ "MAD R1.xyz, -R0, R1.x, c[5].x;\n"
+ "MAX R2.xyz, R1, c[5].y;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R3.xyz, R0, R2.w, R3;\n"
+ "MUL R1.xyz, R1, R0.w;\n"
+ "MAD R0.xyz, R0, R1.w, R1;\n"
+ "MUL R2.w, R0, R1;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R1, R2, R3;\n"
+ "MAD R3.xyz, R0.w, R1.w, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "MAD result.color.xyz, R0, R3, R2;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORBURN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 9.9999997e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R2.xyz, -R0.w, R1.w, R3;\n"
+ "MUL R4.xyz, R0.w, R2;\n"
+ "MAX R2.xyz, R0, c[5].y;\n"
+ "MUL R5.xyz, R0, R2.w;\n"
+ "ADD R3.w, -R0, c[5].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R1, R3.w;\n"
+ "MAD R0.xyz, R0, R2.w, R4;\n"
+ "ADD R3.w, -R0, c[5].x;\n"
+ "MAD R1.xyz, R1, R3.w, R2;\n"
+ "MUL R2.x, R0.w, R1.w;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R1.xyz, R1, -R0;\n"
+ "SGE R2.xyz, R3, R2.x;\n"
+ "MAD result.color.xyz, R2, R1, R0;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_HARDLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[5].y;\n"
+ "ADD R3.xyz, R0.w, -R0;\n"
+ "ADD R2.xyz, R1.w, -R1;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[5].x;\n"
+ "MAD R2.xyz, R0.w, R1.w, -R2;\n"
+ "MUL R4.xyz, R0, R2.w;\n"
+ "MUL R3.xyz, R0, R1;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R2.w, -R0, c[5].y;\n"
+ "MUL R0.xyz, R0, c[5].x;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MAD R3.xyz, R3, c[5].x, R4;\n"
+ "MAD R1.xyz, R1, R2.w, R3;\n"
+ "ADD R2.w, R0, R1;\n"
+ "ADD R2.xyz, R2, -R1;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD result.color.xyz, R0, R2, R1;\n"
+ "MAD result.color.w, -R0, R1, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SOFTLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..4],\n"
+ " { 1, 9.9999997e-06, 2, 8 },\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "TEMP R6;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MAX R0.z, R1.w, c[5].y;\n"
+ "RCP R2.w, R0.z;\n"
+ "MUL R2.xyz, R1, R2.w;\n"
+ "MUL R6.xyz, -R2, c[5].w;\n"
+ "MAD R3.xyz, -R1, R2.w, c[5].x;\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MAD R4.xyz, R0, c[5].z, -R0.w;\n"
+ "MUL R5.xyz, R3, R4;\n"
+ "MAD R3.xyz, -R3, R4, R0.w;\n"
+ "ADD R6.xyz, R6, c[6].x;\n"
+ "RSQ R2.x, R2.x;\n"
+ "RSQ R2.z, R2.z;\n"
+ "RSQ R2.y, R2.y;\n"
+ "MAD R5.xyz, -R5, R6, R0.w;\n"
+ "MUL R3.xyz, R1, R3;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.z, R2.z;\n"
+ "RCP R2.y, R2.y;\n"
+ "MAD R2.xyz, R2, R1.w, -R1;\n"
+ "MUL R6.xyz, R2, R4;\n"
+ "MUL R2.xyz, R1, R5;\n"
+ "MUL R4.xyz, R0, c[5].z;\n"
+ "MAD R6.xyz, R1, R0.w, R6;\n"
+ "MUL R5.xyz, R1, c[5].w;\n"
+ "ADD R6.xyz, R6, -R2;\n"
+ "SGE R5.xyz, R5, R1.w;\n"
+ "MUL R5.xyz, R5, R6;\n"
+ "ADD R2.xyz, R2, R5;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "SGE R4.xyz, R4, R0.w;\n"
+ "MUL R2.xyz, R4, R2;\n"
+ "ADD R2.xyz, R3, R2;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DIFFERENCE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "ADD R0.xyz, R0, R1;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R1.x, R0.w, R1.w;\n"
+ "MAD result.color.xyz, -R2, c[5].x, R0;\n"
+ "MAD result.color.w, -R0, R1, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_EXCLUSION_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "MUL R1.xy, fragment.position, c[4];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[5].y, R3;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, R0.w, R1.w;\n"
+ "ADD R2.y, -R0.w, c[5].x;\n"
+ "MAD result.color.xyz, R1, R2.y, R0;\n"
+ "MAD result.color.w, -R0, R1, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODE_BLEND_MODE_MASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R1.xyz, R0, c[2];\n"
+ "RCP R0.z, R1.z;\n"
+ "MUL R1.xy, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[4];\n"
+ "MUL R0.xy, R0, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "DP4 R1.z, R0, c[6];\n"
+ "MUL R1.xy, R1, c[3];\n"
+ "MOV R0.x, R1;\n"
+ "MOV R0.y, -R1;\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "MUL result.color, R0, R1.z;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODE_BLEND_MODE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[4] = { program.local[0..3] };\n"
+ "TEMP R0;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX result.color, R0, texture[0], 2D;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SIMPLE_PORTER_DUFF =
+ "!!ARBfp1.0\n"
+ "PARAM c[11] = { program.local[0..7],\n"
+ " { 1 },\n"
+ " program.local[9..10] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "TEX R0.x, R0, texture[2], 2D;\n"
+ "MUL R0, fragment.color.primary, R0.x;\n"
+ "MUL R2.xyz, R1, c[10].y;\n"
+ "MUL R3.xyz, R2, R0.w;\n"
+ "MUL R2.xyz, R0, c[10].x;\n"
+ "MAD R2.xyz, R2, R1.w, R3;\n"
+ "ADD R3.xy, fragment.position, c[0];\n"
+ "MUL R0.xyz, R0, c[9].y;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MUL R0.xyz, R1, c[9].z;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R0.y, -R1.w, c[8].x;\n"
+ "MUL R0.z, R1.w, R2.w;\n"
+ "MUL R0.x, R0.w, R1.w;\n"
+ "MUL R0.y, R0.w, R0;\n"
+ "DP3 R2.w, R0, c[9];\n"
+ "MUL R3.xy, R3, c[1];\n"
+ "TEX R0, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_MULTIPLY =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0.x, R0, texture[2], 2D;\n"
+ "MUL R0, fragment.color.primary, R0.x;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MUL R2.xyz, R0, R2.x;\n"
+ "MAD R0.xyz, R0, R1, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SCREEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R1.x, R0, texture[2], 2D;\n"
+ "MUL R0.xy, fragment.position, c[7];\n"
+ "ADD R3.xy, fragment.position, c[0];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "ADD R2, R1, R0;\n"
+ "MAD R2, -R1, R0, R2;\n"
+ "MUL R3.xy, R3, c[1];\n"
+ "TEX R1, R3, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[2];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_OVERLAY =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R1.x, R0, texture[2], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R0.xy, fragment.position, c[7];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[8].y;\n"
+ "ADD R3.xyz, R1.w, -R1;\n"
+ "ADD R2.xyz, R0.w, -R0;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[8].x;\n"
+ "MAD R2.xyz, R1.w, R0.w, -R2;\n"
+ "MUL R4.xyz, R1, R2.w;\n"
+ "MUL R3.xyz, R1, R0;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[8].y;\n"
+ "MAD R3.xyz, R3, c[8].x, R4;\n"
+ "MAD R3.xyz, R0, R2.x, R3;\n"
+ "MAD R1.xyz, R0, R2.x, R1;\n"
+ "MUL R2.xyz, R0, c[8].x;\n"
+ "ADD R1.xyz, R1, -R3;\n"
+ "SGE R2.xyz, R2, R0.w;\n"
+ "MAD R2.xyz, R2, R1, R3;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[0];\n"
+ "MUL R1.xy, R1, c[1];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[2];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DARKEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0.x, R0, texture[2], 2D;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R0, fragment.color.primary, R0.x;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_LIGHTEN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0.x, R0, texture[2], 2D;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R0, fragment.color.primary, R0.x;\n"
+ "MUL R3.xyz, R1, R0.w;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORDODGE =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 1e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0.x, R0, texture[2], 2D;\n"
+ "MUL R1, fragment.color.primary, R0.x;\n"
+ "MAX R0.x, R1.w, c[8].y;\n"
+ "RCP R0.x, R0.x;\n"
+ "MAD R0.xyz, -R1, R0.x, c[8].x;\n"
+ "MAX R2.xyz, R0, c[8].y;\n"
+ "MUL R0.xy, fragment.position, c[7];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MAD R4.xyz, R1, R2.w, R3;\n"
+ "MUL R3.xyz, R0, R1.w;\n"
+ "MUL R2.w, R1, R0;\n"
+ "MAD R1.xyz, R1, R0.w, R3;\n"
+ "SGE R1.xyz, R1, R2.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R3, R2, R4;\n"
+ "MAD R4.xyz, R1.w, R0.w, R4;\n"
+ "ADD R4.xyz, R4, -R2;\n"
+ "MAD R2.xyz, R1, R4, R2;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[0];\n"
+ "MUL R1.xy, R1, c[1];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[2];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORBURN =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 9.9999997e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R1.x, R0, texture[2], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R0.xy, fragment.position, c[7];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MAD R3.xyz, R1, R0.w, R2;\n"
+ "MAD R2.xyz, -R1.w, R0.w, R3;\n"
+ "MUL R4.xyz, R1.w, R2;\n"
+ "MAX R2.xyz, R1, c[8].y;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "MUL R5.xyz, R1, R2.w;\n"
+ "ADD R3.w, -R1, c[8].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R0, R3.w;\n"
+ "MAD R1.xyz, R1, R2.w, R4;\n"
+ "MUL R2.w, R1, R0;\n"
+ "ADD R3.w, -R1, c[8].x;\n"
+ "MAD R2.xyz, R0, R3.w, R2;\n"
+ "ADD R2.xyz, R2, -R1;\n"
+ "SGE R3.xyz, R3, R2.w;\n"
+ "MAD R2.xyz, R3, R2, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[0];\n"
+ "MUL R1.xy, R1, c[1];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[2];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_HARDLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R1.x, R0, texture[2], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R0.xy, fragment.position, c[7];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[8].y;\n"
+ "ADD R3.xyz, R1.w, -R1;\n"
+ "ADD R2.xyz, R0.w, -R0;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[8].x;\n"
+ "MAD R2.xyz, R1.w, R0.w, -R2;\n"
+ "MUL R4.xyz, R1, R2.w;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R3.xyz, R1, R0;\n"
+ "ADD R2.w, -R1, c[8].y;\n"
+ "MAD R3.xyz, R3, c[8].x, R4;\n"
+ "MUL R1.xyz, R1, c[8].x;\n"
+ "SGE R1.xyz, R1, R1.w;\n"
+ "MAD R3.xyz, R0, R2.w, R3;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MAD R2.xyz, R1, R2, R3;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[0];\n"
+ "MUL R1.xy, R1, c[1];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[2];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SOFTLIGHT =
+ "!!ARBfp1.0\n"
+ "PARAM c[10] = { program.local[0..7],\n"
+ " { 1, 9.9999997e-06, 2, 8 },\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R1.xyz, R0, c[5];\n"
+ "RCP R0.z, R1.z;\n"
+ "MUL R1.xy, R1, R0.z;\n"
+ "MUL R1.xy, R1, c[6];\n"
+ "MOV R1.y, -R1;\n"
+ "MUL R0.xy, fragment.position, c[7];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MAX R1.z, R0.w, c[8].y;\n"
+ "RCP R2.w, R1.z;\n"
+ "MUL R2.xyz, R0, R2.w;\n"
+ "MUL R5.xyz, -R2, c[8].w;\n"
+ "MAD R4.xyz, -R0, R2.w, c[8].x;\n"
+ "TEX R1.x, R1, texture[2], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MAD R3.xyz, R1, c[8].z, -R1.w;\n"
+ "RSQ R2.x, R2.x;\n"
+ "RSQ R2.z, R2.z;\n"
+ "RSQ R2.y, R2.y;\n"
+ "MUL R4.xyz, R4, R3;\n"
+ "ADD R5.xyz, R5, c[9].x;\n"
+ "MUL R5.xyz, R4, R5;\n"
+ "ADD R4.xyz, R1.w, -R4;\n"
+ "ADD R2.w, -R0, c[8].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.z, R2.z;\n"
+ "RCP R2.y, R2.y;\n"
+ "MAD R2.xyz, R2, R0.w, -R0;\n"
+ "MUL R3.xyz, R2, R3;\n"
+ "ADD R2.xyz, R1.w, -R5;\n"
+ "MAD R5.xyz, R0, R1.w, R3;\n"
+ "MUL R2.xyz, R0, R2;\n"
+ "MUL R3.xyz, R0, c[8].w;\n"
+ "ADD R5.xyz, R5, -R2;\n"
+ "SGE R3.xyz, R3, R0.w;\n"
+ "MUL R3.xyz, R3, R5;\n"
+ "ADD R2.xyz, R2, R3;\n"
+ "MUL R3.xyz, R0, R4;\n"
+ "MUL R4.xyz, R1, c[8].z;\n"
+ "SGE R4.xyz, R4, R1.w;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "MUL R2.xyz, R4, R2;\n"
+ "ADD R2.xyz, R3, R2;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[8];\n"
+ "MAD R2.xyz, R0, R2.x, R1;\n"
+ "ADD R1.z, R1.w, R0.w;\n"
+ "MAD R2.w, -R1, R0, R1.z;\n"
+ "ADD R1.xy, fragment.position, c[0];\n"
+ "MUL R1.xy, R1, c[1];\n"
+ "TEX R1, R1, texture[1], 2D;\n"
+ "ADD R2, R2, -R0;\n"
+ "DP4 R1.x, R1, c[2];\n"
+ "MAD result.color, R1.x, R2, R0;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DIFFERENCE =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "MUL R0.zw, fragment.position.xyxy, c[7].xyxy;\n"
+ "TEX R1, R0.zwzw, texture[0], 2D;\n"
+ "TEX R0.x, R0, texture[2], 2D;\n"
+ "MUL R0, fragment.color.primary, R0.x;\n"
+ "ADD R3.xyz, R0, R1;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MUL R0.xyz, R0, R1.w;\n"
+ "MIN R0.xyz, R0, R2;\n"
+ "MAD R2.xyz, -R0, c[8].x, R3;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_EXCLUSION =
+ "!!ARBfp1.0\n"
+ "PARAM c[9] = { program.local[0..7],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[4];\n"
+ "MAD R0.xyz, fragment.position.x, c[3], R0;\n"
+ "ADD R0.xyz, R0, c[5];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[6];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0.x, R0, texture[2], 2D;\n"
+ "MUL R1.xy, fragment.position, c[7];\n"
+ "TEX R1, R1, texture[0], 2D;\n"
+ "MUL R0, fragment.color.primary, R0.x;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MAD R3.xyz, R0, R1.w, R2;\n"
+ "MUL R2.xyz, R0, R1;\n"
+ "MAD R2.xyz, -R2, c[8].y, R3;\n"
+ "ADD R2.w, -R1, c[8].x;\n"
+ "MAD R0.xyz, R0, R2.w, R2;\n"
+ "ADD R2.x, -R0.w, c[8];\n"
+ "MAD R2.xyz, R1, R2.x, R0;\n"
+ "ADD R0.z, R0.w, R1.w;\n"
+ "MAD R2.w, -R0, R1, R0.z;\n"
+ "ADD R0.xy, fragment.position, c[0];\n"
+ "MUL R0.xy, R0, c[1];\n"
+ "TEX R0, R0, texture[1], 2D;\n"
+ "ADD R2, R2, -R1;\n"
+ "DP4 R0.x, R0, c[2];\n"
+ "MAD result.color, R0.x, R2, R1;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[8] = { program.local[0..4],\n"
+ " { 1 },\n"
+ " program.local[6..7] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.w, -R0.y;\n"
+ "MOV R0.z, R0.x;\n"
+ "TEX R1.x, R0.zwzw, texture[1], 2D;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, R0, c[7].y;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R3.xyz, R2, R1.w;\n"
+ "MUL R2.xyz, R1, c[7].x;\n"
+ "MUL R0.xyz, R0, c[6].z;\n"
+ "MAD R2.xyz, R2, R0.w, R3;\n"
+ "MUL R1.xyz, R1, c[6].y;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, -R1.w, c[5];\n"
+ "MAD result.color.xyz, R0, R2.x, R1;\n"
+ "ADD R0.y, -R0.w, c[5].x;\n"
+ "MUL R0.x, R1.w, R0.w;\n"
+ "MUL R0.z, R0.w, R2.x;\n"
+ "MUL R0.y, R1.w, R0;\n"
+ "DP3 result.color.w, R0, c[6];\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_MULTIPLY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.zw, R0.xyxy, R0.z;\n"
+ "MUL R1.xy, R0.zwzw, c[3];\n"
+ "MOV R1.y, -R1;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1.x, R1, texture[1], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "ADD R2.x, -R0.w, c[5];\n"
+ "MUL R2.xyz, R1, R2.x;\n"
+ "MAD R1.xyz, R1, R0, R2;\n"
+ "ADD R2.x, R1.w, R0.w;\n"
+ "ADD R2.y, -R1.w, c[5].x;\n"
+ "MAD result.color.xyz, R0, R2.y, R1;\n"
+ "MAD result.color.w, -R1, R0, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SCREEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[5] = { program.local[0..4] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.w, -R0.y;\n"
+ "MOV R0.z, R0.x;\n"
+ "TEX R1.x, R0.zwzw, texture[1], 2D;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "ADD R2, R1, R0;\n"
+ "MAD result.color, -R1, R0, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_OVERLAY_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R1.x, R0, texture[1], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R3.xyz, R1.w, -R1;\n"
+ "ADD R2.xyz, R0.w, -R0;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[5].y;\n"
+ "MUL R2.xyz, R2, c[5].x;\n"
+ "MAD R2.xyz, R1.w, R0.w, -R2;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "MUL R3.xyz, R1, R2.w;\n"
+ "MUL R1.xyz, R1, R0;\n"
+ "ADD R2.w, -R1, c[5].y;\n"
+ "MAD R1.xyz, R1, c[5].x, R3;\n"
+ "MAD R1.xyz, R0, R2.w, R1;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MUL R0.xyz, R0, c[5].x;\n"
+ "ADD R2.w, R1, R0;\n"
+ "ADD R2.xyz, R2, -R1;\n"
+ "SGE R0.xyz, R0, R0.w;\n"
+ "MAD result.color.xyz, R0, R2, R1;\n"
+ "MAD result.color.w, -R1, R0, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DARKEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R1.x, R0, texture[1], 2D;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MUL R3.xyz, R0, R1.w;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, R1.w, R0.w;\n"
+ "ADD R2.y, -R1.w, c[5].x;\n"
+ "MAD result.color.xyz, R0, R2.y, R1;\n"
+ "MAD result.color.w, -R1, R0, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_LIGHTEN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R1.x, R0, texture[1], 2D;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "MUL R3.xyz, R0, R1.w;\n"
+ "MAX R2.xyz, R2, R3;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, R1.w, R0.w;\n"
+ "ADD R2.y, -R1.w, c[5].x;\n"
+ "MAD result.color.xyz, R0, R2.y, R1;\n"
+ "MAD result.color.w, -R1, R0, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORDODGE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 1e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0.x, R0, texture[1], 2D;\n"
+ "MUL R1, fragment.color.primary, R0.x;\n"
+ "MAX R0.x, R1.w, c[5].y;\n"
+ "RCP R0.x, R0.x;\n"
+ "MAD R0.xyz, -R1, R0.x, c[5].x;\n"
+ "MAX R2.xyz, R0, c[5].y;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R2.w, -R1, c[5].x;\n"
+ "MUL R3.xyz, R0, R2.w;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R3.xyz, R1, R2.w, R3;\n"
+ "MUL R0.xyz, R0, R1.w;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R0, R2, R3;\n"
+ "MAD R0.xyz, R1, R0.w, R0;\n"
+ "MAD R3.xyz, R1.w, R0.w, R3;\n"
+ "MUL R2.w, R1, R0;\n"
+ "ADD R1.x, R1.w, R0.w;\n"
+ "ADD R3.xyz, R3, -R2;\n"
+ "SGE R0.xyz, R0, R2.w;\n"
+ "MAD result.color.xyz, R0, R3, R2;\n"
+ "MAD result.color.w, -R1, R0, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORBURN_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 9.9999997e-06 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R1.x, R0, texture[1], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MAD R3.xyz, R1, R0.w, R2;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R2.xyz, -R1.w, R0.w, R3;\n"
+ "MUL R4.xyz, R1.w, R2;\n"
+ "MAX R2.xyz, R1, c[5].y;\n"
+ "MUL R5.xyz, R1, R2.w;\n"
+ "ADD R3.w, -R1, c[5].x;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.y, R2.y;\n"
+ "RCP R2.z, R2.z;\n"
+ "MAD R2.xyz, R4, R2, R5;\n"
+ "MUL R4.xyz, R0, R3.w;\n"
+ "MAD R1.xyz, R1, R2.w, R4;\n"
+ "ADD R3.w, -R1, c[5].x;\n"
+ "MAD R0.xyz, R0, R3.w, R2;\n"
+ "MUL R2.x, R1.w, R0.w;\n"
+ "ADD R2.w, R1, R0;\n"
+ "ADD R0.xyz, R0, -R1;\n"
+ "SGE R2.xyz, R3, R2.x;\n"
+ "MAD result.color.xyz, R2, R0, R1;\n"
+ "MAD result.color.w, -R1, R0, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_HARDLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2, 1 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R1.x, R0, texture[1], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "ADD R2.w, -R0, c[5].y;\n"
+ "ADD R3.xyz, R1.w, -R1;\n"
+ "ADD R2.xyz, R0.w, -R0;\n"
+ "MUL R2.xyz, R2, R3;\n"
+ "MUL R2.xyz, R2, c[5].x;\n"
+ "MAD R2.xyz, R1.w, R0.w, -R2;\n"
+ "MUL R4.xyz, R1, R2.w;\n"
+ "MUL R3.xyz, R1, R0;\n"
+ "MAD R2.xyz, R1, R2.w, R2;\n"
+ "ADD R2.w, -R1, c[5].y;\n"
+ "MUL R1.xyz, R1, c[5].x;\n"
+ "MAD R2.xyz, R0, R2.w, R2;\n"
+ "MAD R3.xyz, R3, c[5].x, R4;\n"
+ "MAD R0.xyz, R0, R2.w, R3;\n"
+ "ADD R2.w, R1, R0;\n"
+ "ADD R2.xyz, R2, -R0;\n"
+ "SGE R1.xyz, R1, R1.w;\n"
+ "MAD result.color.xyz, R1, R2, R0;\n"
+ "MAD result.color.w, -R1, R0, R2;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SOFTLIGHT_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..4],\n"
+ " { 1, 9.9999997e-06, 2, 8 },\n"
+ " { 3 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "TEMP R4;\n"
+ "TEMP R5;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R1.xyz, R0, c[2];\n"
+ "RCP R0.z, R1.z;\n"
+ "MUL R1.xy, R1, R0.z;\n"
+ "MUL R1.xy, R1, c[3];\n"
+ "MOV R1.y, -R1;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MAX R1.z, R0.w, c[5].y;\n"
+ "RCP R2.w, R1.z;\n"
+ "MUL R2.xyz, R0, R2.w;\n"
+ "MUL R5.xyz, -R2, c[5].w;\n"
+ "MAD R4.xyz, -R0, R2.w, c[5].x;\n"
+ "TEX R1.x, R1, texture[1], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MAD R3.xyz, R1, c[5].z, -R1.w;\n"
+ "RSQ R2.x, R2.x;\n"
+ "RSQ R2.z, R2.z;\n"
+ "RSQ R2.y, R2.y;\n"
+ "MUL R4.xyz, R4, R3;\n"
+ "ADD R5.xyz, R5, c[6].x;\n"
+ "MUL R5.xyz, R4, R5;\n"
+ "ADD R4.xyz, R1.w, -R4;\n"
+ "RCP R2.x, R2.x;\n"
+ "RCP R2.z, R2.z;\n"
+ "RCP R2.y, R2.y;\n"
+ "MAD R2.xyz, R2, R0.w, -R0;\n"
+ "MUL R3.xyz, R2, R3;\n"
+ "ADD R2.xyz, R1.w, -R5;\n"
+ "MAD R5.xyz, R0, R1.w, R3;\n"
+ "MUL R2.xyz, R0, R2;\n"
+ "MUL R3.xyz, R0, c[5].w;\n"
+ "ADD R5.xyz, R5, -R2;\n"
+ "SGE R3.xyz, R3, R0.w;\n"
+ "MUL R3.xyz, R3, R5;\n"
+ "ADD R2.xyz, R2, R3;\n"
+ "MUL R3.xyz, R0, R4;\n"
+ "MUL R4.xyz, R1, c[5].z;\n"
+ "ADD R2.xyz, R2, -R3;\n"
+ "SGE R4.xyz, R4, R1.w;\n"
+ "MUL R2.xyz, R4, R2;\n"
+ "ADD R2.xyz, R3, R2;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, R1.w, R0.w;\n"
+ "ADD R2.y, -R1.w, c[5].x;\n"
+ "MAD result.color.xyz, R0, R2.y, R1;\n"
+ "MAD result.color.w, -R1, R0, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DIFFERENCE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R1.x, R0, texture[1], 2D;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R3.xyz, R0, R1.w;\n"
+ "MUL R2.xyz, R1, R0.w;\n"
+ "ADD R0.xyz, R1, R0;\n"
+ "MIN R2.xyz, R2, R3;\n"
+ "ADD R1.x, R1.w, R0.w;\n"
+ "MAD result.color.xyz, -R2, c[5].x, R0;\n"
+ "MAD result.color.w, -R1, R0, R1.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_EXCLUSION_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[6] = { program.local[0..4],\n"
+ " { 1, 2 } };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "TEMP R2;\n"
+ "TEMP R3;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R1.x, R0, texture[1], 2D;\n"
+ "MUL R0.xy, fragment.position, c[4];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL R2.xyz, R0, R1.w;\n"
+ "MAD R3.xyz, R1, R0.w, R2;\n"
+ "MUL R2.xyz, R1, R0;\n"
+ "MAD R2.xyz, -R2, c[5].y, R3;\n"
+ "ADD R2.w, -R0, c[5].x;\n"
+ "MAD R1.xyz, R1, R2.w, R2;\n"
+ "ADD R2.x, R1.w, R0.w;\n"
+ "ADD R2.y, -R1.w, c[5].x;\n"
+ "MAD result.color.xyz, R0, R2.y, R1;\n"
+ "MAD result.color.w, -R1, R0, R2.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODE_BLEND_MODE_MASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[7] = { program.local[0..6] };\n"
+ "TEMP R0;\n"
+ "TEMP R1;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R1.xyz, R0, c[2];\n"
+ "RCP R0.z, R1.z;\n"
+ "MUL R0.zw, R1.xyxy, R0.z;\n"
+ "MUL R1.xy, R0.zwzw, c[3];\n"
+ "MOV R1.y, -R1;\n"
+ "ADD R0.xy, fragment.position, c[4];\n"
+ "MUL R0.xy, R0, c[5];\n"
+ "TEX R0, R0, texture[0], 2D;\n"
+ "TEX R1.x, R1, texture[1], 2D;\n"
+ "DP4 R0.x, R0, c[6];\n"
+ "MUL R1, fragment.color.primary, R1.x;\n"
+ "MUL result.color, R1, R0.x;\n"
+ "END\n"
+ ;
+
+static const char *FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODE_BLEND_MODE_NOMASK =
+ "!!ARBfp1.0\n"
+ "PARAM c[4] = { program.local[0..3] };\n"
+ "TEMP R0;\n"
+ "MUL R0.xyz, fragment.position.y, c[1];\n"
+ "MAD R0.xyz, fragment.position.x, c[0], R0;\n"
+ "ADD R0.xyz, R0, c[2];\n"
+ "RCP R0.z, R0.z;\n"
+ "MUL R0.xy, R0, R0.z;\n"
+ "MUL R0.xy, R0, c[3];\n"
+ "MOV R0.y, -R0;\n"
+ "TEX R0.x, R0, texture[0], 2D;\n"
+ "MUL result.color, fragment.color.primary, R0.x;\n"
+ "END\n"
+ ;
+
+static const char *mask_fragment_program_sources[num_fragment_masks] = {
+ FragmentProgram_FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA,
+ FragmentProgram_FRAGMENT_PROGRAM_MASK_ELLIPSE_AA,
+};
+
+static const char *painter_fragment_program_sources[num_fragment_brushes][num_fragment_composition_modes] = {
+ {
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_MULTIPLY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SCREEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_OVERLAY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DARKEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_LIGHTEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORDODGE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORBURN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_HARDLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SOFTLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DIFFERENCE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_EXCLUSION,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_MULTIPLY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SCREEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_OVERLAY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DARKEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_LIGHTEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORDODGE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_COLORBURN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODES_EXCLUSION_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODE_BLEND_MODE_MASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_SOLID_COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ },
+ {
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_MULTIPLY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SCREEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_OVERLAY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DARKEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_LIGHTEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORDODGE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORBURN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_HARDLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SOFTLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DIFFERENCE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_EXCLUSION,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_MULTIPLY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SCREEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_OVERLAY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DARKEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_LIGHTEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORDODGE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_COLORBURN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODES_EXCLUSION_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODE_BLEND_MODE_MASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_RADIAL_COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ },
+ {
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_MULTIPLY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SCREEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_OVERLAY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DARKEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_LIGHTEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORDODGE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORBURN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_HARDLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SOFTLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DIFFERENCE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_EXCLUSION,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_MULTIPLY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SCREEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_OVERLAY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DARKEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_LIGHTEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORDODGE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_COLORBURN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODES_EXCLUSION_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODE_BLEND_MODE_MASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_CONICAL_COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ },
+ {
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_MULTIPLY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SCREEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_OVERLAY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DARKEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_LIGHTEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORDODGE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORBURN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_HARDLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SOFTLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DIFFERENCE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_EXCLUSION,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_MULTIPLY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SCREEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_OVERLAY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DARKEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_LIGHTEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORDODGE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_COLORBURN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODES_EXCLUSION_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODE_BLEND_MODE_MASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_LINEAR_COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ },
+ {
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_MULTIPLY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SCREEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_OVERLAY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DARKEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_LIGHTEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORDODGE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORBURN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_HARDLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SOFTLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DIFFERENCE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_EXCLUSION,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_MULTIPLY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SCREEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_OVERLAY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DARKEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_LIGHTEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORDODGE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_COLORBURN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODES_EXCLUSION_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODE_BLEND_MODE_MASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_TEXTURE_COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ },
+ {
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SIMPLE_PORTER_DUFF,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_MULTIPLY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SCREEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_OVERLAY,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DARKEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_LIGHTEN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORDODGE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORBURN,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_HARDLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SOFTLIGHT,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DIFFERENCE,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_EXCLUSION,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_MULTIPLY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SCREEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_OVERLAY_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DARKEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_LIGHTEN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORDODGE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_COLORBURN_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_HARDLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_SOFTLIGHT_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_DIFFERENCE_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODES_EXCLUSION_NOMASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODE_BLEND_MODE_MASK,
+ FragmentProgram_FRAGMENT_PROGRAM_BRUSH_PATTERN_COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ },
+};
+
+static int painter_variable_locations[num_fragment_brushes][num_fragment_composition_modes][num_fragment_variables] = {
+ {
+ { -1, -1, -1, 1, -1, 6, 2, -1, 5, 3, 1, 0, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 1, -1, -1, 2, -1, -1, 3, 1, 0, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 1, -1, -1, 2, -1, -1, 3, 1, 0, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 1, -1, -1, 2, -1, -1, 3, 1, 0, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 1, -1, -1, 2, -1, -1, 3, 1, 0, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 1, -1, -1, 2, -1, -1, 3, 1, 0, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 1, -1, -1, 2, -1, -1, 3, 1, 0, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 1, -1, -1, 2, -1, -1, 3, 1, 0, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 1, -1, -1, 2, -1, -1, 3, 1, 0, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 1, -1, -1, 2, -1, -1, 3, 1, 0, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 1, -1, -1, 2, -1, -1, 3, 1, 0, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 1, -1, -1, 2, -1, -1, 3, 1, 0, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, 3, -1, -1, 2, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, 1, -1, -1, 2, -1, -1, -1, 0, -1, -1, 0, -1, -1, -1, -1, -1, },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ },
+ {
+ { -1, -1, 4, 1, 5, 11, 2, -1, 10, 9, 1, 0, 2, 0, -1, 8, 6, 3, -1, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 9, 1, 0, 2, 0, -1, 8, 6, 3, -1, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 9, 1, 0, 2, 0, -1, 8, 6, 3, -1, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 9, 1, 0, 2, 0, -1, 8, 6, 3, -1, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 9, 1, 0, 2, 0, -1, 8, 6, 3, -1, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 9, 1, 0, 2, 0, -1, 8, 6, 3, -1, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 9, 1, 0, 2, 0, -1, 8, 6, 3, -1, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 9, 1, 0, 2, 0, -1, 8, 6, 3, -1, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 9, 1, 0, 2, 0, -1, 8, 6, 3, -1, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 9, 1, 0, 2, 0, -1, 8, 6, 3, -1, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 9, 1, 0, 2, 0, -1, 8, 6, 3, -1, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 9, 1, 0, 2, 0, -1, 8, 6, 3, -1, },
+ { -1, -1, 1, -1, 2, 8, -1, -1, 7, 6, -1, 0, 1, -1, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 6, -1, 0, 1, -1, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 6, -1, 0, 1, -1, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 6, -1, 0, 1, -1, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 6, -1, 0, 1, -1, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 6, -1, 0, 1, -1, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 6, -1, 0, 1, -1, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 6, -1, 0, 1, -1, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 6, -1, 0, 1, -1, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 6, -1, 0, 1, -1, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 6, -1, 0, 1, -1, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 6, -1, 0, 1, -1, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, 7, 2, -1, 8, -1, -1, -1, 0, -1, 1, 6, -1, 5, 3, 0, -1, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, 5, 3, 0, -1, },
+ },
+ {
+ { -1, -1, 4, 1, 5, 12, 2, -1, 11, 10, 1, 0, 2, 0, -1, -1, -1, 3, 8, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 10, 1, 0, 2, 0, -1, -1, -1, 3, 8, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 10, 1, 0, 2, 0, -1, -1, -1, 3, 8, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 10, 1, 0, 2, 0, -1, -1, -1, 3, 8, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 10, 1, 0, 2, 0, -1, -1, -1, 3, 8, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 10, 1, 0, 2, 0, -1, -1, -1, 3, 8, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 10, 1, 0, 2, 0, -1, -1, -1, 3, 8, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 10, 1, 0, 2, 0, -1, -1, -1, 3, 8, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 10, 1, 0, 2, 0, -1, -1, -1, 3, 8, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 10, 1, 0, 2, 0, -1, -1, -1, 3, 8, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 10, 1, 0, 2, 0, -1, -1, -1, 3, 8, },
+ { -1, -1, 4, 1, 5, -1, 2, -1, -1, 10, 1, 0, 2, 0, -1, -1, -1, 3, 8, },
+ { -1, -1, 1, -1, 2, 9, -1, -1, 8, 7, -1, 0, 1, -1, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 7, -1, 0, 1, -1, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 7, -1, 0, 1, -1, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 7, -1, 0, 1, -1, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 7, -1, 0, 1, -1, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 7, -1, 0, 1, -1, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 7, -1, 0, 1, -1, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 7, -1, 0, 1, -1, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 7, -1, 0, 1, -1, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 7, -1, 0, 1, -1, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 7, -1, 0, 1, -1, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, 7, -1, 0, 1, -1, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, 8, 2, -1, 9, -1, -1, -1, 0, -1, 1, 7, -1, -1, -1, 0, 5, },
+ { -1, -1, 1, -1, 2, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, 0, 5, },
+ },
+ {
+ { -1, 6, 4, 1, 5, 10, 2, -1, 9, 7, 1, 0, 2, 0, -1, -1, -1, 3, -1, },
+ { -1, 6, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, 2, 0, -1, -1, -1, 3, -1, },
+ { -1, 6, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, 2, 0, -1, -1, -1, 3, -1, },
+ { -1, 6, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, 2, 0, -1, -1, -1, 3, -1, },
+ { -1, 6, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, 2, 0, -1, -1, -1, 3, -1, },
+ { -1, 6, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, 2, 0, -1, -1, -1, 3, -1, },
+ { -1, 6, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, 2, 0, -1, -1, -1, 3, -1, },
+ { -1, 6, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, 2, 0, -1, -1, -1, 3, -1, },
+ { -1, 6, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, 2, 0, -1, -1, -1, 3, -1, },
+ { -1, 6, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, 2, 0, -1, -1, -1, 3, -1, },
+ { -1, 6, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, 2, 0, -1, -1, -1, 3, -1, },
+ { -1, 6, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, 2, 0, -1, -1, -1, 3, -1, },
+ { -1, 3, 1, -1, 2, 7, -1, -1, 6, 4, -1, 0, 1, -1, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, 1, -1, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, 5, 2, -1, 6, -1, -1, -1, 0, -1, 1, 4, -1, -1, -1, 0, -1, },
+ { -1, 3, 1, -1, 2, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, 0, -1, },
+ },
+ {
+ { 2, -1, 4, 1, 5, 10, 2, -1, 9, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 1, -1, 1, -1, 2, 7, -1, -1, 6, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, 5, 2, -1, 6, -1, -1, -1, 0, -1, -1, 4, 3, -1, -1, 0, -1, },
+ { 0, -1, 1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, -1, -1, 0, -1, },
+ },
+ {
+ { 2, -1, 4, 1, 5, 10, 2, -1, 9, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 2, -1, 4, 1, 5, -1, 2, -1, -1, 7, 1, 0, -1, 0, 6, -1, -1, 3, -1, },
+ { 1, -1, 1, -1, 2, 7, -1, -1, 6, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, -1, 2, -1, -1, -1, -1, 4, -1, 0, -1, -1, 3, -1, -1, 0, -1, },
+ { 1, -1, 1, 5, 2, -1, 6, -1, -1, -1, 0, -1, -1, 4, 3, -1, -1, 0, -1, },
+ { 0, -1, 1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, -1, -1, 0, -1, },
+ },
+};
+
+static int mask_variable_locations[num_fragment_masks][num_fragment_variables] = {
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { -1, -1, 2, -1, 3, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, },
+};
+
+#endif
diff --git a/src/opengl/util/generator.cpp b/src/opengl/util/generator.cpp
new file mode 100644
index 0000000000..de2450d005
--- /dev/null
+++ b/src/opengl/util/generator.cpp
@@ -0,0 +1,435 @@
+/****************************************************************************
+**
+** 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 <QFile>
+#include <QList>
+#include <QMap>
+#include <QPair>
+#include <QSet>
+#include <QString>
+#include <QTextStream>
+
+#include <QtDebug>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+typedef QPair<QString, QString> QStringPair;
+
+QString readSourceFile(const QString &sourceFile, bool fragmentProgram = false)
+{
+ QFile file(sourceFile);
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qDebug() << "Missing source file" << sourceFile;
+ exit(0);
+ }
+
+ QString source;
+
+ QTextStream in(&file);
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+
+ if (fragmentProgram && line[0] == '#' && !line.startsWith("#var"))
+ continue;
+
+ if (fragmentProgram)
+ source.append(" \"");
+
+ source.append(line);
+
+ if (fragmentProgram)
+ source.append("\\n\"");
+
+ source.append('\n');
+ }
+
+ if (fragmentProgram)
+ source.append(" ;\n");
+
+ return source;
+}
+
+QList<QStringPair> readConf(const QString &confFile)
+{
+ QFile file(confFile);
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qDebug() << "Missing file" << confFile;
+ exit(0);
+ }
+
+ QList<QStringPair> result;
+
+ QTextStream in(&file);
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+
+ if (line.startsWith('#'))
+ continue;
+
+ QTextStream lineStream(&line);
+
+ QString enumerator;
+ QString sourceFile;
+
+ lineStream >> enumerator;
+
+ if (lineStream.atEnd()) {
+ qDebug() << "Error in file" << confFile << "(" << enumerator << ")";
+ exit(0);
+ }
+
+ lineStream >> sourceFile;
+
+ result << QStringPair(enumerator, readSourceFile(sourceFile));
+ }
+
+ return result;
+}
+
+QString compileSource(const QString &source)
+{
+ {
+ QFile tempSourceFile("__tmp__.glsl");
+ if (!tempSourceFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qDebug() << "Failed opening __tmp__.glsl";
+ exit(0);
+ }
+
+ QTextStream out(&tempSourceFile);
+ out << source;
+ }
+
+ if (std::system("cgc -quiet -oglsl -profile arbfp1 __tmp__.glsl >__tmp__.frag") == -1) {
+ qDebug() << "Failed running cgc";
+ exit(0);
+ }
+
+ return readSourceFile("__tmp__.frag", true);
+}
+
+QString getWord(QString line, int word)
+{
+ QTextStream in(&line);
+
+ QString result;
+
+ for (int i = 0; i < word; ++i)
+ in >> result;
+
+ return result;
+}
+
+static int toInt(const QByteArray &str)
+{
+ int value = 0;
+
+ for (int i = 0; i < str.size(); ++i) {
+ if (str[i] < '0' || str[i] > '9')
+ break;
+
+ value *= 10;
+ value += (str[i] - '0');
+ }
+
+ return value;
+}
+QList<int> getLocations(const QSet<QString> &variables, QString source)
+{
+ QTextStream in(&source);
+
+ QMap<QString, int> locations;
+
+ foreach (QString variable, variables)
+ locations[variable] = -1;
+
+ while (!in.atEnd()) {
+ QString line = in.readLine().trimmed();
+
+ line = line.right(line.size() - 1);
+
+ if (line.startsWith("#var")) {
+ QByteArray temp;
+ QByteArray name;
+
+ QTextStream lineStream(&line);
+
+ lineStream >> temp >> temp >> name;
+
+ int location = -1;
+
+ while (!lineStream.atEnd()) {
+ lineStream >> temp;
+
+ if (temp.startsWith("c[")) {
+ location = toInt(temp.right(temp.size() - 2));
+ break;
+ }
+
+ if (temp == "texunit") {
+ lineStream >> temp;
+ location = toInt(temp);
+ break;
+ }
+ }
+
+ locations[name] = location;
+ }
+ }
+
+ QList<int> result;
+
+ foreach (QString variable, variables)
+ result << locations[variable];
+
+ return result;
+}
+
+// remove #var statements
+QString trimmed(QString source)
+{
+ QTextStream in(&source);
+
+ QString result;
+
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ if (!line.trimmed().startsWith("\"#"))
+ result += line + '\n';
+ }
+
+ return result;
+}
+
+void writeIncludeFile(const QSet<QString> &variables,
+ const QList<QStringPair> &brushes,
+ const QList<QStringPair> &compositionModes,
+ const QList<QStringPair> &masks,
+ const QMap<QString, QMap<QString, QString> > &compiled)
+{
+ QFile includeFile("fragmentprograms_p.h");
+ if (!includeFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qDebug() << "Failed opening fragmentprograms_p.h";
+ exit(0);
+ }
+
+ QTextStream out(&includeFile);
+
+ QLatin1String tab(" ");
+
+ out << "#ifndef FRAGMENTPROGRAMS_H\n"
+ << "#define FRAGMENTPROGRAMS_H\n\n";
+
+ out << "enum FragmentVariable {\n";
+ foreach (QString str, variables)
+ out << tab << "VAR_" << str.toUpper() << ",\n";
+ out << "};\n\n";
+
+ out << "enum FragmentBrushType {\n";
+ foreach (QStringPair brush, brushes)
+ out << tab << brush.first << ",\n";
+ out << "};\n\n";
+
+ out << "enum FragmentCompositionModeType {\n";
+ foreach (QStringPair mode, compositionModes)
+ out << tab << mode.first << ",\n";
+ out << "};\n\n";
+
+ out << "enum FragmentMaskType {\n";
+ foreach (QStringPair mask, masks)
+ out << tab << mask.first << ",\n";
+ out << "};\n\n";
+
+ out << "static const unsigned int num_fragment_variables = " << variables.size() << ";\n\n";
+ out << "static const unsigned int num_fragment_brushes = " << brushes.size() << ";\n";
+ out << "static const unsigned int num_fragment_composition_modes = " << compositionModes.size() << ";\n";
+ out << "static const unsigned int num_fragment_masks = " << masks.size() << ";\n\n";
+
+ foreach (QStringPair mask, masks) {
+ const QString compiledSource = compiled[mask.first]["MASK__"];
+
+ out << "static const char *FragmentProgram_" << mask.first << " =\n"
+ << trimmed(compiledSource)
+ << '\n';
+ }
+
+ foreach (QStringPair brush, brushes) {
+ foreach (QStringPair mode, compositionModes) {
+ const QString compiledSource = compiled[brush.first][mode.first];
+
+ out << "static const char *FragmentProgram_" << brush.first << '_' << mode.first << " =\n"
+ << trimmed(compiledSource)
+ << '\n';
+ }
+ }
+
+ out << "static const char *mask_fragment_program_sources[num_fragment_masks] = {\n";
+ foreach (QStringPair mask, masks)
+ out << tab << "FragmentProgram_" << mask.first << ",\n";
+ out << "};\n\n";
+
+ out << "static const char *painter_fragment_program_sources[num_fragment_brushes][num_fragment_composition_modes] = {\n";
+ foreach (QStringPair brush, brushes) {
+ out << tab << "{\n";
+
+ foreach (QStringPair mode, compositionModes)
+ out << tab << tab << "FragmentProgram_" << brush.first << '_' << mode.first << ",\n";
+
+ out << tab << "},\n";
+ }
+ out << "};\n\n";
+
+ out << "static int painter_variable_locations[num_fragment_brushes][num_fragment_composition_modes][num_fragment_variables] = {\n";
+ foreach (QStringPair brush, brushes) {
+ out << tab << "{\n";
+
+ foreach (QStringPair mode, compositionModes) {
+ out << tab << tab << "{ ";
+
+ QList<int> locations = getLocations(variables, compiled[brush.first][mode.first]);
+
+ foreach (int location, locations)
+ out << location << ", ";
+
+ out << "},\n";
+ }
+
+ out << tab << "},\n";
+ }
+ out << "};\n\n";
+
+ out << "static int mask_variable_locations[num_fragment_masks][num_fragment_variables] = {\n";
+ foreach (QStringPair mask, masks) {
+ out << tab << "{ ";
+
+ QList<int> locations = getLocations(variables, compiled[mask.first]["MASK__"]);
+
+ foreach (int location, locations)
+ out << location << ", ";
+
+ out << "},\n";
+ }
+ out << "};\n\n";
+ out << "#endif\n";
+}
+
+QList<QString> getVariables(QString program)
+{
+ QList<QString> result;
+
+ QTextStream in(&program);
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+
+ if (line.startsWith("uniform")) {
+ QString word = getWord(line, 3);
+ result << word.left(word.size() - 1);
+ } else if (line.startsWith("#include")) {
+ QString file = getWord(line, 2);
+ result << getVariables(readSourceFile(file.mid(1, file.size() - 2)));
+ }
+ }
+
+ return result;
+}
+
+int main()
+{
+ QList<QStringPair> brushes = readConf(QLatin1String("brushes.conf"));
+ QList<QStringPair> compositionModes = readConf(QLatin1String("composition_modes.conf"));
+ QList<QStringPair> masks = readConf(QLatin1String("masks.conf"));
+
+ QString painterSource = readSourceFile("painter.glsl");
+ QString painterNoMaskSource = readSourceFile("painter_nomask.glsl");
+ QString fastPainterSource = readSourceFile("fast_painter.glsl");
+ QString brushPainterSource = readSourceFile("brush_painter.glsl");
+
+ QSet<QString> variables;
+
+ QList<QStringPair> programs[3] = { brushes, compositionModes, masks };
+
+ for (int i = 0; i < 3; ++i)
+ foreach (QStringPair value, programs[i])
+ variables += QSet<QString>::fromList(getVariables(value.second));
+
+ variables += QSet<QString>::fromList(getVariables(painterSource));
+ variables += QSet<QString>::fromList(getVariables(fastPainterSource));
+
+ QMap<QString, QMap<QString, QString> > compiled;
+
+ foreach (QStringPair brush, brushes) {
+ foreach (QStringPair mode, compositionModes) {
+ QString combinedSource = brush.second + mode.second + painterSource;
+ compiled[brush.first][mode.first] = compileSource(combinedSource);
+
+ combinedSource = brush.second + mode.second + painterNoMaskSource;
+ compiled[brush.first][mode.first + "_NOMASK"] = compileSource(combinedSource);
+ }
+
+ QString fastSource = brush.second + fastPainterSource;
+ QString brushSource = brush.second + brushPainterSource;
+
+ compiled[brush.first]["COMPOSITION_MODE_BLEND_MODE_MASK"] = compileSource(fastSource);
+ compiled[brush.first]["COMPOSITION_MODE_BLEND_MODE_NOMASK"] = compileSource(brushSource);
+ }
+
+ QList<QStringPair> temp;
+
+ foreach (QStringPair mode, compositionModes)
+ temp << QStringPair(mode.first + "_NOMASK", mode.second);
+
+ compositionModes += temp;
+
+ compositionModes << QStringPair("COMPOSITION_MODE_BLEND_MODE_MASK", "")
+ << QStringPair("COMPOSITION_MODE_BLEND_MODE_NOMASK", "");
+
+ foreach (QStringPair mask, masks)
+ compiled[mask.first]["MASK__"] = compileSource(mask.second);
+
+ writeIncludeFile(variables, brushes, compositionModes, masks, compiled);
+
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/util/generator.pro b/src/opengl/util/generator.pro
new file mode 100644
index 0000000000..9425dbea00
--- /dev/null
+++ b/src/opengl/util/generator.pro
@@ -0,0 +1,11 @@
+######################################################################
+# Automatically generated by qmake (2.01a) Thu Oct 19 11:03:24 2006
+######################################################################
+
+TEMPLATE = app
+TARGET = generator
+DEPENDPATH += .
+INCLUDEPATH += .
+
+# Input
+SOURCES += generator.cpp
diff --git a/src/opengl/util/glsl_to_include.sh b/src/opengl/util/glsl_to_include.sh
new file mode 100755
index 0000000000..59d4693c3c
--- /dev/null
+++ b/src/opengl/util/glsl_to_include.sh
@@ -0,0 +1,33 @@
+#! /bin/sh
+
+# Compile a .glsl file to a file that can be included in a C++ program
+USAGE="Usage: $0 <file.glsl>"
+CGC=cgc
+CGC_PROFILE=arbfp1
+
+if test $# -ne 1
+then
+ echo $USAGE
+ exit 1
+fi
+
+GLSL_FILE=$1
+FRAG_FILE=`basename $1 .glsl`.frag
+#GLSL_INC_FILE=`basename $1 .glsl`.glsl_quoted
+
+echo "// Generated by src/opengl/util/$0 from $1" > $FRAG_FILE
+$CGC -quiet -oglsl -profile $CGC_PROFILE $GLSL_FILE | while read line
+do
+ if test `echo $line | cut -c1` != "#"
+ then
+ echo -e \"$line\" >> $FRAG_FILE
+ fi
+done
+echo "; // Generated by src/opengl/util/$0 from $1" >> $FRAG_FILE
+
+#echo "// Generated by src/opengl/util/$0 from $1" > $GLSL_INC_FILE
+#cat $GLSL_FILE | while read line
+#do
+# printf \"%s\\\\n\"\\n "$line" >> $GLSL_INC_FILE
+#done
+#echo "; // Generated by src/opengl/util/$0 from $1" >> $GLSL_INC_FILE
diff --git a/src/opengl/util/linear_brush.glsl b/src/opengl/util/linear_brush.glsl
new file mode 100644
index 0000000000..90a4440a99
--- /dev/null
+++ b/src/opengl/util/linear_brush.glsl
@@ -0,0 +1,22 @@
+uniform sampler1D palette;
+uniform vec3 linear;
+uniform vec3 inv_matrix_m0;
+uniform vec3 inv_matrix_m1;
+uniform vec3 inv_matrix_m2;
+
+vec4 brush()
+{
+ mat3 mat;
+
+ mat[0] = inv_matrix_m0;
+ mat[1] = inv_matrix_m1;
+ mat[2] = inv_matrix_m2;
+
+ vec3 hcoords = mat * vec3(gl_FragCoord.xy, 1);
+ vec2 A = hcoords.xy / hcoords.z;
+
+ float val = dot(linear.xy, A) * linear.z;
+
+ return texture1D(palette, val);
+}
+
diff --git a/src/opengl/util/masks.conf b/src/opengl/util/masks.conf
new file mode 100644
index 0000000000..733ac81c70
--- /dev/null
+++ b/src/opengl/util/masks.conf
@@ -0,0 +1,3 @@
+FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA trap_exact_aa.glsl
+FRAGMENT_PROGRAM_MASK_ELLIPSE_AA ellipse_aa.glsl
+#FRAGMENT_PROGRAM_MASK_ELLIPSE ellipse.glsl
diff --git a/src/opengl/util/painter.glsl b/src/opengl/util/painter.glsl
new file mode 100644
index 0000000000..b990234778
--- /dev/null
+++ b/src/opengl/util/painter.glsl
@@ -0,0 +1,21 @@
+uniform sampler2D dst_texture;
+uniform sampler2D mask_texture;
+uniform vec2 inv_mask_size;
+uniform vec2 inv_dst_size;
+uniform vec2 mask_offset;
+uniform vec4 mask_channel;
+
+float mask()
+{
+ return dot(mask_channel, texture2D(mask_texture, (gl_FragCoord.xy + mask_offset) * inv_mask_size));
+}
+
+void main()
+{
+ vec4 dst = texture2D(dst_texture, gl_FragCoord.xy * inv_dst_size);
+
+ // combine clip and coverage channels
+ float mask_alpha = mask();
+
+ gl_FragColor = mix(dst, composite(brush(), dst), mask_alpha);
+}
diff --git a/src/opengl/util/painter_nomask.glsl b/src/opengl/util/painter_nomask.glsl
new file mode 100644
index 0000000000..af5ad6505f
--- /dev/null
+++ b/src/opengl/util/painter_nomask.glsl
@@ -0,0 +1,9 @@
+uniform sampler2D dst_texture;
+uniform vec2 inv_dst_size;
+
+void main()
+{
+ vec4 dst = texture2D(dst_texture, gl_FragCoord.xy * inv_dst_size);
+
+ gl_FragColor = composite(brush(), dst);
+}
diff --git a/src/opengl/util/pattern_brush.glsl b/src/opengl/util/pattern_brush.glsl
new file mode 100644
index 0000000000..e070449e01
--- /dev/null
+++ b/src/opengl/util/pattern_brush.glsl
@@ -0,0 +1,25 @@
+uniform sampler2D brush_texture;
+uniform vec2 inv_brush_texture_size;
+uniform vec3 inv_matrix_m0;
+uniform vec3 inv_matrix_m1;
+uniform vec3 inv_matrix_m2;
+
+vec4 brush()
+{
+ mat3 mat;
+
+ mat[0] = inv_matrix_m0;
+ mat[1] = inv_matrix_m1;
+ mat[2] = inv_matrix_m2;
+
+ vec3 hcoords = mat * vec3(gl_FragCoord.xy, 1);
+ vec2 coords = hcoords.xy / hcoords.z;
+
+ coords *= inv_brush_texture_size;
+
+ coords.y = -coords.y;
+
+ float alpha = texture2D(brush_texture, coords).r;
+
+ return gl_Color * alpha;
+}
diff --git a/src/opengl/util/radial_brush.glsl b/src/opengl/util/radial_brush.glsl
new file mode 100644
index 0000000000..84bec62e65
--- /dev/null
+++ b/src/opengl/util/radial_brush.glsl
@@ -0,0 +1,28 @@
+uniform sampler1D palette;
+uniform vec2 fmp;
+uniform float fmp2_m_radius2;
+uniform vec3 inv_matrix_m0;
+uniform vec3 inv_matrix_m1;
+uniform vec3 inv_matrix_m2;
+
+vec4 brush()
+{
+ mat3 mat;
+
+ mat[0] = inv_matrix_m0;
+ mat[1] = inv_matrix_m1;
+ mat[2] = inv_matrix_m2;
+
+ vec3 hcoords = mat * vec3(gl_FragCoord.xy, 1);
+ vec2 A = hcoords.xy / hcoords.z;
+ vec2 B = fmp;
+
+ float a = fmp2_m_radius2;
+ float b = 2.0*dot(A, B);
+ float c = -dot(A, A);
+
+ float val = (-b + sqrt(b*b - 4.0*a*c)) / (2.0*a);
+
+ return texture1D(palette, val);
+}
+
diff --git a/src/opengl/util/simple_porter_duff.glsl b/src/opengl/util/simple_porter_duff.glsl
new file mode 100644
index 0000000000..83aef48c93
--- /dev/null
+++ b/src/opengl/util/simple_porter_duff.glsl
@@ -0,0 +1,16 @@
+uniform vec2 porterduff_ab;
+uniform vec3 porterduff_xyz;
+
+vec4 composite(vec4 src, vec4 dst)
+{
+ vec4 result;
+
+ result.xyz = porterduff_ab.x * src.xyz * dst.a
+ + porterduff_ab.y * dst.xyz * src.a
+ + porterduff_xyz.y * src.xyz * (1 - dst.a)
+ + porterduff_xyz.z * dst.xyz * (1 - src.a);
+
+ result.a = dot(porterduff_xyz, vec3(src.a * dst.a, src.a * (1 - dst.a), dst.a * (1 - src.a)));
+
+ return result;
+}
diff --git a/src/opengl/util/solid_brush.glsl b/src/opengl/util/solid_brush.glsl
new file mode 100644
index 0000000000..760afd1a72
--- /dev/null
+++ b/src/opengl/util/solid_brush.glsl
@@ -0,0 +1,4 @@
+vec4 brush()
+{
+ return gl_Color;
+}
diff --git a/src/opengl/util/texture_brush.glsl b/src/opengl/util/texture_brush.glsl
new file mode 100644
index 0000000000..93865b8afc
--- /dev/null
+++ b/src/opengl/util/texture_brush.glsl
@@ -0,0 +1,23 @@
+uniform sampler2D brush_texture;
+uniform vec2 inv_brush_texture_size;
+uniform vec3 inv_matrix_m0;
+uniform vec3 inv_matrix_m1;
+uniform vec3 inv_matrix_m2;
+
+vec4 brush()
+{
+ mat3 mat;
+
+ mat[0] = inv_matrix_m0;
+ mat[1] = inv_matrix_m1;
+ mat[2] = inv_matrix_m2;
+
+ vec3 hcoords = mat * vec3(gl_FragCoord.xy, 1);
+ vec2 coords = hcoords.xy / hcoords.z;
+
+ coords *= inv_brush_texture_size;
+
+ coords.y = -coords.y;
+
+ return texture2D(brush_texture, coords);
+}
diff --git a/src/opengl/util/trap_exact_aa.glsl b/src/opengl/util/trap_exact_aa.glsl
new file mode 100644
index 0000000000..b96f87d04a
--- /dev/null
+++ b/src/opengl/util/trap_exact_aa.glsl
@@ -0,0 +1,58 @@
+float quad_aa()
+{
+ float top = min(gl_FragCoord.y + 0.5, gl_TexCoord[0].x);
+ float bottom = max(gl_FragCoord.y - 0.5, gl_TexCoord[0].y);
+
+ float area = top - bottom;
+
+ float left = gl_FragCoord.x - 0.5;
+ float right = gl_FragCoord.x + 0.5;
+
+ // use line equations to compute intersections of left/right edges with top/bottom of truncated pixel
+ vec4 vecX = gl_TexCoord[1].xxzz * vec2(top, bottom).xyxy + gl_TexCoord[1].yyww;
+
+ vec2 invA = gl_TexCoord[0].zw;
+
+ // transform right line to left to be able to use same calculations for both
+ vecX.zw = 2 * gl_FragCoord.x - vecX.zw;
+
+ vec2 topX = vec2(vecX.x, vecX.z);
+ vec2 bottomX = vec2(vecX.y, vecX.w);
+
+ // transform lines such that top intersection is to the right of bottom intersection
+ vec2 topXTemp = max(topX, bottomX);
+ vec2 bottomXTemp = min(topX, bottomX);
+
+ // make sure line slope reflects mirrored lines
+ invA = mix(invA, -invA, step(topX, bottomX));
+
+ vec2 vecLeftRight = vec2(left, right);
+
+ // compute the intersections of the lines with the left and right edges of the pixel
+ vec4 intersectY = bottom + (vecLeftRight.xyxy - bottomXTemp.xxyy) * invA.xxyy;
+
+ vec2 temp = mix(area - 0.5 * (right - bottomXTemp) * (intersectY.yw - bottom), // left < bottom < right < top
+ (0.5 * (topXTemp + bottomXTemp) - left) * area, // left < bottom < top < right
+ step(topXTemp, right));
+
+ vec2 excluded = 0.5 * (top - intersectY.xz) * (topXTemp - left); // bottom < left < top < right
+
+ excluded = mix((top - 0.5 * (intersectY.yw + intersectY.xz)) * (right - left), // bottom < left < right < top
+ excluded, step(topXTemp, right));
+
+ excluded = mix(temp, // left < bottom < right (see calculation of temp)
+ excluded, step(bottomXTemp, left));
+
+ excluded = mix(vec2(area, area), // right < bottom < top
+ excluded, step(bottomXTemp, right));
+
+ excluded *= step(left, topXTemp);
+
+ return (area - excluded.x - excluded.y) * step(bottom, top);
+}
+
+void main()
+{
+ gl_FragColor = quad_aa();
+}
+