diff options
Diffstat (limited to 'src/opengl')
94 files changed, 37126 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 0000000..5b9d28b --- /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 0000000..0352d39 --- /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 0000000..c205022 --- /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 0000000..b4591b2 --- /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 0000000..346ea13 --- /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 0000000..e460e08 --- /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 0000000..c8f47b2 --- /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 0000000..634be84 --- /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 0000000..1625b84 --- /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 0000000..a74f044 --- /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 0000000..ce66e4b --- /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 0000000..48d7caf --- /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 0000000..165a0f3 --- /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 0000000..76cb573 --- /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 0000000..9135c53 --- /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 0000000..dbec385 --- /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 0000000..8efe7e7 --- /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 0000000..2d90342 --- /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 0000000..01b1d6f --- /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 0000000..e514ff5 --- /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 0000000..98c5710 --- /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 0000000..39f25e2 --- /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 0000000..314c659 --- /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, ®ionBounds); + Rect regionBoundsDest = regionBounds; + regionBoundsDest.bottom *= scale; + regionBoundsDest.right *= scale; + MapRgn(region, ®ionBounds, ®ionBoundsDest); + } + + 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 0000000..b8bbeaf --- /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 0000000..cb9aa89 --- /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 0000000..bd8569a --- /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, ¶ms); + 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, ¶ms); + 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, ¶ms); + 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, ¶ms); + 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 0000000..cb51598 --- /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 0000000..28c34de --- /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 0000000..480a2dc --- /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 0000000..02a2c13 --- /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 0000000..6bdb0c4 --- /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 0000000..8357cf9 --- /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 0000000..a0517f5 --- /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 0000000..fb22272 --- /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 0000000..a9e1b2f --- /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 0000000..18905ab --- /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 0000000..369de7f --- /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 0000000..5f74f26 --- /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 0000000..0131570 --- /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 0000000..964efa2 --- /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 0000000..14941ab --- /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 0000000..abdf838 --- /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 0000000..e81a576 --- /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 0000000..0804503 --- /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 0000000..ff23948 --- /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 0000000..dc2eea6 --- /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 0000000..a488b97 --- /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 0000000..d045bea --- /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 0000000..40e549b --- /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 0000000..740f879 --- /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 0000000..ce8ad7a --- /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 0000000..9e36cdf --- /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 0000000..96abe18 --- /dev/null +++ b/src/opengl/qpaintengine_opengl.cpp @@ -0,0 +1,5786 @@ +/**************************************************************************** +** +** 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(); +} + +void QOpenGLPaintEnginePrivate::strokePath(const QPainterPath &path, bool use_cache) +{ + QBrush old_brush = cbrush; + cbrush = cpen.brush(); + + extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp + 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 0000000..ad5d56b --- /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 ®ion, 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 0000000..5d668cd --- /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 0000000..63703fd --- /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 0000000..83bd80b --- /dev/null +++ b/src/opengl/qwindowsurface_gl.cpp @@ -0,0 +1,660 @@ +/**************************************************************************** +** +** 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 +// + +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); + + Q_GUI_EXPORT bool qt_win_owndc_required; + 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 0000000..0194378 --- /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 ®ion, const QPoint &offset); + void setGeometry(const QRect &rect); + bool scroll(const QRegion &area, int dx, int dy); + + void beginPaint(const QRegion ®ion); + void endPaint(const QRegion ®ion); + + 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 0000000..ff20eb4 --- /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 0000000..6c4acdf --- /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 0000000..93e2b3b --- /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 0000000..a5a153f --- /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 0000000..c194441 --- /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 0000000..c1e83fd --- /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 0000000..ca13ce7 --- /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 0000000..ccd1183 --- /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 0000000..9dd4de3 --- /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 0000000..1fbd27a --- /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 0000000..268345a --- /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 0000000..a9b7226 --- /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 0000000..8f4f010 --- /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 0000000..0237827 --- /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 0000000..df52830 --- /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 0000000..83ee2f5 --- /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 0000000..860ae77 --- /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 0000000..f7a6454 --- /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 0000000..5372f58 --- /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 0000000..0878f99 --- /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 0000000..eed18e8 --- /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 0000000..63f5e5f --- /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 0000000..ecf0bf8 --- /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 0000000..de2450d --- /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 0000000..9425dbe --- /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 0000000..59d4693 --- /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 0000000..90a4440 --- /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 0000000..733ac81 --- /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 0000000..b990234 --- /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 0000000..af5ad65 --- /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 0000000..e070449 --- /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 0000000..84bec62 --- /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 0000000..83aef48 --- /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 0000000..760afd1 --- /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 0000000..93865b8 --- /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 0000000..b96f87d --- /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(); +} + |