summaryrefslogtreecommitdiffstats
path: root/src/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'src/opengl')
-rw-r--r--src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp8
-rw-r--r--src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h39
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager.cpp18
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager_p.h14
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadersource_p.h24
-rw-r--r--src/opengl/gl2paintengineex/qglgradientcache.cpp2
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp468
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h26
-rw-r--r--src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp90
-rw-r--r--src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h3
-rw-r--r--src/opengl/gl2paintengineex/qtriangulatingstroker.cpp38
-rw-r--r--src/opengl/gl2paintengineex/qtriangulatingstroker_p.h5
-rw-r--r--src/opengl/gl2paintengineex/qtriangulator.cpp2985
-rw-r--r--src/opengl/gl2paintengineex/qtriangulator_p.h98
-rw-r--r--src/opengl/opengl.pro14
-rw-r--r--src/opengl/qgl.cpp600
-rw-r--r--src/opengl/qgl.h38
-rw-r--r--src/opengl/qgl_cl_p.h141
-rw-r--r--src/opengl/qgl_egl.cpp248
-rw-r--r--src/opengl/qgl_egl_p.h7
-rw-r--r--src/opengl/qgl_p.h136
-rw-r--r--src/opengl/qgl_qws.cpp23
-rw-r--r--src/opengl/qgl_win.cpp161
-rw-r--r--src/opengl/qgl_wince.cpp22
-rw-r--r--src/opengl/qgl_x11.cpp389
-rw-r--r--src/opengl/qgl_x11egl.cpp613
-rw-r--r--src/opengl/qglbuffer.cpp576
-rw-r--r--src/opengl/qglbuffer.h132
-rw-r--r--src/opengl/qglextensions.cpp44
-rw-r--r--src/opengl/qglextensions_p.h138
-rw-r--r--src/opengl/qglframebufferobject.cpp42
-rw-r--r--src/opengl/qglframebufferobject.h2
-rw-r--r--src/opengl/qglpaintdevice.cpp29
-rw-r--r--src/opengl/qglpaintdevice_p.h1
-rw-r--r--src/opengl/qglpixelbuffer.cpp12
-rw-r--r--src/opengl/qglpixelbuffer.h1
-rw-r--r--src/opengl/qglpixelbuffer_egl.cpp20
-rw-r--r--src/opengl/qglpixelbuffer_p.h12
-rw-r--r--src/opengl/qglshaderprogram.cpp412
-rw-r--r--src/opengl/qglshaderprogram.h50
-rw-r--r--src/opengl/qgraphicsshadereffect.cpp2
-rw-r--r--src/opengl/qgraphicssystem_gl.cpp23
-rw-r--r--src/opengl/qgraphicssystem_gl_p.h4
-rw-r--r--src/opengl/qpaintengine_opengl.cpp304
-rw-r--r--src/opengl/qpaintengine_opengl_p.h1
-rw-r--r--src/opengl/qpixmapdata_gl_p.h2
-rw-r--r--src/opengl/qpixmapdata_x11gl_egl.cpp373
-rw-r--r--src/opengl/qpixmapdata_x11gl_p.h14
-rw-r--r--src/opengl/qwindowsurface_gl.cpp63
-rw-r--r--src/opengl/qwindowsurface_gl_p.h4
-rw-r--r--src/opengl/qwindowsurface_x11gl.cpp156
-rw-r--r--src/opengl/qwindowsurface_x11gl_p.h4
52 files changed, 6950 insertions, 1681 deletions
diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp
index 516b847..559a6fd 100644
--- a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp
+++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp
@@ -61,12 +61,6 @@ QGLRect QGL2PEXVertexArray::boundingRect() const
return QGLRect(minX, minY, maxX, maxY);
}
-void QGL2PEXVertexArray::addRect(const QRectF &rect)
-{
- vertexArray << rect.topLeft() << rect.topRight() << rect.bottomRight()
- << rect.bottomRight() << rect.bottomLeft() << rect.topLeft();
-}
-
void QGL2PEXVertexArray::addClosingLine(int index)
{
if (QPointF(vertexArray.at(index)) != QPointF(vertexArray.last()))
@@ -145,7 +139,7 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc
// threshold based on same algorithm as in qtriangulatingstroker.cpp
int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * 3.14f / (curveInverseScale * 6));
if (threshold < 3) threshold = 3;
- qreal one_over_threshold_minus_1 = 1.f / (threshold - 1);
+ qreal one_over_threshold_minus_1 = qreal(1) / (threshold - 1);
for (int t=0; t<threshold; ++t) {
QPointF pt = b.pointAt(t * one_over_threshold_minus_1);
lineToArray(pt.x(), pt.y());
diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
index e0497b1..46029b9 100644
--- a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
+++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
@@ -100,10 +100,45 @@ class QGL2PEXVertexArray
{
public:
QGL2PEXVertexArray() :
+ vertexArray(0), vertexArrayStops(0),
maxX(-2e10), maxY(-2e10), minX(2e10), minY(2e10),
- boundingRectDirty(true) {}
+ boundingRectDirty(true)
+ { }
+
+ inline void addRect(const QRectF &rect)
+ {
+ qreal top = rect.top();
+ qreal left = rect.left();
+ qreal bottom = rect.bottom();
+ qreal right = rect.right();
+
+ vertexArray << QGLPoint(left, top)
+ << QGLPoint(right, top)
+ << QGLPoint(right, bottom)
+ << QGLPoint(right, bottom)
+ << QGLPoint(left, bottom)
+ << QGLPoint(left, top);
+ }
+
+ inline void addQuad(const QRectF &rect)
+ {
+ qreal top = rect.top();
+ qreal left = rect.left();
+ qreal bottom = rect.bottom();
+ qreal right = rect.right();
+
+ vertexArray << QGLPoint(left, top)
+ << QGLPoint(right, top)
+ << QGLPoint(left, bottom)
+ << QGLPoint(right, bottom);
+
+ }
+
+ inline void addVertex(const GLfloat x, const GLfloat y)
+ {
+ vertexArray.add(QGLPoint(x, y));
+ }
- void addRect(const QRectF &rect);
void addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline = true);
void clear();
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
index 0c98e3b..40b3641 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
@@ -96,6 +96,7 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
code[UntransformedPositionVertexShader] = qglslUntransformedPositionVertexShader;
code[PositionOnlyVertexShader] = qglslPositionOnlyVertexShader;
+ code[ComplexGeometryPositionOnlyVertexShader] = qglslComplexGeometryPositionOnlyVertexShader;
code[PositionWithPatternBrushVertexShader] = qglslPositionWithPatternBrushVertexShader;
code[PositionWithLinearGradientBrushVertexShader] = qglslPositionWithLinearGradientBrushVertexShader;
code[PositionWithConicalGradientBrushVertexShader] = qglslPositionWithConicalGradientBrushVertexShader;
@@ -329,7 +330,7 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS
newProg->program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
if (newProg->useOpacityAttribute)
newProg->program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR);
- if (newProg->usePmvMatrix) {
+ if (newProg->usePmvMatrixAttribute) {
newProg->program->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR);
newProg->program->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR);
newProg->program->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR);
@@ -403,6 +404,7 @@ void QGLEngineSharedShaders::cleanupCustomStage(QGLCustomShaderStage* stage)
QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context)
: ctx(context),
shaderProgNeedsChanging(true),
+ complexGeometry(false),
srcPixelType(Qt::NoBrush),
opacityMode(NoOpacity),
maskType(NoMask),
@@ -444,7 +446,8 @@ GLuint QGLEngineShaderManager::getUniformLocation(Uniform id)
"inverse_2_fmp2_m_radius2",
"invertedTextureSize",
"brushTransform",
- "brushTexture"
+ "brushTexture",
+ "matrix"
};
if (uniformLocations.at(id) == GLuint(-1))
@@ -752,7 +755,16 @@ bool QGLEngineShaderManager::useCorrectShaderProg()
}
requiredProgram.useTextureCoords = texCoords;
requiredProgram.useOpacityAttribute = (opacityMode == AttributeOpacity);
- requiredProgram.usePmvMatrix = true;
+ if (complexGeometry && srcPixelType == Qt::SolidPattern) {
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::ComplexGeometryPositionOnlyVertexShader;
+ requiredProgram.usePmvMatrixAttribute = false;
+ } else {
+ requiredProgram.usePmvMatrixAttribute = true;
+
+ // Force complexGeometry off, since we currently don't support that mode for
+ // non-solid brushes
+ complexGeometry = false;
+ }
// At this point, requiredProgram is fully populated so try to find the program in the cache
currentShaderProg = sharedShaders->findProgramInCache(requiredProgram);
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
index 3ab4ebe..06b96ae 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
@@ -272,6 +272,7 @@ public:
// UntransformedPositionVertexShader must be first in the list:
UntransformedPositionVertexShader,
PositionOnlyVertexShader,
+ ComplexGeometryPositionOnlyVertexShader,
PositionWithPatternBrushVertexShader,
PositionWithLinearGradientBrushVertexShader,
PositionWithConicalGradientBrushVertexShader,
@@ -400,7 +401,7 @@ public:
bool useTextureCoords;
bool useOpacityAttribute;
- bool usePmvMatrix;
+ bool usePmvMatrixAttribute;
bool operator==(const QGLEngineShaderProg& other) {
// We don't care about the program
@@ -446,6 +447,7 @@ public:
InvertedTextureSize,
BrushTransform,
BrushTexture,
+ Matrix,
NumUniforms
};
@@ -474,6 +476,15 @@ public:
void useSimpleProgram();
void useBlitProgram();
+ void setHasComplexGeometry(bool hasComplexGeometry)
+ {
+ complexGeometry = hasComplexGeometry;
+ shaderProgNeedsChanging = true;
+ }
+ bool hasComplexGeometry() const
+ {
+ return complexGeometry;
+ }
QGLShaderProgram* currentProgram(); // Returns pointer to the shader the manager has chosen
QGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers
@@ -487,6 +498,7 @@ private slots:
private:
QGLContext* ctx;
bool shaderProgNeedsChanging;
+ bool complexGeometry;
// Current state variables which influence the choice of shader:
QTransform brushTransform;
diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
index c963265..a7ece0f 100644
--- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
@@ -107,6 +107,14 @@ static const char* const qglslPositionOnlyVertexShader = "\n\
gl_Position = vec4(transformedPos.xy, 0.0, transformedPos.z); \n\
}\n";
+static const char* const qglslComplexGeometryPositionOnlyVertexShader = "\n\
+ uniform highp mat3 matrix; \n\
+ attribute highp vec2 vertexCoordsArray; \n\
+ void setPosition(void) \n\
+ { \n\
+ gl_Position = vec4(matrix * vec3(vertexCoordsArray, 1), 1);\n\
+ } \n";
+
static const char* const qglslUntransformedPositionVertexShader = "\n\
attribute highp vec4 vertexCoordsArray; \n\
void setPosition(void) \n\
@@ -274,7 +282,7 @@ static const char* const qglslPositionWithTextureBrushVertexShader = "\n\
uniform mediump vec2 halfViewportSize; \n\
uniform highp vec2 invertedTextureSize; \n\
uniform highp mat3 brushTransform; \n\
- varying highp vec2 textureCoords; \n\
+ varying highp vec2 brushTextureCoords; \n\
void setPosition(void) \n\
{ \n\
highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
@@ -284,7 +292,7 @@ static const char* const qglslPositionWithTextureBrushVertexShader = "\n\
mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\
mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\
gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\
- textureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \n\
+ brushTextureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \n\
}\n";
static const char* const qglslAffinePositionWithTextureBrushVertexShader
@@ -295,28 +303,28 @@ static const char* const qglslAffinePositionWithTextureBrushVertexShader
// we emulate GL_REPEAT by only taking the fractional part of the texture coords.
// TODO: Special case POT textures which don't need this emulation
static const char* const qglslTextureBrushSrcFragmentShader = "\n\
- varying highp vec2 textureCoords; \n\
+ varying highp vec2 brushTextureCoords; \n\
uniform lowp sampler2D brushTexture; \n\
lowp vec4 srcPixel() { \n\
- return texture2D(brushTexture, fract(textureCoords)); \n\
+ return texture2D(brushTexture, fract(brushTextureCoords)); \n\
}\n";
#else
static const char* const qglslTextureBrushSrcFragmentShader = "\n\
- varying highp vec2 textureCoords; \n\
+ varying highp vec2 brushTextureCoords; \n\
uniform lowp sampler2D brushTexture; \n\
lowp vec4 srcPixel() \n\
{ \n\
- return texture2D(brushTexture, textureCoords); \n\
+ return texture2D(brushTexture, brushTextureCoords); \n\
}\n";
#endif
static const char* const qglslTextureBrushSrcWithPatternFragmentShader = "\n\
- varying highp vec2 textureCoords; \n\
+ varying highp vec2 brushTextureCoords; \n\
uniform lowp vec4 patternColor; \n\
uniform lowp sampler2D brushTexture; \n\
lowp vec4 srcPixel() \n\
{ \n\
- return patternColor * (1.0 - texture2D(brushTexture, textureCoords).r); \n\
+ return patternColor * (1.0 - texture2D(brushTexture, brushTextureCoords).r); \n\
}\n";
// Solid Fill Brush
diff --git a/src/opengl/gl2paintengineex/qglgradientcache.cpp b/src/opengl/gl2paintengineex/qglgradientcache.cpp
index 192e01c..a1495dd 100644
--- a/src/opengl/gl2paintengineex/qglgradientcache.cpp
+++ b/src/opengl/gl2paintengineex/qglgradientcache.cpp
@@ -39,10 +39,10 @@
**
****************************************************************************/
+#include "qglgradientcache_p.h"
#include <private/qdrawhelper_p.h>
#include <private/qgl_p.h>
-#include "qglgradientcache_p.h"
QT_BEGIN_NAMESPACE
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
index 237b3ab..ee49a3d 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -64,6 +64,7 @@
// #define QT_OPENGL_CACHE_AS_VBOS
+#include "qglgradientcache_p.h"
#include "qpaintengineex_opengl2_p.h"
#include <string.h> //for memcpy
@@ -77,8 +78,9 @@
#include <private/qfontengine_p.h>
#include <private/qpixmapdata_gl_p.h>
#include <private/qdatabuffer_p.h>
+#include <private/qstatictext_p.h>
+#include <private/qtriangulator_p.h>
-#include "qglgradientcache_p.h"
#include "qglengineshadermanager_p.h"
#include "qgl2pexvertexarray_p.h"
#include "qtriangulatingstroker_p.h"
@@ -107,6 +109,11 @@ QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate()
e->data = 0;
e->engine = 0;
}
+
+ if (elementIndicesVBOId != 0) {
+ glDeleteBuffers(1, &elementIndicesVBOId);
+ elementIndicesVBOId = 0;
+ }
}
void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id)
@@ -210,7 +217,9 @@ void QGL2PaintEngineExPrivate::updateBrushTexture()
const QPixmap& texPixmap = currentBrush.texture();
glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
- QGLTexture *tex = ctx->d_func()->bindTexture(texPixmap, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption);
+ QGLTexture *tex = ctx->d_func()->bindTexture(texPixmap, GL_TEXTURE_2D, GL_RGBA,
+ QGLContext::InternalBindOption |
+ QGLContext::CanFlipNativePixmapBindOption);
updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
textureInvertedY = tex->options & QGLContext::InvertedYBindOption ? -1 : 1;
}
@@ -388,6 +397,7 @@ void QGL2PaintEngineExPrivate::updateMatrix()
qreal(0.0001));
matrixDirty = false;
+ matrixUniformDirty = true;
// Set the PMV matrix attribute. As we use an attributes rather than uniforms, we only
// need to do this once for every matrix change and persists across all shader programs.
@@ -506,6 +516,8 @@ void QGL2PaintEngineEx::beginNativePainting()
ensureActive();
d->transferMode(BrushDrawingMode);
+ d->nativePaintingActive = true;
+
QGLContext *ctx = d->ctx;
glUseProgram(0);
@@ -521,10 +533,10 @@ void QGL2PaintEngineEx::beginNativePainting()
float mv_matrix[4][4] =
{
- { mtx.m11(), mtx.m12(), 0, mtx.m13() },
- { mtx.m21(), mtx.m22(), 0, mtx.m23() },
- { 0, 0, 1, 0 },
- { mtx.dx(), mtx.dy(), 0, mtx.m33() }
+ { float(mtx.m11()), float(mtx.m12()), 0, float(mtx.m13()) },
+ { float(mtx.m21()), float(mtx.m22()), 0, float(mtx.m23()) },
+ { 0, 0, 1, 0 },
+ { float(mtx.dx()), float(mtx.dy()), 0, float(mtx.m33()) }
};
const QSize sz = d->device->size();
@@ -561,9 +573,9 @@ void QGL2PaintEngineExPrivate::resetGLState()
glStencilMask(0xff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_ALWAYS, 0, 0xff);
- glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
- glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
- glDisableVertexAttribArray(QT_OPACITY_ATTR);
+ ctx->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false);
+ ctx->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, false);
+ ctx->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false);
#ifndef QT_OPENGL_ES_2
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // color may have been changed by glVertexAttrib()
#endif
@@ -573,6 +585,12 @@ void QGL2PaintEngineEx::endNativePainting()
{
Q_D(QGL2PaintEngineEx);
d->needsSync = true;
+ d->nativePaintingActive = false;
+}
+
+bool QGL2PaintEngineEx::isNativePaintingActive() const {
+ Q_D(const QGL2PaintEngineEx);
+ return d->nativePaintingActive;
}
void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode)
@@ -587,6 +605,9 @@ void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode)
if (newMode == TextDrawingMode) {
setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data());
setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data());
+ shaderManager->setHasComplexGeometry(true);
+ } else {
+ shaderManager->setHasComplexGeometry(false);
}
if (newMode == ImageDrawingMode) {
@@ -611,10 +632,13 @@ struct QGL2PEVectorPathCache
{
#ifdef QT_OPENGL_CACHE_AS_VBOS
GLuint vbo;
+ GLuint ibo;
#else
float *vertices;
+ quint32 *indices;
#endif
int vertexCount;
+ int indexCount;
GLenum primitiveType;
qreal iscale;
};
@@ -625,9 +649,12 @@ void QGL2PaintEngineExPrivate::cleanupVectorPath(QPaintEngineEx *engine, void *d
#ifdef QT_OPENGL_CACHE_AS_VBOS
Q_ASSERT(engine->type() == QPaintEngine::OpenGL2);
static_cast<QGL2PaintEngineEx *>(engine)->d_func()->unusedVBOSToClean << c->vbo;
+ if (c->ibo)
+ d->unusedIBOSToClean << c->ibo;
#else
Q_UNUSED(engine);
qFree(c->vertices);
+ qFree(c->indices);
#endif
delete c;
}
@@ -658,6 +685,9 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
const QPointF* const points = reinterpret_cast<const QPointF*>(path.points());
+ // ### Remove before release...
+ static bool do_vectorpath_cache = qgetenv("QT_OPENGL_NO_PATH_CACHE").isEmpty();
+
// 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());
@@ -669,6 +699,8 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
QVectorPath::CacheEntry *data = path.lookupCacheData(q);
QGL2PEVectorPathCache *cache;
+ bool updateCache = false;
+
if (data) {
cache = (QGL2PEVectorPathCache *) data->data;
// Check if scale factor is exceeded for curved paths and generate curves if so...
@@ -678,34 +710,39 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
#ifdef QT_OPENGL_CACHE_AS_VBOS
glDeleteBuffers(1, &cache->vbo);
cache->vbo = 0;
+ Q_ASSERT(cache->ibo == 0);
#else
qFree(cache->vertices);
+ Q_ASSERT(cache->indices == 0);
#endif
- cache->vertexCount = 0;
+ updateCache = true;
}
}
} else {
cache = new QGL2PEVectorPathCache;
- cache->vertexCount = 0;
data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath);
+ updateCache = true;
}
// Flatten the path at the current scale factor and fill it into the cache struct.
- if (!cache->vertexCount) {
+ if (updateCache) {
vertexCoordinateArray.clear();
vertexCoordinateArray.addPath(path, inverseScale, false);
int vertexCount = vertexCoordinateArray.vertexCount();
int floatSizeInBytes = vertexCount * 2 * sizeof(float);
cache->vertexCount = vertexCount;
+ cache->indexCount = 0;
cache->primitiveType = GL_TRIANGLE_FAN;
cache->iscale = inverseScale;
#ifdef QT_OPENGL_CACHE_AS_VBOS
glGenBuffers(1, &cache->vbo);
glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
glBufferData(GL_ARRAY_BUFFER, floatSizeInBytes, vertexCoordinateArray.data(), GL_STATIC_DRAW);
+ cache->ibo = 0;
#else
cache->vertices = (float *) qMalloc(floatSizeInBytes);
memcpy(cache->vertices, vertexCoordinateArray.data(), floatSizeInBytes);
+ cache->indices = 0;
#endif
}
@@ -721,8 +758,6 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
} else {
// printf(" - Marking path as cachable...\n");
// Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable
- // ### Remove before release...
- static bool do_vectorpath_cache = qgetenv("QT_OPENGL_NO_PATH_CACHE").isEmpty();
if (do_vectorpath_cache)
path.makeCacheable();
vertexCoordinateArray.clear();
@@ -732,31 +767,117 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
}
} else {
- // The path is too complicated & needs the stencil technique
- vertexCoordinateArray.clear();
- vertexCoordinateArray.addPath(path, inverseScale, false);
+ bool useCache = path.isCacheable();
+ if (useCache) {
+ QRectF bbox = path.controlPointRect();
+ // If the path doesn't fit within these limits, it is possible that the triangulation will fail.
+ useCache &= (bbox.left() > -0x8000 * inverseScale)
+ && (bbox.right() < 0x8000 * inverseScale)
+ && (bbox.top() > -0x8000 * inverseScale)
+ && (bbox.bottom() < 0x8000 * inverseScale);
+ }
- fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
+ if (useCache) {
+ QVectorPath::CacheEntry *data = path.lookupCacheData(q);
+ QGL2PEVectorPathCache *cache;
- glStencilMask(0xff);
- glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
+ bool updateCache = false;
+
+ if (data) {
+ cache = (QGL2PEVectorPathCache *) data->data;
+ // Check if scale factor is exceeded for curved paths and generate curves if so...
+ if (path.isCurved()) {
+ qreal scaleFactor = cache->iscale / inverseScale;
+ if (scaleFactor < 0.5 || scaleFactor > 2.0) {
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+ glDeleteBuffers(1, &cache->vbo);
+ glDeleteBuffers(1, &cache->ibo);
+#else
+ qFree(cache->vertices);
+ qFree(cache->indices);
+#endif
+ updateCache = true;
+ }
+ }
+ } else {
+ cache = new QGL2PEVectorPathCache;
+ data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath);
+ updateCache = true;
+ }
+
+ // Flatten the path at the current scale factor and fill it into the cache struct.
+ if (updateCache) {
+ QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale));
+ cache->vertexCount = polys.vertices.size() / 2;
+ cache->indexCount = polys.indices.size();
+ cache->primitiveType = GL_TRIANGLES;
+ cache->iscale = inverseScale;
+
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+ glGenBuffers(1, &cache->vbo);
+ glGenBuffers(1, &cache->ibo);
+ glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint32) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW);
+
+ QVarLengthArray<float> vertices(polys.vertices.size());
+ for (int i = 0; i < polys.vertices.size(); ++i)
+ vertices[i] = float(inverseScale * polys.vertices.at(i));
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
+#else
+ cache->vertices = (float *) qMalloc(sizeof(float) * polys.vertices.size());
+ cache->indices = (quint32 *) qMalloc(sizeof(quint32) * polys.indices.size());
+ memcpy(cache->indices, polys.indices.data(), sizeof(quint32) * polys.indices.size());
+ for (int i = 0; i < polys.vertices.size(); ++i)
+ cache->vertices[i] = float(inverseScale * polys.vertices.at(i));
+#endif
+ }
+
+ prepareForDraw(currentBrush.isOpaque());
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+ glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo);
+ setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0);
+ glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+#else
+ setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices);
+ glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, cache->indices);
+#endif
- if (q->state()->clipTestEnabled) {
- // Pass when high bit is set, replace stencil value with current clip
- glStencilFunc(GL_NOTEQUAL, q->state()->currentClip, GL_STENCIL_HIGH_BIT);
- } else if (path.hasWindingFill()) {
- // Pass when any bit is set, replace stencil value with 0
- glStencilFunc(GL_NOTEQUAL, 0, 0xff);
} else {
- // Pass when high bit is set, replace stencil value with 0
- glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT);
- }
- prepareForDraw(currentBrush.isOpaque());
+ // printf(" - Marking path as cachable...\n");
+ // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable
+ if (do_vectorpath_cache)
+ path.makeCacheable();
- // Stencil the brush onto the dest buffer
- composite(vertexCoordinateArray.boundingRect());
- glStencilMask(0);
- updateClipScissorTest();
+ // The path is too complicated & needs the stencil technique
+ vertexCoordinateArray.clear();
+ vertexCoordinateArray.addPath(path, inverseScale, false);
+
+ fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
+
+ glStencilMask(0xff);
+ glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
+
+ if (q->state()->clipTestEnabled) {
+ // Pass when high bit is set, replace stencil value with current clip
+ glStencilFunc(GL_NOTEQUAL, q->state()->currentClip, GL_STENCIL_HIGH_BIT);
+ } else if (path.hasWindingFill()) {
+ // Pass when any bit is set, replace stencil value with 0
+ glStencilFunc(GL_NOTEQUAL, 0, 0xff);
+ } else {
+ // Pass when high bit is set, replace stencil value with 0
+ glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT);
+ }
+ prepareForDraw(currentBrush.isOpaque());
+
+ // Stencil the brush onto the dest buffer
+ composite(vertexCoordinateArray.boundingRect());
+ glStencilMask(0);
+ updateClipScissorTest();
+ }
}
}
@@ -938,6 +1059,7 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque)
// The shader program has changed so mark all uniforms as dirty:
brushUniformsDirty = true;
opacityUniformDirty = true;
+ matrixUniformDirty = true;
}
if (brushUniformsDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode)
@@ -948,6 +1070,12 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque)
opacityUniformDirty = false;
}
+ if (matrixUniformDirty && shaderManager->hasComplexGeometry()) {
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Matrix),
+ pmvMatrix);
+ matrixUniformDirty = false;
+ }
+
return changed;
}
@@ -1048,16 +1176,20 @@ void QGL2PaintEngineExPrivate::stroke(const QVectorPath &path, const QPen &pen)
// prepareForDraw() down below.
updateMatrix();
+ QRectF clip = q->state()->matrix.inverted().mapRect(q->state()->clipEnabled
+ ? q->state()->rectangleClip
+ : QRectF(0, 0, width, height));
+
if (penStyle == Qt::SolidLine) {
- stroker.process(path, pen);
+ stroker.process(path, pen, clip);
} else { // Some sort of dash
- dasher.process(path, pen);
+ dasher.process(path, pen, clip);
QVectorPath dashStroke(dasher.points(),
dasher.elementCount(),
dasher.elementTypes());
- stroker.process(dashStroke, pen);
+ stroker.process(dashStroke, pen, clip);
}
if (opaque) {
@@ -1192,9 +1324,33 @@ void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const
d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel());
}
-void QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src)
+void QGL2PaintEngineEx::drawStaticTextItem(QStaticTextItem *textItem)
+{
+ Q_D(QGL2PaintEngineEx);
+
+ ensureActive();
+
+ QFontEngineGlyphCache::Type glyphType = textItem->fontEngine->glyphFormat >= 0
+ ? QFontEngineGlyphCache::Type(textItem->fontEngine->glyphFormat)
+ : d->glyphCacheType;
+ if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) {
+ if (d->device->alphaRequested() || state()->matrix.type() > QTransform::TxTranslate
+ || (state()->composition_mode != QPainter::CompositionMode_Source
+ && state()->composition_mode != QPainter::CompositionMode_SourceOver))
+ {
+ glyphType = QFontEngineGlyphCache::Raster_A8;
+ }
+ }
+
+ d->drawCachedGlyphs(glyphType, textItem);
+}
+
+bool QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src)
{
Q_D(QGL2PaintEngineEx);
+ if (!d->shaderManager)
+ return false;
+
ensureActive();
d->transferMode(ImageDrawingMode);
@@ -1209,6 +1365,7 @@ void QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const
d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
state()->renderHints & QPainter::SmoothPixmapTransform, textureId);
d->drawTexture(dest, srcRect, size, false);
+ return true;
}
void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem)
@@ -1245,33 +1402,67 @@ void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem
}
if (drawCached) {
- d->drawCachedGlyphs(p, glyphType, ti);
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix = QTransform::fromTranslate(p.x(), p.y());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+
+ {
+ QStaticTextItem staticTextItem;
+ staticTextItem.chars = const_cast<QChar *>(ti.chars);
+ staticTextItem.fontEngine = ti.fontEngine;
+ staticTextItem.glyphs = glyphs.data();
+ staticTextItem.numChars = ti.num_chars;
+ staticTextItem.numGlyphs = glyphs.size();
+ staticTextItem.glyphPositions = positions.data();
+
+ d->drawCachedGlyphs(glyphType, &staticTextItem);
+ }
return;
}
QPaintEngineEx::drawTextItem(p, ti);
}
-void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGlyphCache::Type glyphType,
- const QTextItemInt &ti)
+namespace {
+
+ class QOpenGLStaticTextUserData: public QStaticTextUserData
+ {
+ public:
+ QOpenGLStaticTextUserData()
+ : QStaticTextUserData(OpenGLUserData)
+ {
+ }
+
+ ~QOpenGLStaticTextUserData()
+ {
+ }
+
+ QGL2PEXVertexArray vertexCoordinateArray;
+ QGL2PEXVertexArray textureCoordinateArray;
+ };
+
+}
+
+// #define QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO
+
+void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyphType,
+ QStaticTextItem *staticTextItem)
{
Q_Q(QGL2PaintEngineEx);
- QVarLengthArray<QFixedPoint> positions;
- QVarLengthArray<glyph_t> glyphs;
- QTransform matrix = QTransform::fromTranslate(p.x(), p.y());
- ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ QOpenGL2PaintEngineState *s = q->state();
QGLTextureGlyphCache *cache =
- (QGLTextureGlyphCache *) ti.fontEngine->glyphCache(ctx, glyphType, QTransform());
-
+ (QGLTextureGlyphCache *) staticTextItem->fontEngine->glyphCache(ctx, glyphType, QTransform());
if (!cache || cache->cacheType() != glyphType) {
cache = new QGLTextureGlyphCache(ctx, glyphType, QTransform());
- ti.fontEngine->setGlyphCache(ctx, cache);
+ staticTextItem->fontEngine->setGlyphCache(ctx, cache);
}
cache->setPaintEnginePrivate(this);
- cache->populate(ti, glyphs, positions);
+ cache->populate(staticTextItem->fontEngine, staticTextItem->numGlyphs, staticTextItem->glyphs,
+ staticTextItem->glyphPositions);
if (cache->width() == 0 || cache->height() == 0)
return;
@@ -1283,20 +1474,83 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly
GLfloat dx = 1.0 / cache->width();
GLfloat dy = 1.0 / cache->height();
- vertexCoordinateArray.clear();
- textureCoordinateArray.clear();
+ bool recreateVertexArrays = false;
+ if (staticTextItem->userDataNeedsUpdate)
+ recreateVertexArrays = true;
+ else if (staticTextItem->userData == 0)
+ recreateVertexArrays = true;
+ else if (staticTextItem->userData->type != QStaticTextUserData::OpenGLUserData)
+ recreateVertexArrays = true;
+
+ // Use global arrays by default
+ QGL2PEXVertexArray *vertexCoordinates = &vertexCoordinateArray;
+ QGL2PEXVertexArray *textureCoordinates = &textureCoordinateArray;
+
+ if (staticTextItem->useBackendOptimizations) {
+ QOpenGLStaticTextUserData *userData = 0;
+
+ if (staticTextItem->userData == 0
+ || staticTextItem->userData->type != QStaticTextUserData::OpenGLUserData) {
+
+ userData = new QOpenGLStaticTextUserData();
+ staticTextItem->setUserData(userData);
+
+ } else {
+ userData = static_cast<QOpenGLStaticTextUserData*>(staticTextItem->userData);
+ }
+
+ // Use cache if backend optimizations is turned on
+ vertexCoordinates = &userData->vertexCoordinateArray;
+ textureCoordinates = &userData->textureCoordinateArray;
+ }
+
+
+ if (recreateVertexArrays) {
+ vertexCoordinates->clear();
+ textureCoordinates->clear();
+
+ for (int i=0; i<staticTextItem->numGlyphs; ++i) {
+ const QTextureGlyphCache::Coord &c = cache->coords.value(staticTextItem->glyphs[i]);
+ int x = staticTextItem->glyphPositions[i].x.toInt() + c.baseLineX - margin;
+ int y = staticTextItem->glyphPositions[i].y.toInt() - c.baseLineY - margin;
+
+ vertexCoordinates->addQuad(QRectF(x, y, c.w, c.h));
+ textureCoordinates->addQuad(QRectF(c.x*dx, c.y*dy, c.w * dx, c.h * dy));
+ }
+
+ staticTextItem->userDataNeedsUpdate = false;
+ }
+
+ if (elementIndices.size() < staticTextItem->numGlyphs*6) {
+ Q_ASSERT(elementIndices.size() % 6 == 0);
+ int j = elementIndices.size() / 6 * 4;
+ while (j < staticTextItem->numGlyphs*4) {
+ elementIndices.append(j + 0);
+ elementIndices.append(j + 0);
+ elementIndices.append(j + 1);
+ elementIndices.append(j + 2);
+ elementIndices.append(j + 3);
+ elementIndices.append(j + 3);
- 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;
+ j += 4;
+ }
- vertexCoordinateArray.addRect(QRectF(x, y, c.w, c.h));
- textureCoordinateArray.addRect(QRectF(c.x*dx, c.y*dy, c.w * dx, c.h * dy));
+#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
+ if (elementIndicesVBOId == 0)
+ glGenBuffers(1, &elementIndicesVBOId);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementIndices.size() * sizeof(GLushort),
+ elementIndices.constData(), GL_STATIC_DRAW);
+#endif
+ } else {
+#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId);
+#endif
}
- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data());
- setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data());
+ setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data());
+ setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data());
if (addOffset) {
addOffset = false;
@@ -1363,7 +1617,11 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly
updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::MaskTexture), QT_MASK_TEXTURE_UNIT);
- glDrawArrays(GL_TRIANGLES, 0, 6 * glyphs.size());
+#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
+ glDrawElements(GL_TRIANGLE_STRIP, 6 * staticTextItem->numGlyphs, GL_UNSIGNED_SHORT, 0);
+#else
+ glDrawElements(GL_TRIANGLE_STRIP, 6 * staticTextItem->numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data());
+#endif
shaderManager->setMaskType(QGLEngineShaderManager::SubPixelMaskPass2);
@@ -1389,29 +1647,39 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly
//### TODO: Gamma correction
glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT);
- glBindTexture(GL_TEXTURE_2D, cache->texture());
- QOpenGL2PaintEngineState *s = q->state();
+ if (lastMaskTextureUsed != cache->texture()) {
+ glBindTexture(GL_TEXTURE_2D, cache->texture());
+ lastMaskTextureUsed = cache->texture();
+ }
updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, s->matrix.type() > QTransform::TxTranslate);
-
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::MaskTexture), QT_MASK_TEXTURE_UNIT);
- glDrawArrays(GL_TRIANGLES, 0, 6 * glyphs.size());
+
+#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
+ glDrawElements(GL_TRIANGLE_STRIP, 6 * staticTextItem->numGlyphs, GL_UNSIGNED_SHORT, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+#else
+ glDrawElements(GL_TRIANGLE_STRIP, 6 * staticTextItem->numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data());
+#endif
}
-void QGL2PaintEngineEx::drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints)
+void QGL2PaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap,
+ QPainter::PixmapFragmentHints hints)
{
Q_D(QGL2PaintEngineEx);
// Use fallback for extended composition modes.
if (state()->composition_mode > QPainter::CompositionMode_Plus) {
- QPaintEngineEx::drawPixmaps(drawingData, dataCount, pixmap, hints);
+ QPaintEngineEx::drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
return;
}
ensureActive();
- d->drawPixmaps(drawingData, dataCount, pixmap, hints);
+ d->drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
}
-void QGL2PaintEngineExPrivate::drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints)
+void QGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragment *fragments,
+ int fragmentCount, const QPixmap &pixmap,
+ QPainter::PixmapFragmentHints hints)
{
GLfloat dx = 1.0f / pixmap.size().width();
GLfloat dy = 1.0f / pixmap.size().height();
@@ -1432,37 +1700,38 @@ void QGL2PaintEngineExPrivate::drawPixmaps(const QDrawPixmaps::Data *drawingData
bool allOpaque = true;
- for (int i = 0; i < dataCount; ++i) {
+ for (int i = 0; i < fragmentCount; ++i) {
qreal s = 0;
qreal c = 1;
- if (drawingData[i].rotation != 0) {
- s = qFastSin(drawingData[i].rotation * Q_PI / 180);
- c = qFastCos(drawingData[i].rotation * Q_PI / 180);
+ if (fragments[i].rotation != 0) {
+ s = qFastSin(fragments[i].rotation * Q_PI / 180);
+ c = qFastCos(fragments[i].rotation * Q_PI / 180);
}
- qreal right = 0.5 * drawingData[i].scaleX * drawingData[i].source.width();
- qreal bottom = 0.5 * drawingData[i].scaleY * drawingData[i].source.height();
+ qreal right = 0.5 * fragments[i].scaleX * fragments[i].width;
+ qreal bottom = 0.5 * fragments[i].scaleY * fragments[i].height;
QGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c);
QGLPoint bottomLeft(-right * c - bottom * s, -right * s + bottom * c);
- vertexCoordinateArray.lineToArray(bottomRight.x + drawingData[i].point.x(), bottomRight.y + drawingData[i].point.y());
- vertexCoordinateArray.lineToArray(-bottomLeft.x + drawingData[i].point.x(), -bottomLeft.y + drawingData[i].point.y());
- vertexCoordinateArray.lineToArray(-bottomRight.x + drawingData[i].point.x(), -bottomRight.y + drawingData[i].point.y());
- vertexCoordinateArray.lineToArray(-bottomRight.x + drawingData[i].point.x(), -bottomRight.y + drawingData[i].point.y());
- vertexCoordinateArray.lineToArray(bottomLeft.x + drawingData[i].point.x(), bottomLeft.y + drawingData[i].point.y());
- vertexCoordinateArray.lineToArray(bottomRight.x + drawingData[i].point.x(), bottomRight.y + drawingData[i].point.y());
-
- QGLRect src(drawingData[i].source.left() * dx, drawingData[i].source.top() * dy,
- drawingData[i].source.right() * dx, drawingData[i].source.bottom() * dy);
-
- textureCoordinateArray.lineToArray(src.right, src.bottom);
- textureCoordinateArray.lineToArray(src.right, src.top);
- textureCoordinateArray.lineToArray(src.left, src.top);
- textureCoordinateArray.lineToArray(src.left, src.top);
- textureCoordinateArray.lineToArray(src.left, src.bottom);
- textureCoordinateArray.lineToArray(src.right, src.bottom);
-
- qreal opacity = drawingData[i].opacity * q->state()->opacity;
+ vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y);
+ vertexCoordinateArray.addVertex(-bottomLeft.x + fragments[i].x, -bottomLeft.y + fragments[i].y);
+ vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y);
+ vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y);
+ vertexCoordinateArray.addVertex(bottomLeft.x + fragments[i].x, bottomLeft.y + fragments[i].y);
+ vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y);
+
+ QGLRect src(fragments[i].sourceLeft * dx, fragments[i].sourceTop * dy,
+ (fragments[i].sourceLeft + fragments[i].width) * dx,
+ (fragments[i].sourceTop + fragments[i].height) * dy);
+
+ textureCoordinateArray.addVertex(src.right, src.bottom);
+ textureCoordinateArray.addVertex(src.right, src.top);
+ textureCoordinateArray.addVertex(src.left, src.top);
+ textureCoordinateArray.addVertex(src.left, src.top);
+ textureCoordinateArray.addVertex(src.left, src.bottom);
+ textureCoordinateArray.addVertex(src.right, src.bottom);
+
+ qreal opacity = fragments[i].opacity * q->state()->opacity;
opacityArray << opacity << opacity << opacity << opacity << opacity << opacity;
allOpaque &= (opacity >= 0.99f);
}
@@ -1475,21 +1744,22 @@ void QGL2PaintEngineExPrivate::drawPixmaps(const QDrawPixmaps::Data *drawingData
if (texture->options & QGLContext::InvertedYBindOption) {
// Flip texture y-coordinate.
QGLPoint *data = textureCoordinateArray.data();
- for (int i = 0; i < 6 * dataCount; ++i)
+ for (int i = 0; i < 6 * fragmentCount; ++i)
data[i].y = 1 - data[i].y;
}
transferMode(ImageArrayDrawingMode);
bool isBitmap = pixmap.isQBitmap();
- bool isOpaque = !isBitmap && (!pixmap.hasAlphaChannel() || (hints & QDrawPixmaps::OpaqueHint)) && allOpaque;
+ bool isOpaque = !isBitmap && (!pixmap.hasAlphaChannel() || (hints & QPainter::OpaqueHint)) && allOpaque;
updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
q->state()->renderHints & QPainter::SmoothPixmapTransform, texture->id);
// Setup for texture drawing
currentBrush = noBrush;
- shaderManager->setSrcPixelType(isBitmap ? QGLEngineShaderManager::PatternSrc : QGLEngineShaderManager::ImageSrc);
+ shaderManager->setSrcPixelType(isBitmap ? QGLEngineShaderManager::PatternSrc
+ : QGLEngineShaderManager::ImageSrc);
if (prepareForDraw(isOpaque))
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
@@ -1498,7 +1768,7 @@ void QGL2PaintEngineExPrivate::drawPixmaps(const QDrawPixmaps::Data *drawingData
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
}
- glDrawArrays(GL_TRIANGLES, 0, 6 * dataCount);
+ glDrawArrays(GL_TRIANGLES, 0, 6 * fragmentCount);
}
bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
@@ -1523,6 +1793,7 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
d->mode = BrushDrawingMode;
d->brushTextureDirty = true;
d->brushUniformsDirty = true;
+ d->matrixUniformDirty = true;
d->matrixDirty = true;
d->compositionModeDirty = true;
d->opacityUniformDirty = true;
@@ -1605,6 +1876,10 @@ bool QGL2PaintEngineEx::end()
glDeleteBuffers(d->unusedVBOSToClean.size(), d->unusedVBOSToClean.constData());
d->unusedVBOSToClean.clear();
}
+ if (!d->unusedIBOSToClean.isEmpty()) {
+ glDeleteBuffers(d->unusedIBOSToClean.size(), d->unusedIBOSToClean.constData());
+ d->unusedIBOSToClean.clear();
+ }
#endif
return false;
@@ -1626,6 +1901,7 @@ void QGL2PaintEngineEx::ensureActive()
d->transferMode(BrushDrawingMode);
glViewport(0, 0, d->width, d->height);
d->needsSync = false;
+ d->lastMaskTextureUsed = 0;
d->shaderManager->setDirty();
d->ctx->d_func()->syncGlState();
for (int i = 0; i < 3; ++i)
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
index 8fa0eff..59b90d8 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
@@ -123,9 +123,9 @@ public:
virtual void renderHintsChanged();
virtual void transformChanged();
- virtual void drawTexture(const QRectF &r, GLuint textureId, const QSize &size, const QRectF &sr);
virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
- virtual void drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints);
+ virtual void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap,
+ QPainter::PixmapFragmentHints hints);
virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
Qt::ImageConversionFlags flags = Qt::AutoColor);
virtual void drawTextItem(const QPointF &p, const QTextItem &textItem);
@@ -133,6 +133,9 @@ public:
virtual void stroke(const QVectorPath &path, const QPen &pen);
virtual void clip(const QVectorPath &path, Qt::ClipOperation op);
+ virtual void drawStaticTextItem(QStaticTextItem *textItem);
+
+ bool drawTexture(const QRectF &r, GLuint textureId, const QSize &size, const QRectF &sr);
Type type() const { return OpenGL2; }
@@ -152,11 +155,11 @@ public:
void setRenderTextActive(bool);
+ bool isNativePaintingActive() const;
private:
Q_DISABLE_COPY(QGL2PaintEngineEx)
};
-
class QGL2PaintEngineExPrivate : public QPaintEngineExPrivate
{
Q_DECLARE_PUBLIC(QGL2PaintEngineEx)
@@ -173,9 +176,13 @@ public:
width(0), height(0),
ctx(0),
useSystemClip(true),
+ elementIndicesVBOId(0),
+ opacityArray(0),
snapToPixelGrid(false),
addOffset(false),
- inverseScale(1)
+ nativePaintingActive(false),
+ inverseScale(1),
+ lastMaskTextureUsed(0)
{ }
~QGL2PaintEngineExPrivate();
@@ -193,8 +200,9 @@ public:
void fill(const QVectorPath &path);
void stroke(const QVectorPath &path, const QPen &pen);
void drawTexture(const QGLRect& dest, const QGLRect& src, const QSize &textureSize, bool opaque, bool pattern = false);
- void drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints);
- void drawCachedGlyphs(const QPointF &p, QFontEngineGlyphCache::Type glyphType, const QTextItemInt &ti);
+ void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap,
+ QPainter::PixmapFragmentHints hints);
+ void drawCachedGlyphs(QFontEngineGlyphCache::Type glyphType, QStaticTextItem *staticTextItem);
// Calls glVertexAttributePointer if the pointer has changed
inline void setVertexAttributePointer(unsigned int arrayIndex, const GLfloat *pointer);
@@ -253,6 +261,7 @@ public:
bool brushTextureDirty;
bool brushUniformsDirty;
bool opacityUniformDirty;
+ bool matrixUniformDirty;
bool stencilClean; // Has the stencil not been used for clipping so far?
bool useSystemClip;
@@ -265,16 +274,20 @@ public:
QGL2PEXVertexArray vertexCoordinateArray;
QGL2PEXVertexArray textureCoordinateArray;
+ QVector<GLushort> elementIndices;
+ GLuint elementIndicesVBOId;
QDataBuffer<GLfloat> opacityArray;
GLfloat staticVertexCoordinateArray[8];
GLfloat staticTextureCoordinateArray[8];
bool snapToPixelGrid;
bool addOffset; // When enabled, adds a 0.49,0.49 offset to matrix in updateMatrix
+ bool nativePaintingActive;
GLfloat pmvMatrix[3][3];
GLfloat inverseScale;
GLuint lastTextureUsed;
+ GLuint lastMaskTextureUsed;
bool needsSync;
bool multisamplingAlwaysEnabled;
@@ -293,6 +306,7 @@ public:
QSet<QVectorPath::CacheEntry *> pathCaches;
QVector<GLuint> unusedVBOSToClean;
+ QVector<GLuint> unusedIBOSToClean;
const GLfloat *vertexAttribPointers[3];
};
diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp
index 994c1c9..410cf21 100644
--- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp
+++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp
@@ -42,6 +42,10 @@
#include "qtextureglyphcache_gl_p.h"
#include "qpaintengineex_opengl2_p.h"
+#if defined QT_OPENGL_ES_2 && !defined(QT_NO_EGL)
+#include "private/qeglcontext_p.h"
+#endif
+
QT_BEGIN_NAMESPACE
#ifdef Q_WS_WIN
@@ -49,12 +53,19 @@ extern Q_GUI_EXPORT bool qt_cleartype_enabled;
#endif
QGLTextureGlyphCache::QGLTextureGlyphCache(QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix)
- : QTextureGlyphCache(type, matrix)
+ : QImageTextureGlyphCache(type, matrix)
, ctx(context)
, m_width(0)
, m_height(0)
{
- glGenFramebuffers(1, &m_fbo);
+ // broken FBO readback is a bug in the SGX 1.3 and 1.4 drivers for the N900 where
+ // copying between FBO's is broken if the texture is either GL_ALPHA or POT. The
+ // workaround is to use a system-memory copy of the glyph cache for this device.
+ // Switching to NPOT and GL_RGBA would both cost a lot more graphics memory and
+ // be slower, so that is not desireable.
+ if (!ctx->d_ptr->workaround_brokenFBOReadBack)
+ glGenFramebuffers(1, &m_fbo);
+
connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)),
SLOT(contextDestroyed(const QGLContext*)));
}
@@ -63,7 +74,9 @@ QGLTextureGlyphCache::~QGLTextureGlyphCache()
{
if (ctx) {
QGLShareContextScope scope(ctx);
- glDeleteFramebuffers(1, &m_fbo);
+
+ if (!ctx->d_ptr->workaround_brokenFBOReadBack)
+ glDeleteFramebuffers(1, &m_fbo);
if (m_width || m_height)
glDeleteTextures(1, &m_texture);
@@ -72,6 +85,18 @@ QGLTextureGlyphCache::~QGLTextureGlyphCache()
void QGLTextureGlyphCache::createTextureData(int width, int height)
{
+ // create in QImageTextureGlyphCache baseclass is meant to be called
+ // only to create the initial image and does not preserve the content,
+ // so we don't call when this function is called from resize.
+ if (ctx->d_ptr->workaround_brokenFBOReadBack && image().isNull())
+ QImageTextureGlyphCache::createTextureData(width, height);
+
+ // Make the lower glyph texture size 16 x 16.
+ if (width < 16)
+ width = 16;
+ if (height < 16)
+ height = 16;
+
glGenTextures(1, &m_texture);
glBindTexture(GL_TEXTURE_2D, m_texture);
@@ -93,14 +118,28 @@ void QGLTextureGlyphCache::createTextureData(int width, int height)
void QGLTextureGlyphCache::resizeTextureData(int width, int height)
{
- // ### the QTextureGlyphCache API needs to be reworked to allow
- // ### resizeTextureData to fail
-
int oldWidth = m_width;
int oldHeight = m_height;
+ // Make the lower glyph texture size 16 x 16.
+ if (width < 16)
+ width = 16;
+ if (height < 16)
+ height = 16;
+
GLuint oldTexture = m_texture;
createTextureData(width, height);
+
+ if (ctx->d_ptr->workaround_brokenFBOReadBack) {
+ QImageTextureGlyphCache::resizeTextureData(width, height);
+ Q_ASSERT(image().depth() == 8);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, oldWidth, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, image().constBits());
+ glDeleteTextures(1, &oldTexture);
+ return;
+ }
+
+ // ### the QTextureGlyphCache API needs to be reworked to allow
+ // ### resizeTextureData to fail
glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fbo);
@@ -159,20 +198,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height)
glBindTexture(GL_TEXTURE_2D, m_texture);
-#ifdef QT_OPENGL_ES_2
- QDataBuffer<uchar> buffer(4*oldWidth*oldHeight);
- buffer.resize(4*oldWidth*oldHeight);
- glReadPixels(0, 0, oldWidth, oldHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
-
- // do an in-place conversion from GL_RGBA to GL_ALPHA
- for (int i=0; i<oldWidth*oldHeight; ++i)
- buffer.data()[i] = buffer.at(4*i + 3);
-
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, oldWidth, oldHeight,
- GL_ALPHA, GL_UNSIGNED_BYTE, buffer.data());
-#else
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
-#endif
glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_RENDERBUFFER_EXT, 0);
@@ -187,6 +213,21 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height)
void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph)
{
+ if (ctx->d_ptr->workaround_brokenFBOReadBack) {
+ QImageTextureGlyphCache::fillTexture(c, glyph);
+
+ glBindTexture(GL_TEXTURE_2D, m_texture);
+ const QImage &texture = image();
+ const uchar *bits = texture.constBits();
+ bits += c.y * texture.bytesPerLine() + c.x;
+ for (int i=0; i<c.h; ++i) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, c.w, 1, GL_ALPHA, GL_UNSIGNED_BYTE, bits);
+ bits += texture.bytesPerLine();
+ }
+
+ return;
+ }
+
QImage mask = textureMapForGlyph(glyph);
const int maskWidth = mask.width();
const int maskHeight = mask.height();
@@ -235,17 +276,6 @@ void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph)
}
}
-int QGLTextureGlyphCache::glyphMargin() const
-{
-#if defined(Q_WS_MAC)
- return 2;
-#elif defined (Q_WS_X11)
- return 0;
-#else
- return m_type == QFontEngineGlyphCache::Raster_RGBMask ? 2 : 0;
-#endif
-}
-
int QGLTextureGlyphCache::glyphPadding() const
{
return 1;
diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h
index 04731b1..6bcd655 100644
--- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h
+++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h
@@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE
class QGL2PaintEngineExPrivate;
-class QGLTextureGlyphCache : public QObject, public QTextureGlyphCache
+class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QObject, public QImageTextureGlyphCache
{
Q_OBJECT
public:
@@ -72,7 +72,6 @@ public:
virtual void createTextureData(int width, int height);
virtual void resizeTextureData(int width, int height);
virtual void fillTexture(const Coord &c, glyph_t glyph);
- virtual int glyphMargin() const;
virtual int glyphPadding() const;
inline GLuint texture() const { return m_texture; }
diff --git a/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp b/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp
index eaa3d57..9bc099d 100644
--- a/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp
+++ b/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp
@@ -73,7 +73,7 @@ void QTriangulatingStroker::endCapOrJoinClosed(const qreal *start, const qreal *
}
-void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen)
+void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen, const QRectF &)
{
const qreal *pts = path.points();
const QPainterPath::ElementType *types = path.elements();
@@ -111,7 +111,7 @@ void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen)
// depending on if the pen is cosmetic or not.
//
// The curvyness value of PI/14 was based on,
- // arcLength=2*PI*r/4=PI/2 and splitting length into somewhere
+ // arcLength = 2*PI*r/4 = PI*r/2 and splitting length into somewhere
// between 3 and 8 where 5 seemed to be give pretty good results
// hence: Q_PI/14. Lower divisors will give more detail at the
// direct cost of performance.
@@ -481,31 +481,53 @@ static void qdashprocessor_cubicTo(qreal, qreal, qreal, qreal, qreal, qreal, voi
}
QDashedStrokeProcessor::QDashedStrokeProcessor()
- : m_dash_stroker(0), m_inv_scale(1)
+ : m_points(0), m_types(0),
+ m_dash_stroker(0), m_inv_scale(1)
{
m_dash_stroker.setMoveToHook(qdashprocessor_moveTo);
m_dash_stroker.setLineToHook(qdashprocessor_lineTo);
m_dash_stroker.setCubicToHook(qdashprocessor_cubicTo);
}
-void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen)
+void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen, const QRectF &clip)
{
const qreal *pts = path.points();
const QPainterPath::ElementType *types = path.elements();
int count = path.elementCount();
+ bool cosmetic = pen.isCosmetic();
+
m_points.reset();
m_types.reset();
+ m_points.reserve(path.elementCount());
+ m_types.reserve(path.elementCount());
qreal width = qpen_widthf(pen);
if (width == 0)
width = 1;
m_dash_stroker.setDashPattern(pen.dashPattern());
- m_dash_stroker.setStrokeWidth(pen.isCosmetic() ? width * m_inv_scale : width);
+ m_dash_stroker.setStrokeWidth(cosmetic ? width * m_inv_scale : width);
m_dash_stroker.setMiterLimit(pen.miterLimit());
- qreal curvyness = sqrt(width) * m_inv_scale / 8;
+ m_dash_stroker.setClipRect(clip);
+
+ float curvynessAdd, curvynessMul, roundness = 0;
+
+ // simplfy pens that are thin in device size (2px wide or less)
+ if (width < 2.5 && (cosmetic || m_inv_scale == 1)) {
+ curvynessAdd = 0.5;
+ curvynessMul = CURVE_FLATNESS / m_inv_scale;
+ roundness = 1;
+ } else if (cosmetic) {
+ curvynessAdd= width / 2;
+ curvynessMul= CURVE_FLATNESS;
+ roundness = qMax<int>(4, width * CURVE_FLATNESS);
+ } else {
+ curvynessAdd = width * m_inv_scale;
+ curvynessMul = CURVE_FLATNESS / m_inv_scale;
+ roundness = qMax<int>(4, width * curvynessMul);
+ }
if (count < 2)
return;
@@ -540,9 +562,11 @@ void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen)
*(((const QPointF *) pts) + 1),
*(((const QPointF *) pts) + 2));
QRectF bounds = b.bounds();
- int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * curvyness);
+ float rad = qMax(bounds.width(), bounds.height());
+ int threshold = qMin<float>(64, (rad + curvynessAdd) * curvynessMul);
if (threshold < 4)
threshold = 4;
+
qreal threshold_minus_1 = threshold - 1;
for (int i=0; i<threshold; ++i) {
QPointF pt = b.pointAt(i / threshold_minus_1);
diff --git a/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h b/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h
index 06b8a44..ab27ed6 100644
--- a/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h
+++ b/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h
@@ -54,7 +54,8 @@ QT_BEGIN_NAMESPACE
class QTriangulatingStroker
{
public:
- void process(const QVectorPath &path, const QPen &pen);
+ QTriangulatingStroker() : m_vertices(0) {}
+ void process(const QVectorPath &path, const QPen &pen, const QRectF &clip);
inline int vertexCount() const { return m_vertices.size(); }
inline const float *vertices() const { return m_vertices.data(); }
@@ -96,7 +97,7 @@ class QDashedStrokeProcessor
public:
QDashedStrokeProcessor();
- void process(const QVectorPath &path, const QPen &pen);
+ void process(const QVectorPath &path, const QPen &pen, const QRectF &clip);
inline void addElement(QPainterPath::ElementType type, qreal x, qreal y) {
m_points.add(x);
diff --git a/src/opengl/gl2paintengineex/qtriangulator.cpp b/src/opengl/gl2paintengineex/qtriangulator.cpp
new file mode 100644
index 0000000..df7cbc2
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qtriangulator.cpp
@@ -0,0 +1,2985 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtriangulator_p.h"
+
+#include <QtGui/qdialog.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpainterpath.h>
+#include <QtGui/private/qbezier_p.h>
+#include <QtGui/private/qdatabuffer_p.h>
+#include <QtCore/qbitarray.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qalgorithms.h>
+#include <QtDebug>
+
+#include <math.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define Q_TRIANGULATOR_DEBUG
+
+#define Q_FIXED_POINT_SCALE 32
+
+// Quick sort.
+template <class T, class LessThan>
+static void sort(T *array, int count, LessThan lessThan)
+{
+ // If the number of elements fall below some threshold, use insertion sort.
+ const int INSERTION_SORT_LIMIT = 7; // About 7 is fastest on my computer...
+ if (count <= INSERTION_SORT_LIMIT) {
+ for (int i = 1; i < count; ++i) {
+ T temp = array[i];
+ int j = i;
+ while (j > 0 && lessThan(temp, array[j - 1])) {
+ array[j] = array[j - 1];
+ --j;
+ }
+ array[j] = temp;
+ }
+ return;
+ }
+
+ int high = count - 1;
+ int low = 0;
+ int mid = high / 2;
+ if (lessThan(array[mid], array[low]))
+ qSwap(array[mid], array[low]);
+ if (lessThan(array[high], array[mid]))
+ qSwap(array[high], array[mid]);
+ if (lessThan(array[mid], array[low]))
+ qSwap(array[mid], array[low]);
+
+ --high;
+ ++low;
+ qSwap(array[mid], array[high]);
+ int pivot = high;
+ --high;
+
+ while (low <= high) {
+ while (!lessThan(array[pivot], array[low])) {
+ ++low;
+ if (low > high)
+ goto sort_loop_end;
+ }
+ while (!lessThan(array[high], array[pivot])) {
+ --high;
+ if (low > high)
+ goto sort_loop_end;
+ }
+ qSwap(array[low], array[high]);
+ ++low;
+ --high;
+ }
+sort_loop_end:
+ if (low != pivot)
+ qSwap(array[pivot], array[low]);
+ sort(array, low, lessThan);
+ sort(array + low + 1, count - low - 1, lessThan);
+}
+
+// Quick sort.
+template <class T>
+static void sort(T *array, int count)
+{
+ // If the number of elements fall below some threshold, use insertion sort.
+ const int INSERTION_SORT_LIMIT = 25; // About 25 is fastest on my computer...
+ if (count <= INSERTION_SORT_LIMIT) {
+ for (int i = 1; i < count; ++i) {
+ T temp = array[i];
+ int j = i;
+ while (j > 0 && (temp < array[j - 1])) {
+ array[j] = array[j - 1];
+ --j;
+ }
+ array[j] = temp;
+ }
+ return;
+ }
+
+ int high = count - 1;
+ int low = 0;
+ int mid = high / 2;
+ if ((array[mid] < array[low]))
+ qSwap(array[mid], array[low]);
+ if ((array[high] < array[mid]))
+ qSwap(array[high], array[mid]);
+ if ((array[mid] < array[low]))
+ qSwap(array[mid], array[low]);
+
+ --high;
+ ++low;
+ qSwap(array[mid], array[high]);
+ int pivot = high;
+ --high;
+
+ while (low <= high) {
+ while (!(array[pivot] < array[low])) {
+ ++low;
+ if (low > high)
+ goto sort_loop_end;
+ }
+ while (!(array[high] < array[pivot])) {
+ --high;
+ if (low > high)
+ goto sort_loop_end;
+ }
+ qSwap(array[low], array[high]);
+ ++low;
+ --high;
+ }
+sort_loop_end:
+ if (low != pivot)
+ qSwap(array[pivot], array[low]);
+ sort(array, low);
+ sort(array + low + 1, count - low - 1);
+}
+
+//============================================================================//
+// QFraction //
+//============================================================================//
+
+// Fraction must be in the range [0, 1)
+struct QFraction
+{
+ // Comparison operators must not be called on invalid fractions.
+ inline bool operator < (const QFraction &other) const;
+ inline bool operator == (const QFraction &other) const;
+ inline bool operator != (const QFraction &other) const {return !(*this == other);}
+ inline bool operator > (const QFraction &other) const {return other < *this;}
+ inline bool operator >= (const QFraction &other) const {return !(*this < other);}
+ inline bool operator <= (const QFraction &other) const {return !(*this > other);}
+
+ inline bool isValid() const {return denominator != 0;}
+
+ // numerator and denominator must not have common denominators.
+ quint64 numerator, denominator;
+};
+
+static inline quint64 gcd(quint64 x, quint64 y)
+{
+ while (y != 0) {
+ quint64 z = y;
+ y = x % y;
+ x = z;
+ }
+ return x;
+}
+
+static inline int compare(quint64 a, quint64 b)
+{
+ return (a > b) - (a < b);
+}
+
+// Compare a/b with c/d.
+// Return negative if less, 0 if equal, positive if greater.
+// a < b, c < d
+static int qCompareFractions(quint64 a, quint64 b, quint64 c, quint64 d)
+{
+ const quint64 LIMIT = Q_UINT64_C(0x100000000);
+ for (;;) {
+ // If the products 'ad' and 'bc' fit into 64 bits, they can be directly compared.
+ if (b < LIMIT && d < LIMIT)
+ return compare(a * d, b * c);
+
+ if (a == 0 || c == 0)
+ return compare(a, c);
+
+ // a/b < c/d <=> d/c < b/a
+ quint64 b_div_a = b / a;
+ quint64 d_div_c = d / c;
+ if (b_div_a != d_div_c)
+ return compare(d_div_c, b_div_a);
+
+ // floor(d/c) == floor(b/a)
+ // frac(d/c) < frac(b/a) ?
+ // frac(x/y) = (x%y)/y
+ d -= d_div_c * c; //d %= c;
+ b -= b_div_a * a; //b %= a;
+ qSwap(a, d);
+ qSwap(b, c);
+ }
+}
+
+// Fraction must be in the range [0, 1)
+// Assume input is valid.
+static QFraction qFraction(quint64 n, quint64 d) {
+ QFraction result;
+ if (n == 0) {
+ result.numerator = 0;
+ result.denominator = 1;
+ } else {
+ quint64 g = gcd(n, d);
+ result.numerator = n / g;
+ result.denominator = d / g;
+ }
+ return result;
+}
+
+inline bool QFraction::operator < (const QFraction &other) const
+{
+ return qCompareFractions(numerator, denominator, other.numerator, other.denominator) < 0;
+}
+
+inline bool QFraction::operator == (const QFraction &other) const
+{
+ return numerator == other.numerator && denominator == other.denominator;
+}
+
+//============================================================================//
+// QPodPoint //
+//============================================================================//
+
+struct QPodPoint
+{
+ inline bool operator < (const QPodPoint &other) const
+ {
+ if (y != other.y)
+ return y < other.y;
+ return x < other.x;
+ }
+
+ inline bool operator > (const QPodPoint &other) const {return other < *this;}
+ inline bool operator <= (const QPodPoint &other) const {return !(*this > other);}
+ inline bool operator >= (const QPodPoint &other) const {return !(*this < other);}
+ inline bool operator == (const QPodPoint &other) const {return x == other.x && y == other.y;}
+ inline bool operator != (const QPodPoint &other) const {return x != other.x || y != other.y;}
+
+ inline QPodPoint &operator += (const QPodPoint &other) {x += other.x; y += other.y; return *this;}
+ inline QPodPoint &operator -= (const QPodPoint &other) {x -= other.x; y -= other.y; return *this;}
+ inline QPodPoint operator + (const QPodPoint &other) const {QPodPoint result = {x + other.x, y + other.y}; return result;}
+ inline QPodPoint operator - (const QPodPoint &other) const {QPodPoint result = {x - other.x, y - other.y}; return result;}
+
+ int x;
+ int y;
+};
+
+static inline qint64 qCross(const QPodPoint &u, const QPodPoint &v)
+{
+ return qint64(u.x) * qint64(v.y) - qint64(u.y) * qint64(v.x);
+}
+
+static inline qint64 qDot(const QPodPoint &u, const QPodPoint &v)
+{
+ return qint64(u.x) * qint64(v.x) + qint64(u.y) * qint64(v.y);
+}
+
+// Return positive value if 'p' is to the right of the line 'v1'->'v2', negative if left of the
+// line and zero if exactly on the line.
+// The returned value is the z-component of the qCross product between 'v2-v1' and 'p-v1',
+// which is twice the signed area of the triangle 'p'->'v1'->'v2' (positive for CW order).
+static inline qint64 qPointDistanceFromLine(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2)
+{
+ return qCross(v2 - v1, p - v1);
+}
+
+static inline bool qPointIsLeftOfLine(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2)
+{
+ return qPointDistanceFromLine(p, v1, v2) < 0;
+}
+
+// Return:
+// -1 if u < v
+// 0 if u == v
+// 1 if u > v
+static int comparePoints(const QPodPoint &u, const QPodPoint &v)
+{
+ if (u.y < v.y)
+ return -1;
+ if (u.y > v.y)
+ return 1;
+ if (u.x < v.x)
+ return -1;
+ if (u.x > v.x)
+ return 1;
+ return 0;
+}
+
+//============================================================================//
+// QIntersectionPoint //
+//============================================================================//
+
+struct QIntersectionPoint
+{
+ inline bool isValid() const {return xOffset.isValid() && yOffset.isValid();}
+ QPodPoint round() const;
+ inline bool isAccurate() const {return xOffset.numerator == 0 && yOffset.numerator == 0;}
+ bool operator < (const QIntersectionPoint &other) const;
+ bool operator == (const QIntersectionPoint &other) const;
+ inline bool operator != (const QIntersectionPoint &other) const {return !(*this == other);}
+ inline bool operator > (const QIntersectionPoint &other) const {return other < *this;}
+ inline bool operator >= (const QIntersectionPoint &other) const {return !(*this < other);}
+ inline bool operator <= (const QIntersectionPoint &other) const {return !(*this > other);}
+ bool isOnLine(const QPodPoint &u, const QPodPoint &v) const;
+
+ QPodPoint upperLeft;
+ QFraction xOffset;
+ QFraction yOffset;
+};
+
+static inline QIntersectionPoint qIntersectionPoint(const QPodPoint &point)
+{
+ // upperLeft = point, xOffset = 0/1, yOffset = 0/1.
+ QIntersectionPoint p = {{point.x, point.y}, {0, 1}, {0, 1}};
+ return p;
+}
+
+static inline QIntersectionPoint qIntersectionPoint(int x, int y)
+{
+ // upperLeft = (x, y), xOffset = 0/1, yOffset = 0/1.
+ QIntersectionPoint p = {{x, y}, {0, 1}, {0, 1}};
+ return p;
+}
+
+static QIntersectionPoint qIntersectionPoint(const QPodPoint &u1, const QPodPoint &u2, const QPodPoint &v1, const QPodPoint &v2)
+{
+ QIntersectionPoint result = {{0, 0}, {0, 0}, {0, 0}};
+
+ QPodPoint u = u2 - u1;
+ QPodPoint v = v2 - v1;
+ qint64 d1 = qCross(u, v1 - u1);
+ qint64 d2 = qCross(u, v2 - u1);
+ qint64 det = d2 - d1;
+ qint64 d3 = qCross(v, u1 - v1);
+ qint64 d4 = d3 - det; //qCross(v, u2 - v1);
+
+ // Check that the math is correct.
+ Q_ASSERT(d4 == qCross(v, u2 - v1));
+
+ // The intersection point can be expressed as:
+ // v1 - v * d1/det
+ // v2 - v * d2/det
+ // u1 + u * d3/det
+ // u2 + u * d4/det
+
+ // I'm only interested in lines that are crossing, so ignore parallel lines even if they overlap.
+ if (det == 0)
+ return result;
+
+ if (det < 0) {
+ det = -det;
+ d1 = -d1;
+ d2 = -d2;
+ d3 = -d3;
+ d4 = -d4;
+ }
+
+ // I'm only interested in lines intersecting at their interior, not at their end points.
+ // The lines intersect at their interior if and only if 'd1 < 0', 'd2 > 0', 'd3 < 0' and 'd4 > 0'.
+ if (d1 >= 0 || d2 <= 0 || d3 <= 0 || d4 >= 0)
+ return result;
+
+ // Calculate the intersection point as follows:
+ // v1 - v * d1/det | v1 <= v2 (component-wise)
+ // v2 - v * d2/det | v2 < v1 (component-wise)
+
+ // Assuming 21 bits per vector component.
+ // TODO: Make code path for 31 bits per vector component.
+ if (v.x >= 0) {
+ result.upperLeft.x = v1.x + (-v.x * d1) / det;
+ result.xOffset = qFraction(quint64(-v.x * d1) % quint64(det), quint64(det));
+ } else {
+ result.upperLeft.x = v2.x + (-v.x * d2) / det;
+ result.xOffset = qFraction(quint64(-v.x * d2) % quint64(det), quint64(det));
+ }
+
+ if (v.y >= 0) {
+ result.upperLeft.y = v1.y + (-v.y * d1) / det;
+ result.yOffset = qFraction(quint64(-v.y * d1) % quint64(det), quint64(det));
+ } else {
+ result.upperLeft.y = v2.y + (-v.y * d2) / det;
+ result.yOffset = qFraction(quint64(-v.y * d2) % quint64(det), quint64(det));
+ }
+
+ Q_ASSERT(result.xOffset.isValid());
+ Q_ASSERT(result.yOffset.isValid());
+ return result;
+}
+
+QPodPoint QIntersectionPoint::round() const
+{
+ QPodPoint result = upperLeft;
+ if (2 * xOffset.numerator >= xOffset.denominator)
+ ++result.x;
+ if (2 * yOffset.numerator >= yOffset.denominator)
+ ++result.y;
+ return result;
+}
+
+bool QIntersectionPoint::operator < (const QIntersectionPoint &other) const
+{
+ if (upperLeft.y != other.upperLeft.y)
+ return upperLeft.y < other.upperLeft.y;
+ if (yOffset != other.yOffset)
+ return yOffset < other.yOffset;
+ if (upperLeft.x != other.upperLeft.x)
+ return upperLeft.x < other.upperLeft.x;
+ return xOffset < other.xOffset;
+}
+
+bool QIntersectionPoint::operator == (const QIntersectionPoint &other) const
+{
+ return upperLeft == other.upperLeft && xOffset == other.xOffset && yOffset == other.yOffset;
+}
+
+// Returns true if this point is on the infinite line passing through 'u' and 'v'.
+bool QIntersectionPoint::isOnLine(const QPodPoint &u, const QPodPoint &v) const
+{
+ // TODO: Make code path for coordinates with more than 21 bits.
+ const QPodPoint p = upperLeft - u;
+ const QPodPoint q = v - u;
+ bool isHorizontal = p.y == 0 && yOffset.numerator == 0;
+ bool isVertical = p.x == 0 && xOffset.numerator == 0;
+ if (isHorizontal && isVertical)
+ return true;
+ if (isHorizontal)
+ return q.y == 0;
+ if (q.y == 0)
+ return false;
+ if (isVertical)
+ return q.x == 0;
+ if (q.x == 0)
+ return false;
+
+ // At this point, 'p+offset' and 'q' cannot lie on the x or y axis.
+
+ if (((q.x < 0) == (q.y < 0)) != ((p.x < 0) == (p.y < 0)))
+ return false; // 'p + offset' and 'q' pass through different quadrants.
+
+ // Move all coordinates into the first quadrant.
+ quint64 nx, ny;
+ if (p.x < 0)
+ nx = quint64(-p.x) * xOffset.denominator - xOffset.numerator;
+ else
+ nx = quint64(p.x) * xOffset.denominator + xOffset.numerator;
+ if (p.y < 0)
+ ny = quint64(-p.y) * yOffset.denominator - yOffset.numerator;
+ else
+ ny = quint64(p.y) * yOffset.denominator + yOffset.numerator;
+
+ return qFraction(quint64(qAbs(q.x)) * xOffset.denominator, quint64(qAbs(q.y)) * yOffset.denominator) == qFraction(nx, ny);
+}
+
+//============================================================================//
+// QMaxHeap //
+//============================================================================//
+
+template <class T>
+class QMaxHeap
+{
+public:
+ QMaxHeap() : m_data(0) {}
+ inline int size() const {return m_data.size();}
+ inline bool empty() const {return m_data.isEmpty();}
+ inline bool isEmpty() const {return m_data.isEmpty();}
+ void push(const T &x);
+ T pop();
+ inline const T &top() const {return m_data.first();}
+private:
+ static inline int parent(int i) {return (i - 1) / 2;}
+ static inline int left(int i) {return 2 * i + 1;}
+ static inline int right(int i) {return 2 * i + 2;}
+
+ QDataBuffer<T> m_data;
+};
+
+template <class T>
+void QMaxHeap<T>::push(const T &x)
+{
+ int current = m_data.size();
+ int parent = QMaxHeap::parent(current);
+ m_data.add(x);
+ while (current != 0 && m_data.at(parent) < x) {
+ m_data.at(current) = m_data.at(parent);
+ current = parent;
+ parent = QMaxHeap::parent(current);
+ }
+ m_data.at(current) = x;
+}
+
+template <class T>
+T QMaxHeap<T>::pop()
+{
+ T result = m_data.first();
+ T back = m_data.last();
+ m_data.pop_back();
+ if (!m_data.isEmpty()) {
+ int current = 0;
+ for (;;) {
+ int left = QMaxHeap::left(current);
+ int right = QMaxHeap::right(current);
+ if (left >= m_data.size())
+ break;
+ int greater = left;
+ if (right < m_data.size() && m_data.at(left) < m_data.at(right))
+ greater = right;
+ if (m_data.at(greater) < back)
+ break;
+ m_data.at(current) = m_data.at(greater);
+ current = greater;
+ }
+ m_data.at(current) = back;
+ }
+ return result;
+}
+
+//============================================================================//
+// QRBTree //
+//============================================================================//
+
+template <class T>
+struct QRBTree
+{
+ struct Node
+ {
+ inline Node() : parent(0), left(0), right(0), red(true) { }
+ inline ~Node() {if (left) delete left; if (right) delete right;}
+ T data;
+ Node *parent;
+ Node *left;
+ Node *right;
+ bool red;
+ };
+
+ inline QRBTree() : root(0), freeList(0) { }
+ inline ~QRBTree();
+
+ inline void clear();
+
+ void attachBefore(Node *parent, Node *child);
+ void attachAfter(Node *parent, Node *child);
+
+ inline Node *front(Node *node) const;
+ inline Node *back(Node *node) const;
+ Node *next(Node *node) const;
+ Node *previous(Node *node) const;
+
+ inline void deleteNode(Node *&node);
+ inline Node *newNode();
+
+ // Return 1 if 'left' comes after 'right', 0 if equal, and -1 otherwise.
+ // 'left' and 'right' cannot be null.
+ int order(Node *left, Node *right);
+ inline bool verify() const;
+
+private:
+ void rotateLeft(Node *node);
+ void rotateRight(Node *node);
+ void update(Node *node);
+
+ inline void attachLeft(Node *parent, Node *child);
+ inline void attachRight(Node *parent, Node *child);
+
+ int blackDepth(Node *top) const;
+ bool checkRedBlackProperty(Node *top) const;
+
+ void swapNodes(Node *n1, Node *n2);
+ void detach(Node *node);
+
+ // 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree.
+ void rebalance(Node *node);
+
+public:
+ Node *root;
+private:
+ Node *freeList;
+};
+
+template <class T>
+inline QRBTree<T>::~QRBTree()
+{
+ clear();
+ while (freeList) {
+ // Avoid recursively calling the destructor, as this list may become large.
+ Node *next = freeList->right;
+ freeList->right = 0;
+ delete freeList;
+ freeList = next;
+ }
+}
+
+template <class T>
+inline void QRBTree<T>::clear()
+{
+ if (root)
+ delete root;
+ root = 0;
+}
+
+template <class T>
+void QRBTree<T>::rotateLeft(Node *node)
+{
+ // | | //
+ // N B //
+ // / \ / \ //
+ // A B ---> N D //
+ // / \ / \ //
+ // C D A C //
+
+ Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root);
+ ref = node->right;
+ node->right->parent = node->parent;
+
+ // : //
+ // N //
+ // / :| //
+ // A B //
+ // / \ //
+ // C D //
+
+ node->right = ref->left;
+ if (ref->left)
+ ref->left->parent = node;
+
+ // : | //
+ // N B //
+ // / \ : \ //
+ // A C D //
+
+ ref->left = node;
+ node->parent = ref;
+
+ // | //
+ // B //
+ // / \ //
+ // N D //
+ // / \ //
+ // A C //
+}
+
+template <class T>
+void QRBTree<T>::rotateRight(Node *node)
+{
+ // | | //
+ // N A //
+ // / \ / \ //
+ // A B ---> C N //
+ // / \ / \ //
+ // C D D B //
+
+ Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root);
+ ref = node->left;
+ node->left->parent = node->parent;
+
+ node->left = ref->right;
+ if (ref->right)
+ ref->right->parent = node;
+
+ ref->right = node;
+ node->parent = ref;
+}
+
+template <class T>
+void QRBTree<T>::update(Node *node) // call this after inserting a node
+{
+ for (;;) {
+ Node *parent = node->parent;
+
+ // if the node is the root, color it black
+ if (!parent) {
+ node->red = false;
+ return;
+ }
+
+ // if the parent is black, the node can be left red
+ if (!parent->red)
+ return;
+
+ // at this point, the parent is red and cannot be the root
+ Node *grandpa = parent->parent;
+ Q_ASSERT(grandpa);
+
+ Node *uncle = (parent == grandpa->left ? grandpa->right : grandpa->left);
+ if (uncle && uncle->red) {
+ // grandpa's black, parent and uncle are red.
+ // let parent and uncle be black, grandpa red and recursively update grandpa.
+ Q_ASSERT(!grandpa->red);
+ parent->red = false;
+ uncle->red = false;
+ grandpa->red = true;
+ node = grandpa;
+ continue;
+ }
+
+ // at this point, uncle is black
+ if (node == parent->right && parent == grandpa->left)
+ rotateLeft(node = parent);
+ else if (node == parent->left && parent == grandpa->right)
+ rotateRight(node = parent);
+ parent = node->parent;
+
+ if (parent == grandpa->left) {
+ rotateRight(grandpa);
+ parent->red = false;
+ grandpa->red = true;
+ } else {
+ rotateLeft(grandpa);
+ parent->red = false;
+ grandpa->red = true;
+ }
+ return;
+ }
+}
+
+template <class T>
+inline void QRBTree<T>::attachLeft(Node *parent, Node *child)
+{
+ Q_ASSERT(!parent->left);
+ parent->left = child;
+ child->parent = parent;
+ update(child);
+}
+
+template <class T>
+inline void QRBTree<T>::attachRight(Node *parent, Node *child)
+{
+ Q_ASSERT(!parent->right);
+ parent->right = child;
+ child->parent = parent;
+ update(child);
+}
+
+template <class T>
+void QRBTree<T>::attachBefore(Node *parent, Node *child)
+{
+ if (!root)
+ update(root = child);
+ else if (!parent)
+ attachRight(back(root), child);
+ else if (parent->left)
+ attachRight(back(parent->left), child);
+ else
+ attachLeft(parent, child);
+}
+
+template <class T>
+void QRBTree<T>::attachAfter(Node *parent, Node *child)
+{
+ if (!root)
+ update(root = child);
+ else if (!parent)
+ attachLeft(front(root), child);
+ else if (parent->right)
+ attachLeft(front(parent->right), child);
+ else
+ attachRight(parent, child);
+}
+
+template <class T>
+void QRBTree<T>::swapNodes(Node *n1, Node *n2)
+{
+ // Since iterators must not be invalidated, it is not sufficient to only swap the data.
+ if (n1->parent == n2) {
+ n1->parent = n2->parent;
+ n2->parent = n1;
+ } else if (n2->parent == n1) {
+ n2->parent = n1->parent;
+ n1->parent = n2;
+ } else {
+ qSwap(n1->parent, n2->parent);
+ }
+
+ qSwap(n1->left, n2->left);
+ qSwap(n1->right, n2->right);
+ qSwap(n1->red, n2->red);
+
+ if (n1->parent) {
+ if (n1->parent->left == n2)
+ n1->parent->left = n1;
+ else
+ n1->parent->right = n1;
+ } else {
+ root = n1;
+ }
+
+ if (n2->parent) {
+ if (n2->parent->left == n1)
+ n2->parent->left = n2;
+ else
+ n2->parent->right = n2;
+ } else {
+ root = n2;
+ }
+
+ if (n1->left)
+ n1->left->parent = n1;
+ if (n1->right)
+ n1->right->parent = n1;
+
+ if (n2->left)
+ n2->left->parent = n2;
+ if (n2->right)
+ n2->right->parent = n2;
+}
+
+template <class T>
+void QRBTree<T>::detach(Node *node) // call this before removing a node.
+{
+ if (node->right)
+ swapNodes(node, front(node->right));
+
+ Node *child = (node->left ? node->left : node->right);
+
+ if (!node->red) {
+ if (child && child->red)
+ child->red = false;
+ else
+ rebalance(node);
+ }
+
+ Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root);
+ ref = child;
+ if (child)
+ child->parent = node->parent;
+ node->left = node->right = node->parent = 0;
+}
+
+// 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree.
+template <class T>
+void QRBTree<T>::rebalance(Node *node)
+{
+ Q_ASSERT(!node->red);
+ for (;;) {
+ if (!node->parent)
+ return;
+
+ // at this point, node is not a parent, it is black, thus it must have a sibling.
+ Node *sibling = (node == node->parent->left ? node->parent->right : node->parent->left);
+ Q_ASSERT(sibling);
+
+ if (sibling->red) {
+ sibling->red = false;
+ node->parent->red = true;
+ if (node == node->parent->left)
+ rotateLeft(node->parent);
+ else
+ rotateRight(node->parent);
+ sibling = (node == node->parent->left ? node->parent->right : node->parent->left);
+ Q_ASSERT(sibling);
+ }
+
+ // at this point, the sibling is black.
+ Q_ASSERT(!sibling->red);
+
+ if ((!sibling->left || !sibling->left->red) && (!sibling->right || !sibling->right->red)) {
+ bool parentWasRed = node->parent->red;
+ sibling->red = true;
+ node->parent->red = false;
+ if (parentWasRed)
+ return;
+ node = node->parent;
+ continue;
+ }
+
+ // at this point, at least one of the sibling's children is red.
+
+ if (node == node->parent->left) {
+ if (!sibling->right || !sibling->right->red) {
+ Q_ASSERT(sibling->left);
+ sibling->red = true;
+ sibling->left->red = false;
+ rotateRight(sibling);
+
+ sibling = sibling->parent;
+ Q_ASSERT(sibling);
+ }
+ sibling->red = node->parent->red;
+ node->parent->red = false;
+
+ Q_ASSERT(sibling->right->red);
+ sibling->right->red = false;
+ rotateLeft(node->parent);
+ } else {
+ if (!sibling->left || !sibling->left->red) {
+ Q_ASSERT(sibling->right);
+ sibling->red = true;
+ sibling->right->red = false;
+ rotateLeft(sibling);
+
+ sibling = sibling->parent;
+ Q_ASSERT(sibling);
+ }
+ sibling->red = node->parent->red;
+ node->parent->red = false;
+
+ Q_ASSERT(sibling->left->red);
+ sibling->left->red = false;
+ rotateRight(node->parent);
+ }
+ return;
+ }
+}
+
+template <class T>
+inline typename QRBTree<T>::Node *QRBTree<T>::front(Node *node) const
+{
+ while (node->left)
+ node = node->left;
+ return node;
+}
+
+template <class T>
+inline typename QRBTree<T>::Node *QRBTree<T>::back(Node *node) const
+{
+ while (node->right)
+ node = node->right;
+ return node;
+}
+
+template <class T>
+typename QRBTree<T>::Node *QRBTree<T>::next(Node *node) const
+{
+ if (node->right)
+ return front(node->right);
+ while (node->parent && node == node->parent->right)
+ node = node->parent;
+ return node->parent;
+}
+
+template <class T>
+typename QRBTree<T>::Node *QRBTree<T>::previous(Node *node) const
+{
+ if (node->left)
+ return back(node->left);
+ while (node->parent && node == node->parent->left)
+ node = node->parent;
+ return node->parent;
+}
+
+template <class T>
+int QRBTree<T>::blackDepth(Node *top) const
+{
+ if (!top)
+ return 0;
+ int leftDepth = blackDepth(top->left);
+ int rightDepth = blackDepth(top->right);
+ if (leftDepth != rightDepth)
+ return -1;
+ if (!top->red)
+ ++leftDepth;
+ return leftDepth;
+}
+
+template <class T>
+bool QRBTree<T>::checkRedBlackProperty(Node *top) const
+{
+ if (!top)
+ return true;
+ if (top->left && !checkRedBlackProperty(top->left))
+ return false;
+ if (top->right && !checkRedBlackProperty(top->right))
+ return false;
+ return !(top->red && ((top->left && top->left->red) || (top->right && top->right->red)));
+}
+
+template <class T>
+inline bool QRBTree<T>::verify() const
+{
+ return checkRedBlackProperty(root) && blackDepth(root) != -1;
+}
+
+template <class T>
+inline void QRBTree<T>::deleteNode(Node *&node)
+{
+ Q_ASSERT(node);
+ detach(node);
+ node->right = freeList;
+ freeList = node;
+ node = 0;
+}
+
+template <class T>
+inline typename QRBTree<T>::Node *QRBTree<T>::newNode()
+{
+ if (freeList) {
+ Node *node = freeList;
+ freeList = freeList->right;
+ node->parent = node->left = node->right = 0;
+ node->red = true;
+ return node;
+ }
+ return new Node;
+}
+
+// Return 1 if 'left' comes after 'right', 0 if equal, and -1 otherwise.
+// 'left' and 'right' cannot be null.
+template <class T>
+int QRBTree<T>::order(Node *left, Node *right)
+{
+ Q_ASSERT(left && right);
+ if (left == right)
+ return 0;
+
+ QVector<Node *> leftAncestors;
+ QVector<Node *> rightAncestors;
+ while (left) {
+ leftAncestors.push_back(left);
+ left = left->parent;
+ }
+ while (right) {
+ rightAncestors.push_back(right);
+ right = right->parent;
+ }
+ Q_ASSERT(leftAncestors.back() == root && rightAncestors.back() == root);
+
+ while (!leftAncestors.empty() && !rightAncestors.empty() && leftAncestors.back() == rightAncestors.back()) {
+ leftAncestors.pop_back();
+ rightAncestors.pop_back();
+ }
+
+ if (!leftAncestors.empty())
+ return (leftAncestors.back() == leftAncestors.back()->parent->left ? -1 : 1);
+
+ if (!rightAncestors.empty())
+ return (rightAncestors.back() == rightAncestors.back()->parent->right ? -1 : 1);
+
+ // The code should never reach this point.
+ Q_ASSERT(!leftAncestors.empty() || !rightAncestors.empty());
+ return 0;
+}
+
+//============================================================================//
+// QInt64Hash //
+//============================================================================//
+
+// Copied from qhash.cpp
+static const uchar prime_deltas[] = {
+ 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
+ 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0
+};
+
+// Copied from qhash.cpp
+static inline int primeForNumBits(int numBits)
+{
+ return (1 << numBits) + prime_deltas[numBits];
+}
+
+static inline int primeForCount(int count)
+{
+ int low = 0;
+ int high = 32;
+ for (int i = 0; i < 5; ++i) {
+ int mid = (high + low) / 2;
+ if (count >= 1 << mid)
+ low = mid;
+ else
+ high = mid;
+ }
+ return primeForNumBits(high);
+}
+
+// Hash set of quint64s. Elements cannot be removed without clearing the
+// entire set. A value of -1 is used to mark unused entries.
+class QInt64Set
+{
+public:
+ inline QInt64Set(int capacity = 64);
+ inline ~QInt64Set() {if (m_array) delete[] m_array;}
+ inline bool isValid() const {return m_array;}
+ void insert(quint64 key);
+ bool contains(quint64 key) const;
+ inline void clear();
+private:
+ bool rehash(int capacity);
+
+ static const quint64 UNUSED;
+
+ quint64 *m_array;
+ int m_capacity;
+ int m_count;
+};
+
+const quint64 QInt64Set::UNUSED = quint64(-1);
+
+inline QInt64Set::QInt64Set(int capacity)
+{
+ m_capacity = primeForCount(capacity);
+ m_array = new quint64[m_capacity];
+ if (m_array)
+ clear();
+ else
+ m_capacity = 0;
+}
+
+bool QInt64Set::rehash(int capacity)
+{
+ quint64 *oldArray = m_array;
+ int oldCapacity = m_capacity;
+
+ m_capacity = capacity;
+ m_array = new quint64[m_capacity];
+ if (m_array) {
+ clear();
+ if (oldArray) {
+ for (int i = 0; i < oldCapacity; ++i) {
+ if (oldArray[i] != UNUSED)
+ insert(oldArray[i]);
+ }
+ delete[] oldArray;
+ }
+ return true;
+ } else {
+ m_capacity = oldCapacity;
+ m_array = oldArray;
+ return false;
+ }
+}
+
+void QInt64Set::insert(quint64 key)
+{
+ if (m_count > 3 * m_capacity / 4)
+ rehash(primeForCount(2 * m_capacity));
+ Q_ASSERT_X(m_array, "QInt64Hash<T>::insert", "Hash set not allocated.");
+ int index = int(key % m_capacity);
+ for (int i = 0; i < m_capacity; ++i) {
+ index += i;
+ if (index >= m_capacity)
+ index -= m_capacity;
+ if (m_array[index] == key)
+ return;
+ if (m_array[index] == UNUSED) {
+ ++m_count;
+ m_array[index] = key;
+ return;
+ }
+ }
+ Q_ASSERT_X(0, "QInt64Hash<T>::insert", "Hash set full.");
+}
+
+bool QInt64Set::contains(quint64 key) const
+{
+ Q_ASSERT_X(m_array, "QInt64Hash<T>::contains", "Hash set not allocated.");
+ int index = int(key % m_capacity);
+ for (int i = 0; i < m_capacity; ++i) {
+ index += i;
+ if (index >= m_capacity)
+ index -= m_capacity;
+ if (m_array[index] == key)
+ return true;
+ if (m_array[index] == UNUSED)
+ return false;
+ }
+ return false;
+}
+
+inline void QInt64Set::clear()
+{
+ Q_ASSERT_X(m_array, "QInt64Hash<T>::clear", "Hash set not allocated.");
+ for (int i = 0; i < m_capacity; ++i)
+ m_array[i] = UNUSED;
+ m_count = 0;
+}
+
+//============================================================================//
+// QRingBuffer //
+//============================================================================//
+
+// T must be POD.
+template <class T>
+class QRingBuffer
+{
+public:
+ inline QRingBuffer() : m_array(0), m_head(0), m_size(0), m_capacity(0) { }
+ inline ~QRingBuffer() {if (m_array) delete[] m_array;}
+ bool reallocate(int capacity);
+ inline const T &head() const {Q_ASSERT(m_size > 0); return m_array[m_head];}
+ inline const T &dequeue();
+ inline void enqueue(const T &x);
+ inline bool isEmpty() const {return m_size == 0;}
+private:
+ T *m_array;
+ int m_head;
+ int m_size;
+ int m_capacity;
+};
+
+template <class T>
+bool QRingBuffer<T>::reallocate(int capacity)
+{
+ T *oldArray = m_array;
+ m_array = new T[capacity];
+ if (m_array) {
+ if (oldArray) {
+ if (m_head + m_size > m_capacity) {
+ memcpy(m_array, oldArray + m_head, (m_capacity - m_head) * sizeof(T));
+ memcpy(m_array + (m_capacity - m_head), oldArray, (m_head + m_size - m_capacity) * sizeof(T));
+ } else {
+ memcpy(m_array, oldArray + m_head, m_size * sizeof(T));
+ }
+ delete[] oldArray;
+ }
+ m_capacity = capacity;
+ m_head = 0;
+ return true;
+ } else {
+ m_array = oldArray;
+ return false;
+ }
+}
+
+template <class T>
+inline const T &QRingBuffer<T>::dequeue()
+{
+ Q_ASSERT(m_size > 0);
+ Q_ASSERT(m_array);
+ Q_ASSERT(m_capacity >= m_size);
+ int index = m_head;
+ if (++m_head >= m_capacity)
+ m_head -= m_capacity;
+ --m_size;
+ return m_array[index];
+}
+
+template <class T>
+inline void QRingBuffer<T>::enqueue(const T &x)
+{
+ if (m_size == m_capacity)
+ reallocate(qMax(2 * m_capacity, 64));
+ int index = m_head + m_size;
+ if (index >= m_capacity)
+ index -= m_capacity;
+ m_array[index] = x;
+ ++m_size;
+}
+
+//============================================================================//
+// QTriangulator //
+//============================================================================//
+
+class QTriangulator
+{
+public:
+ typedef QVarLengthArray<int, 6> ShortArray;
+
+ //================================//
+ // QTriangulator::ComplexToSimple //
+ //================================//
+ friend class ComplexToSimple;
+ class ComplexToSimple
+ {
+ public:
+ inline ComplexToSimple(QTriangulator *parent) : m_parent(parent),
+ m_edges(0), m_events(0), m_splits(0) { }
+ void decompose();
+ private:
+ struct Edge
+ {
+ inline int &upper() {return pointingUp ? to : from;}
+ inline int &lower() {return pointingUp ? from : to;}
+ inline int upper() const {return pointingUp ? to : from;}
+ inline int lower() const {return pointingUp ? from : to;}
+
+ QRBTree<int>::Node *node;
+ int from, to; // vertex
+ int next, previous; // edge
+ int winding;
+ bool mayIntersect;
+ bool pointingUp, originallyPointingUp;
+ };
+
+ friend class CompareEdges;
+ class CompareEdges
+ {
+ public:
+ inline CompareEdges(ComplexToSimple *parent) : m_parent(parent) { }
+ bool operator () (int i, int j) const;
+ private:
+ ComplexToSimple *m_parent;
+ };
+
+ struct Intersection
+ {
+ bool operator < (const Intersection &other) const {return other.intersectionPoint < intersectionPoint;}
+
+ QIntersectionPoint intersectionPoint;
+ int vertex;
+ int leftEdge;
+ int rightEdge;
+ };
+
+ struct Split
+ {
+ int vertex;
+ int edge;
+ bool accurate;
+ };
+
+ struct Event
+ {
+ enum Type {Upper, Lower};
+ inline bool operator < (const Event &other) const;
+
+ QPodPoint point;
+ Type type;
+ int edge;
+ };
+
+#ifdef Q_TRIANGULATOR_DEBUG
+ friend class DebugDialog;
+ friend class QTriangulator;
+ class DebugDialog : public QDialog
+ {
+ public:
+ DebugDialog(ComplexToSimple *parent, int currentVertex);
+ protected:
+ void paintEvent(QPaintEvent *);
+ void wheelEvent(QWheelEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void mousePressEvent(QMouseEvent *);
+ private:
+ ComplexToSimple *m_parent;
+ QRectF m_window;
+ QPoint m_lastMousePos;
+ int m_vertex;
+ };
+#endif
+
+ void initEdges();
+ bool calculateIntersection(int left, int right);
+ bool edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const;
+ QRBTree<int>::Node *searchEdgeLeftOf(int edgeIndex) const;
+ QRBTree<int>::Node *searchEdgeLeftOf(int edgeIndex, QRBTree<int>::Node *after) const;
+ QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> bounds(const QPodPoint &point) const;
+ QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> outerBounds(const QPodPoint &point) const;
+ void splitEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost, int vertex, const QIntersectionPoint &intersectionPoint);
+ void reorderEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost);
+ void sortEdgeList(const QPodPoint eventPoint);
+ void fillPriorityQueue();
+ void calculateIntersections();
+ int splitEdge(int splitIndex);
+ bool splitEdgesAtIntersections();
+ void insertEdgeIntoVectorIfWanted(ShortArray &orderedEdges, int i);
+ void removeUnwantedEdgesAndConnect();
+ void removeUnusedPoints();
+
+ QTriangulator *m_parent;
+ QDataBuffer<Edge> m_edges;
+ QRBTree<int> m_edgeList;
+ QDataBuffer<Event> m_events;
+ QDataBuffer<Split> m_splits;
+ QMaxHeap<Intersection> m_topIntersection;
+ QInt64Set m_processedEdgePairs;
+ int m_initialPointCount;
+ };
+#ifdef Q_TRIANGULATOR_DEBUG
+ friend class ComplexToSimple::DebugDialog;
+#endif
+
+ //=================================//
+ // QTriangulator::SimpleToMonotone //
+ //=================================//
+ friend class SimpleToMonotone;
+ class SimpleToMonotone
+ {
+ public:
+ inline SimpleToMonotone(QTriangulator *parent) : m_parent(parent), m_edges(0), m_upperVertex(0) { }
+ void decompose();
+ private:
+ enum VertexType {MergeVertex, EndVertex, RegularVertex, StartVertex, SplitVertex};
+
+ struct Edge
+ {
+ QRBTree<int>::Node *node;
+ int helper, twin, next, previous;
+ quint32 from, to;
+ VertexType type;
+ bool pointingUp;
+ int upper() const {return (pointingUp ? to : from);}
+ int lower() const {return (pointingUp ? from : to);}
+ };
+
+ friend class CompareVertices;
+ class CompareVertices
+ {
+ public:
+ CompareVertices(SimpleToMonotone *parent) : m_parent(parent) { }
+ bool operator () (int i, int j) const;
+ private:
+ SimpleToMonotone *m_parent;
+ };
+
+ void setupDataStructures();
+ void removeZeroLengthEdges();
+ void fillPriorityQueue();
+ bool edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const;
+ // Returns the rightmost edge not to the right of the given edge.
+ QRBTree<int>::Node *searchEdgeLeftOfEdge(int edgeIndex) const;
+ // Returns the rightmost edge left of the given point.
+ QRBTree<int>::Node *searchEdgeLeftOfPoint(int pointIndex) const;
+ void classifyVertex(int i);
+ void classifyVertices();
+ bool pointIsInSector(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2, const QPodPoint &v3);
+ bool pointIsInSector(int vertex, int sector);
+ int findSector(int edge, int vertex);
+ void createDiagonal(int lower, int upper);
+ void monotoneDecomposition();
+
+ QTriangulator *m_parent;
+ QRBTree<int> m_edgeList;
+ QDataBuffer<Edge> m_edges;
+ QDataBuffer<int> m_upperVertex;
+ bool m_clockwiseOrder;
+ };
+
+ //====================================//
+ // QTriangulator::MonotoneToTriangles //
+ //====================================//
+ friend class MonotoneToTriangles;
+ class MonotoneToTriangles
+ {
+ public:
+ inline MonotoneToTriangles(QTriangulator *parent) : m_parent(parent) { }
+ void decompose();
+ private:
+ inline quint32 indices(int index) const {return m_parent->m_indices.at(index + m_first);}
+ inline int next(int index) const {return (index + 1) % m_length;}
+ inline int previous(int index) const {return (index + m_length - 1) % m_length;}
+ inline bool less(int i, int j) const {return m_parent->m_vertices.at(indices(i)) < m_parent->m_vertices.at(indices(j));}
+ inline bool leftOfEdge(int i, int j, int k) const
+ {
+ return qPointIsLeftOfLine(m_parent->m_vertices.at(indices(i)),
+ m_parent->m_vertices.at(indices(j)), m_parent->m_vertices.at(indices(k)));
+ }
+
+ QTriangulator *m_parent;
+ int m_first;
+ int m_length;
+ };
+
+ inline QTriangulator() : m_vertices(0) { }
+
+ // Call this only once.
+ void initialize(const qreal *polygon, int count, uint hint, const QTransform &matrix);
+ // Call this only once.
+ void initialize(const QVectorPath &path, const QTransform &matrix, qreal lod);
+ // Call this only once.
+ void initialize(const QPainterPath &path, const QTransform &matrix, qreal lod);
+ // Call either triangulate() or polyline() only once.
+ QTriangleSet triangulate();
+ QPolylineSet polyline();
+private:
+ QDataBuffer<QPodPoint> m_vertices;
+ QVector<quint32> m_indices;
+ uint m_hint;
+};
+
+//============================================================================//
+// QTriangulator //
+//============================================================================//
+
+QTriangleSet QTriangulator::triangulate()
+{
+ for (int i = 0; i < m_vertices.size(); ++i) {
+ Q_ASSERT(qAbs(m_vertices.at(i).x) < (1 << 21));
+ Q_ASSERT(qAbs(m_vertices.at(i).y) < (1 << 21));
+ }
+
+ if (!(m_hint & (QVectorPath::OddEvenFill | QVectorPath::WindingFill)))
+ m_hint |= QVectorPath::OddEvenFill;
+
+ if (m_hint & QVectorPath::NonConvexShapeMask) {
+ ComplexToSimple c2s(this);
+ c2s.decompose();
+ SimpleToMonotone s2m(this);
+ s2m.decompose();
+ }
+ MonotoneToTriangles m2t(this);
+ m2t.decompose();
+
+ QTriangleSet result;
+ result.indices = m_indices;
+ result.vertices.resize(2 * m_vertices.size());
+ for (int i = 0; i < m_vertices.size(); ++i) {
+ result.vertices[2 * i + 0] = qreal(m_vertices.at(i).x) / Q_FIXED_POINT_SCALE;
+ result.vertices[2 * i + 1] = qreal(m_vertices.at(i).y) / Q_FIXED_POINT_SCALE;
+ }
+ return result;
+}
+
+QPolylineSet QTriangulator::polyline()
+{
+ QPolylineSet result;
+ result.indices = m_indices;
+ result.vertices.resize(2 * m_vertices.size());
+ for (int i = 0; i < m_vertices.size(); ++i) {
+ result.vertices[2 * i + 0] = qreal(m_vertices.at(i).x) / Q_FIXED_POINT_SCALE;
+ result.vertices[2 * i + 1] = qreal(m_vertices.at(i).y) / Q_FIXED_POINT_SCALE;
+ }
+ return result;
+}
+
+void QTriangulator::initialize(const qreal *polygon, int count, uint hint, const QTransform &matrix)
+{
+ m_hint = hint;
+ m_vertices.resize(count);
+ m_indices.resize(count + 1);
+ for (int i = 0; i < count; ++i) {
+ qreal x, y;
+ matrix.map(polygon[2 * i + 0], polygon[2 * i + 1], &x, &y);
+ m_vertices.at(i).x = qRound(x * Q_FIXED_POINT_SCALE);
+ m_vertices.at(i).y = qRound(y * Q_FIXED_POINT_SCALE);
+ m_indices[i] = i;
+ }
+ m_indices[count] = Q_TRIANGULATE_END_OF_POLYGON;
+}
+
+void QTriangulator::initialize(const QVectorPath &path, const QTransform &matrix, qreal lod)
+{
+ m_hint = path.hints();
+ // Curved paths will be converted to complex polygons.
+ m_hint &= ~QVectorPath::CurvedShapeMask;
+
+ const qreal *p = path.points();
+ const QPainterPath::ElementType *e = path.elements();
+ if (e) {
+ for (int i = 0; i < path.elementCount(); ++i, ++e, p += 2) {
+ switch (*e) {
+ case QPainterPath::MoveToElement:
+ if (!m_indices.isEmpty())
+ m_indices.push_back(Q_TRIANGULATE_END_OF_POLYGON);
+ // Fall through.
+ case QPainterPath::LineToElement:
+ m_indices.push_back(quint32(m_vertices.size()));
+ m_vertices.resize(m_vertices.size() + 1);
+ qreal x, y;
+ matrix.map(p[0], p[1], &x, &y);
+ m_vertices.last().x = qRound(x * Q_FIXED_POINT_SCALE);
+ m_vertices.last().y = qRound(y * Q_FIXED_POINT_SCALE);
+ break;
+ case QPainterPath::CurveToElement:
+ {
+ qreal pts[8];
+ for (int i = 0; i < 4; ++i)
+ matrix.map(p[2 * i - 2], p[2 * i - 1], &pts[2 * i + 0], &pts[2 * i + 1]);
+ for (int i = 0; i < 8; ++i)
+ pts[i] *= lod;
+ QBezier bezier = QBezier::fromPoints(QPointF(pts[0], pts[1]), QPointF(pts[2], pts[3]), QPointF(pts[4], pts[5]), QPointF(pts[6], pts[7]));
+ QPolygonF poly = bezier.toPolygon();
+ // Skip first point, it already exists in 'm_vertices'.
+ for (int j = 1; j < poly.size(); ++j) {
+ m_indices.push_back(quint32(m_vertices.size()));
+ m_vertices.resize(m_vertices.size() + 1);
+ m_vertices.last().x = qRound(poly.at(j).x() * Q_FIXED_POINT_SCALE / lod);
+ m_vertices.last().y = qRound(poly.at(j).y() * Q_FIXED_POINT_SCALE / lod);
+ }
+ }
+ i += 2;
+ e += 2;
+ p += 4;
+ break;
+ default:
+ Q_ASSERT_X(0, "QTriangulator::triangulate", "Unexpected element type.");
+ break;
+ }
+ }
+ } else {
+ for (int i = 0; i < path.elementCount(); ++i, p += 2) {
+ m_indices.push_back(quint32(m_vertices.size()));
+ m_vertices.resize(m_vertices.size() + 1);
+ qreal x, y;
+ matrix.map(p[0], p[1], &x, &y);
+ m_vertices.last().x = qRound(x * Q_FIXED_POINT_SCALE);
+ m_vertices.last().y = qRound(y * Q_FIXED_POINT_SCALE);
+ }
+ }
+ m_indices.push_back(Q_TRIANGULATE_END_OF_POLYGON);
+}
+
+void QTriangulator::initialize(const QPainterPath &path, const QTransform &matrix, qreal lod)
+{
+ initialize(qtVectorPathForPath(path), matrix, lod);
+}
+
+//============================================================================//
+// QTriangulator::ComplexToSimple //
+//============================================================================//
+
+void QTriangulator::ComplexToSimple::decompose()
+{
+ m_initialPointCount = m_parent->m_vertices.size();
+ initEdges();
+ do {
+ calculateIntersections();
+ } while (splitEdgesAtIntersections());
+
+ removeUnwantedEdgesAndConnect();
+ removeUnusedPoints();
+
+ m_parent->m_indices.clear();
+ QBitArray processed(m_edges.size(), false);
+ for (int first = 0; first < m_edges.size(); ++first) {
+ // If already processed, or if unused path, skip.
+ if (processed.at(first) || m_edges.at(first).next == -1)
+ continue;
+
+ int i = first;
+ do {
+ Q_ASSERT(!processed.at(i));
+ Q_ASSERT(m_edges.at(m_edges.at(i).next).previous == i);
+ m_parent->m_indices.push_back(m_edges.at(i).from);
+ processed.setBit(i);
+ i = m_edges.at(i).next; // CCW order
+ } while (i != first);
+ m_parent->m_indices.push_back(Q_TRIANGULATE_END_OF_POLYGON);
+ }
+}
+
+void QTriangulator::ComplexToSimple::initEdges()
+{
+ // Initialize edge structure.
+ // 'next' and 'previous' are not being initialized at this point.
+ int first = 0;
+ for (int i = 0; i < m_parent->m_indices.size(); ++i) {
+ if (m_parent->m_indices.at(i) == Q_TRIANGULATE_END_OF_POLYGON) {
+ if (m_edges.size() != first)
+ m_edges.last().to = m_edges.at(first).from;
+ first = m_edges.size();
+ } else {
+ Q_ASSERT(i + 1 < m_parent->m_indices.size());
+ // {node, from, to, next, previous, winding, mayIntersect, pointingUp, originallyPointingUp}
+ Edge edge = {0, m_parent->m_indices.at(i), m_parent->m_indices.at(i + 1), -1, -1, 0, true, false, false};
+ m_edges.add(edge);
+ }
+ }
+ if (first != m_edges.size())
+ m_edges.last().to = m_edges.at(first).from;
+ for (int i = 0; i < m_edges.size(); ++i) {
+ m_edges.at(i).originallyPointingUp = m_edges.at(i).pointingUp =
+ m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from);
+ }
+}
+
+// Return true if new intersection was found
+bool QTriangulator::ComplexToSimple::calculateIntersection(int left, int right)
+{
+ const Edge &e1 = m_edges.at(left);
+ const Edge &e2 = m_edges.at(right);
+
+ const QPodPoint &u1 = m_parent->m_vertices.at(e1.from);
+ const QPodPoint &u2 = m_parent->m_vertices.at(e1.to);
+ const QPodPoint &v1 = m_parent->m_vertices.at(e2.from);
+ const QPodPoint &v2 = m_parent->m_vertices.at(e2.to);
+ if (qMax(u1.x, u2.x) <= qMin(v1.x, v2.x))
+ return false;
+
+ quint64 key = (left > right ? (quint64(right) << 32) | quint64(left) : (quint64(left) << 32) | quint64(right));
+ if (m_processedEdgePairs.contains(key))
+ return false;
+ m_processedEdgePairs.insert(key);
+
+ Intersection intersection;
+ intersection.leftEdge = left;
+ intersection.rightEdge = right;
+ intersection.intersectionPoint = qIntersectionPoint(u1, u2, v1, v2);
+
+ if (!intersection.intersectionPoint.isValid())
+ return false;
+
+ Q_ASSERT(intersection.intersectionPoint.isOnLine(u1, u2));
+ Q_ASSERT(intersection.intersectionPoint.isOnLine(v1, v2));
+
+ intersection.vertex = m_parent->m_vertices.size();
+ m_topIntersection.push(intersection);
+ m_parent->m_vertices.add(intersection.intersectionPoint.round());
+ return true;
+}
+
+bool QTriangulator::ComplexToSimple::edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const
+{
+ const Edge &leftEdge = m_edges.at(leftEdgeIndex);
+ const Edge &rightEdge = m_edges.at(rightEdgeIndex);
+ const QPodPoint &u = m_parent->m_vertices.at(rightEdge.upper());
+ const QPodPoint &l = m_parent->m_vertices.at(rightEdge.lower());
+ const QPodPoint &upper = m_parent->m_vertices.at(leftEdge.upper());
+ if (upper.x < qMin(l.x, u.x))
+ return true;
+ if (upper.x > qMax(l.x, u.x))
+ return false;
+ qint64 d = qPointDistanceFromLine(upper, l, u);
+ // d < 0: left, d > 0: right, d == 0: on top
+ if (d == 0)
+ d = qPointDistanceFromLine(m_parent->m_vertices.at(leftEdge.lower()), l, u);
+ return d < 0;
+}
+
+QRBTree<int>::Node *QTriangulator::ComplexToSimple::searchEdgeLeftOf(int edgeIndex) const
+{
+ QRBTree<int>::Node *current = m_edgeList.root;
+ QRBTree<int>::Node *result = 0;
+ while (current) {
+ if (edgeIsLeftOfEdge(edgeIndex, current->data)) {
+ current = current->left;
+ } else {
+ result = current;
+ current = current->right;
+ }
+ }
+ return result;
+}
+
+QRBTree<int>::Node *QTriangulator::ComplexToSimple::searchEdgeLeftOf(int edgeIndex, QRBTree<int>::Node *after) const
+{
+ if (!m_edgeList.root)
+ return after;
+ QRBTree<int>::Node *result = after;
+ QRBTree<int>::Node *current = (after ? m_edgeList.next(after) : m_edgeList.front(m_edgeList.root));
+ while (current) {
+ if (edgeIsLeftOfEdge(edgeIndex, current->data))
+ return result;
+ result = current;
+ current = m_edgeList.next(current);
+ }
+ return result;
+}
+
+QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> QTriangulator::ComplexToSimple::bounds(const QPodPoint &point) const
+{
+ QRBTree<int>::Node *current = m_edgeList.root;
+ QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> result(0, 0);
+ while (current) {
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = qPointDistanceFromLine(point, v1, v2);
+ if (d == 0) {
+ result.first = result.second = current;
+ break;
+ }
+ current = (d < 0 ? current->left : current->right);
+ }
+ if (current == 0)
+ return result;
+
+ current = result.first->left;
+ while (current) {
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = qPointDistanceFromLine(point, v1, v2);
+ Q_ASSERT(d >= 0);
+ if (d == 0) {
+ result.first = current;
+ current = current->left;
+ } else {
+ current = current->right;
+ }
+ }
+
+ current = result.second->right;
+ while (current) {
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = qPointDistanceFromLine(point, v1, v2);
+ Q_ASSERT(d <= 0);
+ if (d == 0) {
+ result.second = current;
+ current = current->right;
+ } else {
+ current = current->left;
+ }
+ }
+
+ return result;
+}
+
+QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> QTriangulator::ComplexToSimple::outerBounds(const QPodPoint &point) const
+{
+ QRBTree<int>::Node *current = m_edgeList.root;
+ QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> result(0, 0);
+
+ while (current) {
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = qPointDistanceFromLine(point, v1, v2);
+ if (d == 0)
+ break;
+ if (d < 0) {
+ result.second = current;
+ current = current->left;
+ } else {
+ result.first = current;
+ current = current->right;
+ }
+ }
+
+ if (!current)
+ return result;
+
+ QRBTree<int>::Node *mid = current;
+
+ current = mid->left;
+ while (current) {
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = qPointDistanceFromLine(point, v1, v2);
+ Q_ASSERT(d >= 0);
+ if (d == 0) {
+ current = current->left;
+ } else {
+ result.first = current;
+ current = current->right;
+ }
+ }
+
+ current = mid->right;
+ while (current) {
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = qPointDistanceFromLine(point, v1, v2);
+ Q_ASSERT(d <= 0);
+ if (d == 0) {
+ current = current->right;
+ } else {
+ result.second = current;
+ current = current->left;
+ }
+ }
+
+ return result;
+}
+
+void QTriangulator::ComplexToSimple::splitEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost, int vertex, const QIntersectionPoint &intersectionPoint)
+{
+ Q_ASSERT(leftmost && rightmost);
+
+ // Split.
+ for (;;) {
+ const QPodPoint &u = m_parent->m_vertices.at(m_edges.at(leftmost->data).from);
+ const QPodPoint &v = m_parent->m_vertices.at(m_edges.at(leftmost->data).to);
+ Q_ASSERT(intersectionPoint.isOnLine(u, v));
+ const Split split = {vertex, leftmost->data, intersectionPoint.isAccurate()};
+ if (intersectionPoint.xOffset.numerator != 0 || intersectionPoint.yOffset.numerator != 0 || (intersectionPoint.upperLeft != u && intersectionPoint.upperLeft != v))
+ m_splits.add(split);
+ if (leftmost == rightmost)
+ break;
+ leftmost = m_edgeList.next(leftmost);
+ }
+}
+
+
+void QTriangulator::ComplexToSimple::reorderEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost)
+{
+ Q_ASSERT(leftmost && rightmost);
+
+ QRBTree<int>::Node *storeLeftmost = leftmost;
+ QRBTree<int>::Node *storeRightmost = rightmost;
+
+ // Reorder.
+ while (leftmost != rightmost) {
+ Edge &left = m_edges.at(leftmost->data);
+ Edge &right = m_edges.at(rightmost->data);
+ qSwap(left.node, right.node);
+ qSwap(leftmost->data, rightmost->data);
+ leftmost = m_edgeList.next(leftmost);
+ if (leftmost == rightmost)
+ break;
+ rightmost = m_edgeList.previous(rightmost);
+ }
+
+ rightmost = m_edgeList.next(storeRightmost);
+ leftmost = m_edgeList.previous(storeLeftmost);
+ if (leftmost)
+ calculateIntersection(leftmost->data, storeLeftmost->data);
+ if (rightmost)
+ calculateIntersection(storeRightmost->data, rightmost->data);
+}
+
+void QTriangulator::ComplexToSimple::sortEdgeList(const QPodPoint eventPoint)
+{
+ QIntersectionPoint eventPoint2 = qIntersectionPoint(eventPoint);
+ while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint < eventPoint2) {
+ Intersection intersection = m_topIntersection.pop();
+
+ QIntersectionPoint currentIntersectionPoint = intersection.intersectionPoint;
+ int currentVertex = intersection.vertex;
+
+ QRBTree<int>::Node *leftmost = m_edges.at(intersection.leftEdge).node;
+ QRBTree<int>::Node *rightmost = m_edges.at(intersection.rightEdge).node;
+
+ for (;;) {
+ QRBTree<int>::Node *previous = m_edgeList.previous(leftmost);
+ if (!previous)
+ break;
+ const Edge &edge = m_edges.at(previous->data);
+ const QPodPoint &u = m_parent->m_vertices.at(edge.from);
+ const QPodPoint &v = m_parent->m_vertices.at(edge.to);
+ if (!currentIntersectionPoint.isOnLine(u, v)) {
+ Q_ASSERT(!currentIntersectionPoint.isAccurate() || qCross(currentIntersectionPoint.upperLeft - u, v - u) != 0);
+ break;
+ }
+ leftmost = previous;
+ }
+
+ for (;;) {
+ QRBTree<int>::Node *next = m_edgeList.next(rightmost);
+ if (!next)
+ break;
+ const Edge &edge = m_edges.at(next->data);
+ const QPodPoint &u = m_parent->m_vertices.at(edge.from);
+ const QPodPoint &v = m_parent->m_vertices.at(edge.to);
+ if (!currentIntersectionPoint.isOnLine(u, v)) {
+ Q_ASSERT(!currentIntersectionPoint.isAccurate() || qCross(currentIntersectionPoint.upperLeft - u, v - u) != 0);
+ break;
+ }
+ rightmost = next;
+ }
+
+ Q_ASSERT(leftmost && rightmost);
+ splitEdgeListRange(leftmost, rightmost, currentVertex, currentIntersectionPoint);
+ reorderEdgeListRange(leftmost, rightmost);
+
+ while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint <= currentIntersectionPoint)
+ m_topIntersection.pop();
+
+#ifdef Q_TRIANGULATOR_DEBUG
+ DebugDialog dialog(this, intersection.vertex);
+ dialog.exec();
+#endif
+
+ }
+}
+
+void QTriangulator::ComplexToSimple::fillPriorityQueue()
+{
+ m_events.reset();
+ m_events.reserve(m_edges.size() * 2);
+ for (int i = 0; i < m_edges.size(); ++i) {
+ Q_ASSERT(m_edges.at(i).previous == -1 && m_edges.at(i).next == -1);
+ Q_ASSERT(m_edges.at(i).node == 0);
+ Q_ASSERT(m_edges.at(i).pointingUp == m_edges.at(i).originallyPointingUp);
+ Q_ASSERT(m_edges.at(i).pointingUp == (m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from)));
+ // Ignore zero-length edges.
+ if (m_parent->m_vertices.at(m_edges.at(i).to) != m_parent->m_vertices.at(m_edges.at(i).from)) {
+ QPodPoint upper = m_parent->m_vertices.at(m_edges.at(i).upper());
+ QPodPoint lower = m_parent->m_vertices.at(m_edges.at(i).lower());
+ Event upperEvent = {{upper.x, upper.y}, Event::Upper, i};
+ Event lowerEvent = {{lower.x, lower.y}, Event::Lower, i};
+ m_events.add(upperEvent);
+ m_events.add(lowerEvent);
+ }
+ }
+ //qSort(m_events.data(), m_events.data() + m_events.size());
+ sort(m_events.data(), m_events.size());
+}
+
+void QTriangulator::ComplexToSimple::calculateIntersections()
+{
+ fillPriorityQueue();
+
+ Q_ASSERT(m_topIntersection.empty());
+ Q_ASSERT(m_edgeList.root == 0);
+
+ // Find all intersection points.
+ while (!m_events.isEmpty()) {
+ Event event = m_events.last();
+ sortEdgeList(event.point);
+
+ // Find all edges in the edge list that contain the current vertex and mark them to be split later.
+ QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> range = bounds(event.point);
+ QRBTree<int>::Node *leftNode = range.first ? m_edgeList.previous(range.first) : 0;
+ int vertex = (event.type == Event::Upper ? m_edges.at(event.edge).upper() : m_edges.at(event.edge).lower());
+ QIntersectionPoint eventPoint = qIntersectionPoint(event.point);
+
+ if (range.first != 0) {
+ splitEdgeListRange(range.first, range.second, vertex, eventPoint);
+ reorderEdgeListRange(range.first, range.second);
+ }
+
+ // Handle the edges with start or end point in the current vertex.
+ while (!m_events.isEmpty() && m_events.last().point == event.point) {
+ event = m_events.last();
+ m_events.pop_back();
+ int i = event.edge;
+
+ if (m_edges.at(i).node) {
+ // Remove edge from edge list.
+ Q_ASSERT(event.type == Event::Lower);
+ QRBTree<int>::Node *left = m_edgeList.previous(m_edges.at(i).node);
+ QRBTree<int>::Node *right = m_edgeList.next(m_edges.at(i).node);
+ m_edgeList.deleteNode(m_edges.at(i).node);
+ if (!left || !right)
+ continue;
+ calculateIntersection(left->data, right->data);
+ } else {
+ // Insert edge into edge list.
+ Q_ASSERT(event.type == Event::Upper);
+ QRBTree<int>::Node *left = searchEdgeLeftOf(i, leftNode);
+ m_edgeList.attachAfter(left, m_edges.at(i).node = m_edgeList.newNode());
+ m_edges.at(i).node->data = i;
+ QRBTree<int>::Node *right = m_edgeList.next(m_edges.at(i).node);
+ if (left)
+ calculateIntersection(left->data, i);
+ if (right)
+ calculateIntersection(i, right->data);
+ }
+ }
+ while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint <= eventPoint)
+ m_topIntersection.pop();
+#ifdef Q_TRIANGULATOR_DEBUG
+ DebugDialog dialog(this, vertex);
+ dialog.exec();
+#endif
+ }
+ m_processedEdgePairs.clear();
+}
+
+// Split an edge into two pieces at the given point.
+// The upper piece is pushed to the end of the 'm_edges' vector.
+// The lower piece replaces the old edge.
+// Return the edge whose 'from' is 'pointIndex'.
+int QTriangulator::ComplexToSimple::splitEdge(int splitIndex)
+{
+ const Split &split = m_splits.at(splitIndex);
+ Edge &lowerEdge = m_edges.at(split.edge);
+ Q_ASSERT(lowerEdge.node == 0);
+ Q_ASSERT(lowerEdge.previous == -1 && lowerEdge.next == -1);
+
+ if (lowerEdge.from == split.vertex)
+ return split.edge;
+ if (lowerEdge.to == split.vertex)
+ return lowerEdge.next;
+
+ // Check that angle >= 90 degrees.
+ //Q_ASSERT(qDot(m_points.at(m_edges.at(edgeIndex).from) - m_points.at(pointIndex),
+ // m_points.at(m_edges.at(edgeIndex).to) - m_points.at(pointIndex)) <= 0);
+
+ Edge upperEdge = lowerEdge;
+ upperEdge.mayIntersect |= !split.accurate; // The edge may have been split before at an inaccurate split point.
+ lowerEdge.mayIntersect = !split.accurate;
+ if (lowerEdge.pointingUp) {
+ lowerEdge.to = upperEdge.from = split.vertex;
+ m_edges.add(upperEdge);
+ return m_edges.size() - 1;
+ } else {
+ lowerEdge.from = upperEdge.to = split.vertex;
+ m_edges.add(upperEdge);
+ return split.edge;
+ }
+}
+
+bool QTriangulator::ComplexToSimple::splitEdgesAtIntersections()
+{
+ for (int i = 0; i < m_edges.size(); ++i)
+ m_edges.at(i).mayIntersect = false;
+ bool checkForNewIntersections = false;
+ for (int i = 0; i < m_splits.size(); ++i) {
+ splitEdge(i);
+ checkForNewIntersections |= !m_splits.at(i).accurate;
+ }
+ for (int i = 0; i < m_edges.size(); ++i) {
+ m_edges.at(i).originallyPointingUp = m_edges.at(i).pointingUp =
+ m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from);
+ }
+ m_splits.reset();
+ return checkForNewIntersections;
+}
+
+void QTriangulator::ComplexToSimple::insertEdgeIntoVectorIfWanted(ShortArray &orderedEdges, int i)
+{
+ // Edges with zero length should not reach this part.
+ Q_ASSERT(m_parent->m_vertices.at(m_edges.at(i).from) != m_parent->m_vertices.at(m_edges.at(i).to));
+
+ // Skip edges with unwanted winding number.
+ int windingNumber = m_edges.at(i).winding;
+ if (m_edges.at(i).originallyPointingUp)
+ ++windingNumber;
+
+ // Make sure exactly one fill rule is specified.
+ Q_ASSERT(((m_parent->m_hint & QVectorPath::WindingFill) != 0) != ((m_parent->m_hint & QVectorPath::OddEvenFill) != 0));
+
+ if ((m_parent->m_hint & QVectorPath::WindingFill) && windingNumber != 0 && windingNumber != 1)
+ return;
+
+ // Skip cancelling edges.
+ if (!orderedEdges.isEmpty()) {
+ int j = orderedEdges[orderedEdges.size() - 1];
+ // If the last edge is already connected in one end, it should not be cancelled.
+ if (m_edges.at(j).next == -1 && m_edges.at(j).previous == -1
+ && (m_parent->m_vertices.at(m_edges.at(i).from) == m_parent->m_vertices.at(m_edges.at(j).to))
+ && (m_parent->m_vertices.at(m_edges.at(i).to) == m_parent->m_vertices.at(m_edges.at(j).from))) {
+ orderedEdges.removeLast();
+ return;
+ }
+ }
+ orderedEdges.append(i);
+}
+
+void QTriangulator::ComplexToSimple::removeUnwantedEdgesAndConnect()
+{
+ Q_ASSERT(m_edgeList.root == 0);
+ // Initialize priority queue.
+ fillPriorityQueue();
+
+ ShortArray orderedEdges;
+
+ while (!m_events.isEmpty()) {
+ Event event = m_events.last();
+ int edgeIndex = event.edge;
+
+ // Check that all the edges in the list crosses the current scanline
+ //if (m_edgeList.root) {
+ // for (QRBTree<int>::Node *node = m_edgeList.front(m_edgeList.root); node; node = m_edgeList.next(node)) {
+ // Q_ASSERT(event.point <= m_points.at(m_edges.at(node->data).lower()));
+ // }
+ //}
+
+ orderedEdges.clear();
+ QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> b = outerBounds(event.point);
+ if (m_edgeList.root) {
+ QRBTree<int>::Node *current = (b.first ? m_edgeList.next(b.first) : m_edgeList.front(m_edgeList.root));
+ // Process edges that are going to be removed from the edge list at the current event point.
+ while (current != b.second) {
+ Q_ASSERT(current);
+ Q_ASSERT(m_edges.at(current->data).node == current);
+ Q_ASSERT(qIntersectionPoint(event.point).isOnLine(m_parent->m_vertices.at(m_edges.at(current->data).from), m_parent->m_vertices.at(m_edges.at(current->data).to)));
+ Q_ASSERT(m_parent->m_vertices.at(m_edges.at(current->data).from) == event.point || m_parent->m_vertices.at(m_edges.at(current->data).to) == event.point);
+ insertEdgeIntoVectorIfWanted(orderedEdges, current->data);
+ current = m_edgeList.next(current);
+ }
+ }
+
+ // Remove edges above the event point, insert edges below the event point.
+ do {
+ event = m_events.last();
+ m_events.pop_back();
+ edgeIndex = event.edge;
+
+ // Edges with zero length should not reach this part.
+ Q_ASSERT(m_parent->m_vertices.at(m_edges.at(edgeIndex).from) != m_parent->m_vertices.at(m_edges.at(edgeIndex).to));
+
+ if (m_edges.at(edgeIndex).node) {
+ Q_ASSERT(event.type == Event::Lower);
+ Q_ASSERT(event.point == m_parent->m_vertices.at(m_edges.at(event.edge).lower()));
+ m_edgeList.deleteNode(m_edges.at(edgeIndex).node);
+ } else {
+ Q_ASSERT(event.type == Event::Upper);
+ Q_ASSERT(event.point == m_parent->m_vertices.at(m_edges.at(event.edge).upper()));
+ QRBTree<int>::Node *left = searchEdgeLeftOf(edgeIndex, b.first);
+ m_edgeList.attachAfter(left, m_edges.at(edgeIndex).node = m_edgeList.newNode());
+ m_edges.at(edgeIndex).node->data = edgeIndex;
+ }
+ } while (!m_events.isEmpty() && m_events.last().point == event.point);
+
+ if (m_edgeList.root) {
+ QRBTree<int>::Node *current = (b.first ? m_edgeList.next(b.first) : m_edgeList.front(m_edgeList.root));
+
+ // Calculate winding number and turn counter-clockwise.
+ int currentWindingNumber = (b.first ? m_edges.at(b.first->data).winding : 0);
+ while (current != b.second) {
+ Q_ASSERT(current);
+ //Q_ASSERT(b.second == 0 || m_edgeList.order(current, b.second) < 0);
+ int i = current->data;
+ Q_ASSERT(m_edges.at(i).node == current);
+
+ // Winding number.
+ int ccwWindingNumber = m_edges.at(i).winding = currentWindingNumber;
+ if (m_edges.at(i).originallyPointingUp) {
+ --m_edges.at(i).winding;
+ } else {
+ ++m_edges.at(i).winding;
+ ++ccwWindingNumber;
+ }
+ currentWindingNumber = m_edges.at(i).winding;
+
+ // Turn counter-clockwise.
+ if ((ccwWindingNumber & 1) == 0) {
+ Q_ASSERT(m_edges.at(i).previous == -1 && m_edges.at(i).next == -1);
+ qSwap(m_edges.at(i).from, m_edges.at(i).to);
+ m_edges.at(i).pointingUp = !m_edges.at(i).pointingUp;
+ }
+
+ current = m_edgeList.next(current);
+ }
+
+ // Process edges that were inserted into the edge list at the current event point.
+ current = (b.second ? m_edgeList.previous(b.second) : m_edgeList.back(m_edgeList.root));
+ while (current != b.first) {
+ Q_ASSERT(current);
+ Q_ASSERT(m_edges.at(current->data).node == current);
+ insertEdgeIntoVectorIfWanted(orderedEdges, current->data);
+ current = m_edgeList.previous(current);
+ }
+ }
+ if (orderedEdges.isEmpty())
+ continue;
+
+ Q_ASSERT((orderedEdges.size() & 1) == 0);
+
+ // Connect edges.
+ // First make sure the first edge point towards the current point.
+ int i;
+ if (m_parent->m_vertices.at(m_edges.at(orderedEdges[0]).from) == event.point) {
+ i = 1;
+ int copy = orderedEdges[0]; // Make copy in case the append() will cause a reallocation.
+ orderedEdges.append(copy);
+ } else {
+ Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[0]).to) == event.point);
+ i = 0;
+ }
+
+ // Remove references to duplicate points. First find the point with lowest index.
+ int pointIndex = INT_MAX;
+ for (int j = i; j < orderedEdges.size(); j += 2) {
+ Q_ASSERT(j + 1 < orderedEdges.size());
+ Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[j]).to) == event.point);
+ Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[j + 1]).from) == event.point);
+ if (m_edges.at(orderedEdges[j]).to < pointIndex)
+ pointIndex = m_edges.at(orderedEdges[j]).to;
+ if (m_edges.at(orderedEdges[j + 1]).from < pointIndex)
+ pointIndex = m_edges.at(orderedEdges[j + 1]).from;
+ }
+
+ for (; i < orderedEdges.size(); i += 2) {
+ // Remove references to duplicate points by making all edges reference one common point.
+ m_edges.at(orderedEdges[i]).to = m_edges.at(orderedEdges[i + 1]).from = pointIndex;
+
+ Q_ASSERT(m_edges.at(orderedEdges[i]).pointingUp || m_edges.at(orderedEdges[i]).previous != -1);
+ Q_ASSERT(!m_edges.at(orderedEdges[i + 1]).pointingUp || m_edges.at(orderedEdges[i + 1]).next != -1);
+
+ m_edges.at(orderedEdges[i]).next = orderedEdges[i + 1];
+ m_edges.at(orderedEdges[i + 1]).previous = orderedEdges[i];
+ }
+ } // end while
+}
+
+void QTriangulator::ComplexToSimple::removeUnusedPoints() {
+ QBitArray used(m_parent->m_vertices.size(), false);
+ for (int i = 0; i < m_edges.size(); ++i) {
+ Q_ASSERT((m_edges.at(i).previous == -1) == (m_edges.at(i).next == -1));
+ if (m_edges.at(i).next != -1)
+ used.setBit(m_edges.at(i).from);
+ }
+ QDataBuffer<quint32> newMapping(m_parent->m_vertices.size());
+ newMapping.resize(m_parent->m_vertices.size());
+ int count = 0;
+ for (int i = 0; i < m_parent->m_vertices.size(); ++i) {
+ if (used.at(i)) {
+ m_parent->m_vertices.at(count) = m_parent->m_vertices.at(i);
+ newMapping.at(i) = count;
+ ++count;
+ }
+ }
+ m_parent->m_vertices.resize(count);
+ for (int i = 0; i < m_edges.size(); ++i) {
+ m_edges.at(i).from = newMapping.at(m_edges.at(i).from);
+ m_edges.at(i).to = newMapping.at(m_edges.at(i).to);
+ }
+}
+
+bool QTriangulator::ComplexToSimple::CompareEdges::operator () (int i, int j) const
+{
+ int cmp = comparePoints(m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(i).from),
+ m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(j).from));
+ if (cmp == 0) {
+ cmp = comparePoints(m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(i).to),
+ m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(j).to));
+ }
+ return cmp > 0;
+}
+
+inline bool QTriangulator::ComplexToSimple::Event::operator < (const Event &other) const
+{
+ if (point == other.point)
+ return type < other.type; // 'Lower' has higher priority than 'Upper'.
+ return other.point < point;
+}
+
+//============================================================================//
+// QTriangulator::ComplexToSimple::DebugDialog //
+//============================================================================//
+
+#ifdef Q_TRIANGULATOR_DEBUG
+
+QTriangulator::ComplexToSimple::DebugDialog::DebugDialog(ComplexToSimple *parent, int currentVertex)
+ : m_parent(parent), m_vertex(currentVertex)
+{
+ QDataBuffer<QPodPoint> &vertices = m_parent->m_parent->m_vertices;
+ if (vertices.isEmpty())
+ return;
+
+ int minX, maxX, minY, maxY;
+ minX = maxX = vertices.at(0).x;
+ minY = maxY = vertices.at(0).y;
+ for (int i = 1; i < vertices.size(); ++i) {
+ minX = qMin(minX, vertices.at(i).x);
+ maxX = qMax(maxX, vertices.at(i).x);
+ minY = qMin(minY, vertices.at(i).y);
+ maxY = qMax(maxY, vertices.at(i).y);
+ }
+ int w = maxX - minX;
+ int h = maxY - minY;
+ qreal border = qMin(w, h) / 10.0;
+ m_window = QRectF(minX - border, minY - border, (maxX - minX + 2 * border), (maxY - minY + 2 * border));
+}
+
+void QTriangulator::ComplexToSimple::DebugDialog::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ p.setRenderHint(QPainter::Antialiasing, true);
+ p.fillRect(rect(), Qt::black);
+ QDataBuffer<QPodPoint> &vertices = m_parent->m_parent->m_vertices;
+ if (vertices.isEmpty())
+ return;
+
+ qreal halfPointSize = qMin(m_window.width(), m_window.height()) / 300.0;
+ p.setWindow(m_window.toRect());
+
+ p.setPen(Qt::white);
+
+ QDataBuffer<Edge> &edges = m_parent->m_edges;
+ for (int i = 0; i < edges.size(); ++i) {
+ QPodPoint u = vertices.at(edges.at(i).from);
+ QPodPoint v = vertices.at(edges.at(i).to);
+ p.drawLine(u.x, u.y, v.x, v.y);
+ }
+
+ for (int i = 0; i < vertices.size(); ++i) {
+ QPodPoint q = vertices.at(i);
+ p.fillRect(QRectF(q.x - halfPointSize, q.y - halfPointSize, 2 * halfPointSize, 2 * halfPointSize), Qt::red);
+ }
+
+ Qt::GlobalColor colors[6] = {Qt::red, Qt::green, Qt::blue, Qt::cyan, Qt::magenta, Qt::yellow};
+ p.setOpacity(0.5);
+ int count = 0;
+ if (m_parent->m_edgeList.root) {
+ QRBTree<int>::Node *current = m_parent->m_edgeList.front(m_parent->m_edgeList.root);
+ while (current) {
+ p.setPen(colors[count++ % 6]);
+ QPodPoint u = vertices.at(edges.at(current->data).from);
+ QPodPoint v = vertices.at(edges.at(current->data).to);
+ p.drawLine(u.x, u.y, v.x, v.y);
+ current = m_parent->m_edgeList.next(current);
+ }
+ }
+
+ p.setOpacity(1.0);
+ QPodPoint q = vertices.at(m_vertex);
+ p.fillRect(QRectF(q.x - halfPointSize, q.y - halfPointSize, 2 * halfPointSize, 2 * halfPointSize), Qt::green);
+
+ p.setPen(Qt::gray);
+ QDataBuffer<Split> &splits = m_parent->m_splits;
+ for (int i = 0; i < splits.size(); ++i) {
+ QPodPoint q = vertices.at(splits.at(i).vertex);
+ QPodPoint u = vertices.at(edges.at(splits.at(i).edge).from) - q;
+ QPodPoint v = vertices.at(edges.at(splits.at(i).edge).to) - q;
+ qreal uLen = sqrt(qreal(qDot(u, u)));
+ qreal vLen = sqrt(qreal(qDot(v, v)));
+ if (uLen) {
+ u.x *= 2 * halfPointSize / uLen;
+ u.y *= 2 * halfPointSize / uLen;
+ }
+ if (vLen) {
+ v.x *= 2 * halfPointSize / vLen;
+ v.y *= 2 * halfPointSize / vLen;
+ }
+ u += q;
+ v += q;
+ p.drawLine(u.x, u.y, v.x, v.y);
+ }
+}
+
+void QTriangulator::ComplexToSimple::DebugDialog::wheelEvent(QWheelEvent *event)
+{
+ qreal scale = exp(-0.001 * event->delta());
+ QPointF center = m_window.center();
+ QPointF delta = scale * (m_window.bottomRight() - center);
+ m_window = QRectF(center - delta, center + delta);
+ event->accept();
+ update();
+}
+
+void QTriangulator::ComplexToSimple::DebugDialog::mouseMoveEvent(QMouseEvent *event)
+{
+ if (event->buttons() & Qt::LeftButton) {
+ QPointF delta = event->pos() - m_lastMousePos;
+ delta.setX(delta.x() * m_window.width() / width());
+ delta.setY(delta.y() * m_window.height() / height());
+ m_window.translate(-delta.x(), -delta.y());
+ m_lastMousePos = event->pos();
+ event->accept();
+ update();
+ }
+}
+
+void QTriangulator::ComplexToSimple::DebugDialog::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton)
+ m_lastMousePos = event->pos();
+ event->accept();
+}
+
+
+#endif
+
+//============================================================================//
+// QTriangulator::SimpleToMonotone //
+//============================================================================//
+
+void QTriangulator::SimpleToMonotone::decompose()
+{
+ setupDataStructures();
+ removeZeroLengthEdges();
+ monotoneDecomposition();
+
+ m_parent->m_indices.clear();
+ QBitArray processed(m_edges.size(), false);
+ for (int first = 0; first < m_edges.size(); ++first) {
+ if (processed.at(first))
+ continue;
+ int i = first;
+ do {
+ Q_ASSERT(!processed.at(i));
+ Q_ASSERT(m_edges.at(m_edges.at(i).next).previous == i);
+ m_parent->m_indices.push_back(m_edges.at(i).from);
+ processed.setBit(i);
+ i = m_edges.at(i).next;
+ } while (i != first);
+ if (m_parent->m_indices.size() > 0 && m_parent->m_indices.back() != Q_TRIANGULATE_END_OF_POLYGON)
+ m_parent->m_indices.push_back(Q_TRIANGULATE_END_OF_POLYGON);
+ }
+}
+
+void QTriangulator::SimpleToMonotone::setupDataStructures()
+{
+ int i = 0;
+ Edge e;
+ e.node = 0;
+ e.twin = -1;
+
+ while (i + 3 <= m_parent->m_indices.size()) {
+ int start = m_edges.size();
+
+ do {
+ e.from = m_parent->m_indices.at(i);
+ e.type = RegularVertex;
+ e.next = m_edges.size() + 1;
+ e.previous = m_edges.size() - 1;
+ m_edges.add(e);
+ ++i;
+ Q_ASSERT(i < m_parent->m_indices.size());
+ } while (m_parent->m_indices.at(i) != Q_TRIANGULATE_END_OF_POLYGON);
+
+ m_edges.last().next = start;
+ m_edges.at(start).previous = m_edges.size() - 1;
+ ++i; // Skip Q_TRIANGULATE_END_OF_POLYGON.
+ }
+
+ for (i = 0; i < m_edges.size(); ++i) {
+ m_edges.at(i).to = m_edges.at(m_edges.at(i).next).from;
+ m_edges.at(i).pointingUp = m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from);
+ m_edges.at(i).helper = -1; // Not initialized here.
+ }
+}
+
+void QTriangulator::SimpleToMonotone::removeZeroLengthEdges()
+{
+ for (int i = 0; i < m_edges.size(); ++i) {
+ if (m_parent->m_vertices.at(m_edges.at(i).from) == m_parent->m_vertices.at(m_edges.at(i).to)) {
+ m_edges.at(m_edges.at(i).previous).next = m_edges.at(i).next;
+ m_edges.at(m_edges.at(i).next).previous = m_edges.at(i).previous;
+ m_edges.at(m_edges.at(i).next).from = m_edges.at(i).from;
+ m_edges.at(i).next = -1; // Mark as removed.
+ }
+ }
+
+ QDataBuffer<int> newMapping(m_edges.size());
+ newMapping.resize(m_edges.size());
+ int count = 0;
+ for (int i = 0; i < m_edges.size(); ++i) {
+ if (m_edges.at(i).next != -1) {
+ m_edges.at(count) = m_edges.at(i);
+ newMapping.at(i) = count;
+ ++count;
+ }
+ }
+ m_edges.resize(count);
+ for (int i = 0; i < m_edges.size(); ++i) {
+ m_edges.at(i).next = newMapping.at(m_edges.at(i).next);
+ m_edges.at(i).previous = newMapping.at(m_edges.at(i).previous);
+ }
+}
+
+void QTriangulator::SimpleToMonotone::fillPriorityQueue()
+{
+ m_upperVertex.reset();
+ m_upperVertex.reserve(m_edges.size());
+ for (int i = 0; i < m_edges.size(); ++i)
+ m_upperVertex.add(i);
+ CompareVertices cmp(this);
+ //qSort(m_upperVertex.data(), m_upperVertex.data() + m_upperVertex.size(), cmp);
+ sort(m_upperVertex.data(), m_upperVertex.size(), cmp);
+ //for (int i = 1; i < m_upperVertex.size(); ++i) {
+ // Q_ASSERT(!cmp(m_upperVertex.at(i), m_upperVertex.at(i - 1)));
+ //}
+}
+
+bool QTriangulator::SimpleToMonotone::edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const
+{
+ const Edge &leftEdge = m_edges.at(leftEdgeIndex);
+ const Edge &rightEdge = m_edges.at(rightEdgeIndex);
+ const QPodPoint &u = m_parent->m_vertices.at(rightEdge.upper());
+ const QPodPoint &l = m_parent->m_vertices.at(rightEdge.lower());
+ qint64 d = qPointDistanceFromLine(m_parent->m_vertices.at(leftEdge.upper()), l, u);
+ // d < 0: left, d > 0: right, d == 0: on top
+ if (d == 0)
+ d = qPointDistanceFromLine(m_parent->m_vertices.at(leftEdge.lower()), l, u);
+ return d < 0;
+}
+
+// Returns the rightmost edge not to the right of the given edge.
+QRBTree<int>::Node *QTriangulator::SimpleToMonotone::searchEdgeLeftOfEdge(int edgeIndex) const
+{
+ QRBTree<int>::Node *current = m_edgeList.root;
+ QRBTree<int>::Node *result = 0;
+ while (current) {
+ if (edgeIsLeftOfEdge(edgeIndex, current->data)) {
+ current = current->left;
+ } else {
+ result = current;
+ current = current->right;
+ }
+ }
+ return result;
+}
+
+// Returns the rightmost edge left of the given point.
+QRBTree<int>::Node *QTriangulator::SimpleToMonotone::searchEdgeLeftOfPoint(int pointIndex) const
+{
+ QRBTree<int>::Node *current = m_edgeList.root;
+ QRBTree<int>::Node *result = 0;
+ while (current) {
+ const QPodPoint &p1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &p2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = qPointDistanceFromLine(m_parent->m_vertices.at(pointIndex), p1, p2);
+ if (d <= 0) {
+ current = current->left;
+ } else {
+ result = current;
+ current = current->right;
+ }
+ }
+ return result;
+}
+
+void QTriangulator::SimpleToMonotone::classifyVertex(int i)
+{
+ Edge &e2 = m_edges.at(i);
+ const Edge &e1 = m_edges.at(e2.previous);
+
+ bool startOrSplit = (e1.pointingUp && !e2.pointingUp);
+ bool endOrMerge = (!e1.pointingUp && e2.pointingUp);
+
+ const QPodPoint &p1 = m_parent->m_vertices.at(e1.from);
+ const QPodPoint &p2 = m_parent->m_vertices.at(e2.from);
+ const QPodPoint &p3 = m_parent->m_vertices.at(e2.to);
+ qint64 d = qPointDistanceFromLine(p1, p2, p3);
+ Q_ASSERT(d != 0 || (!startOrSplit && !endOrMerge));
+
+ e2.type = RegularVertex;
+
+ if (m_clockwiseOrder) {
+ if (startOrSplit)
+ e2.type = (d < 0 ? SplitVertex : StartVertex);
+ else if (endOrMerge)
+ e2.type = (d < 0 ? MergeVertex : EndVertex);
+ } else {
+ if (startOrSplit)
+ e2.type = (d > 0 ? SplitVertex : StartVertex);
+ else if (endOrMerge)
+ e2.type = (d > 0 ? MergeVertex : EndVertex);
+ }
+}
+
+void QTriangulator::SimpleToMonotone::classifyVertices()
+{
+ for (int i = 0; i < m_edges.size(); ++i)
+ classifyVertex(i);
+}
+
+bool QTriangulator::SimpleToMonotone::pointIsInSector(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2, const QPodPoint &v3)
+{
+ bool leftOfPreviousEdge = !qPointIsLeftOfLine(p, v2, v1);
+ bool leftOfNextEdge = !qPointIsLeftOfLine(p, v3, v2);
+
+ if (qPointIsLeftOfLine(v1, v2, v3))
+ return leftOfPreviousEdge && leftOfNextEdge;
+ else
+ return leftOfPreviousEdge || leftOfNextEdge;
+}
+
+bool QTriangulator::SimpleToMonotone::pointIsInSector(int vertex, int sector)
+{
+ const QPodPoint &center = m_parent->m_vertices.at(m_edges.at(sector).from);
+ // Handle degenerate edges.
+ while (m_parent->m_vertices.at(m_edges.at(vertex).from) == center)
+ vertex = m_edges.at(vertex).next;
+ int next = m_edges.at(sector).next;
+ while (m_parent->m_vertices.at(m_edges.at(next).from) == center)
+ next = m_edges.at(next).next;
+ int previous = m_edges.at(sector).previous;
+ while (m_parent->m_vertices.at(m_edges.at(previous).from) == center)
+ previous = m_edges.at(previous).previous;
+
+ const QPodPoint &p = m_parent->m_vertices.at(m_edges.at(vertex).from);
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(previous).from);
+ const QPodPoint &v3 = m_parent->m_vertices.at(m_edges.at(next).from);
+ if (m_clockwiseOrder)
+ return pointIsInSector(p, v3, center, v1);
+ else
+ return pointIsInSector(p, v1, center, v3);
+}
+
+int QTriangulator::SimpleToMonotone::findSector(int edge, int vertex)
+{
+ while (!pointIsInSector(vertex, edge)) {
+ edge = m_edges.at(m_edges.at(edge).previous).twin;
+ Q_ASSERT(edge != -1);
+ }
+ return edge;
+}
+
+void QTriangulator::SimpleToMonotone::createDiagonal(int lower, int upper)
+{
+ lower = findSector(lower, upper);
+ upper = findSector(upper, lower);
+
+ int prevLower = m_edges.at(lower).previous;
+ int prevUpper = m_edges.at(upper).previous;
+
+ Edge e;
+
+ e.twin = m_edges.size() + 1;
+ e.next = upper;
+ e.previous = prevLower;
+ e.from = m_edges.at(lower).from;
+ e.to = m_edges.at(upper).from;
+ m_edges.at(upper).previous = m_edges.at(prevLower).next = int(m_edges.size());
+ m_edges.add(e);
+
+ e.twin = m_edges.size() - 1;
+ e.next = lower;
+ e.previous = prevUpper;
+ e.from = m_edges.at(upper).from;
+ e.to = m_edges.at(lower).from;
+ m_edges.at(lower).previous = m_edges.at(prevUpper).next = int(m_edges.size());
+ m_edges.add(e);
+}
+
+void QTriangulator::SimpleToMonotone::monotoneDecomposition()
+{
+ if (m_edges.isEmpty())
+ return;
+
+ Q_ASSERT(!m_edgeList.root);
+ QDataBuffer<QPair<int, int> > diagonals(m_upperVertex.size());
+
+ int i = 0;
+ for (int index = 1; index < m_edges.size(); ++index) {
+ if (m_parent->m_vertices.at(m_edges.at(index).from) < m_parent->m_vertices.at(m_edges.at(i).from))
+ i = index;
+ }
+ Q_ASSERT(i < m_edges.size());
+ int j = m_edges.at(i).previous;
+ Q_ASSERT(j < m_edges.size());
+ m_clockwiseOrder = qPointIsLeftOfLine(m_parent->m_vertices.at(m_edges.at(i).from),
+ m_parent->m_vertices.at(m_edges.at(j).from), m_parent->m_vertices.at(m_edges.at(i).to));
+
+ classifyVertices();
+ fillPriorityQueue();
+
+ // debug: set helpers explicitly (shouldn't be necessary)
+ //for (int i = 0; i < m_edges.size(); ++i)
+ // m_edges.at(i).helper = m_edges.at(i).upper();
+
+ while (!m_upperVertex.isEmpty()) {
+ i = m_upperVertex.last();
+ Q_ASSERT(i < m_edges.size());
+ m_upperVertex.pop_back();
+ j = m_edges.at(i).previous;
+ Q_ASSERT(j < m_edges.size());
+
+ QRBTree<int>::Node *leftEdgeNode = 0;
+
+ switch (m_edges.at(i).type) {
+ case RegularVertex:
+ // If polygon interior is to the right of the vertex...
+ if (m_edges.at(i).pointingUp == m_clockwiseOrder) {
+ if (m_edges.at(i).node) {
+ Q_ASSERT(!m_edges.at(j).node);
+ if (m_edges.at(m_edges.at(i).helper).type == MergeVertex)
+ diagonals.add(QPair<int, int>(i, m_edges.at(i).helper));
+ m_edges.at(j).node = m_edges.at(i).node;
+ m_edges.at(i).node = 0;
+ m_edges.at(j).node->data = j;
+ m_edges.at(j).helper = i;
+ } else if (m_edges.at(j).node) {
+ Q_ASSERT(!m_edges.at(i).node);
+ if (m_edges.at(m_edges.at(j).helper).type == MergeVertex)
+ diagonals.add(QPair<int, int>(i, m_edges.at(j).helper));
+ m_edges.at(i).node = m_edges.at(j).node;
+ m_edges.at(j).node = 0;
+ m_edges.at(i).node->data = i;
+ m_edges.at(i).helper = i;
+ } else {
+ qWarning("Inconsistent polygon. (#1)");
+ }
+ } else {
+ leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from);
+ if (leftEdgeNode) {
+ if (m_edges.at(m_edges.at(leftEdgeNode->data).helper).type == MergeVertex)
+ diagonals.add(QPair<int, int>(i, m_edges.at(leftEdgeNode->data).helper));
+ m_edges.at(leftEdgeNode->data).helper = i;
+ } else {
+ qWarning("Inconsistent polygon. (#2)");
+ }
+ }
+ break;
+ case SplitVertex:
+ leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from);
+ if (leftEdgeNode) {
+ diagonals.add(QPair<int, int>(i, m_edges.at(leftEdgeNode->data).helper));
+ m_edges.at(leftEdgeNode->data).helper = i;
+ } else {
+ qWarning("Inconsistent polygon. (#3)");
+ }
+ // Fall through.
+ case StartVertex:
+ if (m_clockwiseOrder) {
+ leftEdgeNode = searchEdgeLeftOfEdge(j);
+ QRBTree<int>::Node *node = m_edgeList.newNode();
+ node->data = j;
+ m_edges.at(j).node = node;
+ m_edges.at(j).helper = i;
+ m_edgeList.attachAfter(leftEdgeNode, node);
+ Q_ASSERT(m_edgeList.verify());
+ } else {
+ leftEdgeNode = searchEdgeLeftOfEdge(i);
+ QRBTree<int>::Node *node = m_edgeList.newNode();
+ node->data = i;
+ m_edges.at(i).node = node;
+ m_edges.at(i).helper = i;
+ m_edgeList.attachAfter(leftEdgeNode, node);
+ Q_ASSERT(m_edgeList.verify());
+ }
+ break;
+ case MergeVertex:
+ leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from);
+ if (leftEdgeNode) {
+ if (m_edges.at(m_edges.at(leftEdgeNode->data).helper).type == MergeVertex)
+ diagonals.add(QPair<int, int>(i, m_edges.at(leftEdgeNode->data).helper));
+ m_edges.at(leftEdgeNode->data).helper = i;
+ } else {
+ qWarning("Inconsistent polygon. (#4)");
+ }
+ // Fall through.
+ case EndVertex:
+ if (m_clockwiseOrder) {
+ if (m_edges.at(m_edges.at(i).helper).type == MergeVertex)
+ diagonals.add(QPair<int, int>(i, m_edges.at(i).helper));
+ if (m_edges.at(i).node) {
+ m_edgeList.deleteNode(m_edges.at(i).node);
+ Q_ASSERT(m_edgeList.verify());
+ } else {
+ qWarning("Inconsistent polygon. (#5)");
+ }
+ } else {
+ if (m_edges.at(m_edges.at(j).helper).type == MergeVertex)
+ diagonals.add(QPair<int, int>(i, m_edges.at(j).helper));
+ if (m_edges.at(j).node) {
+ m_edgeList.deleteNode(m_edges.at(j).node);
+ Q_ASSERT(m_edgeList.verify());
+ } else {
+ qWarning("Inconsistent polygon. (#6)");
+ }
+ }
+ break;
+ }
+ }
+
+ for (int i = 0; i < diagonals.size(); ++i)
+ createDiagonal(diagonals.at(i).first, diagonals.at(i).second);
+}
+
+bool QTriangulator::SimpleToMonotone::CompareVertices::operator () (int i, int j) const
+{
+ if (m_parent->m_edges.at(i).from == m_parent->m_edges.at(j).from)
+ return m_parent->m_edges.at(i).type > m_parent->m_edges.at(j).type;
+ return m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(i).from) >
+ m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(j).from);
+}
+
+//============================================================================//
+// QTriangulator::MonotoneToTriangles //
+//============================================================================//
+
+void QTriangulator::MonotoneToTriangles::decompose()
+{
+ QVector<quint32> result;
+ QDataBuffer<int> stack(m_parent->m_indices.size());
+ m_first = 0;
+ // Require at least three more indices.
+ while (m_first + 3 <= m_parent->m_indices.size()) {
+ m_length = 0;
+ while (m_parent->m_indices.at(m_first + m_length) != Q_TRIANGULATE_END_OF_POLYGON) {
+ ++m_length;
+ Q_ASSERT(m_first + m_length < m_parent->m_indices.size());
+ }
+ if (m_length < 3) {
+ m_first += m_length + 1;
+ continue;
+ }
+
+ int minimum = 0;
+ while (less(next(minimum), minimum))
+ minimum = next(minimum);
+ while (less(previous(minimum), minimum))
+ minimum = previous(minimum);
+
+ stack.reset();
+ stack.add(minimum);
+ int left = previous(minimum);
+ int right = next(minimum);
+ bool stackIsOnLeftSide;
+ bool clockwiseOrder = leftOfEdge(minimum, left, right);
+
+ if (less(left, right)) {
+ stack.add(left);
+ left = previous(left);
+ stackIsOnLeftSide = true;
+ } else {
+ stack.add(right);
+ right = next(right);
+ stackIsOnLeftSide = false;
+ }
+
+ for (int count = 0; count + 2 < m_length; ++count)
+ {
+ Q_ASSERT(stack.size() >= 2);
+ if (less(left, right)) {
+ if (stackIsOnLeftSide == false) {
+ for (int i = 0; i + 1 < stack.size(); ++i) {
+ result.push_back(indices(stack.at(i + 1)));
+ result.push_back(indices(left));
+ result.push_back(indices(stack.at(i)));
+ }
+ stack.first() = stack.last();
+ stack.resize(1);
+ } else {
+ while (stack.size() >= 2 && (clockwiseOrder ^ !leftOfEdge(left, stack.at(stack.size() - 2), stack.last()))) {
+ result.push_back(indices(stack.at(stack.size() - 2)));
+ result.push_back(indices(left));
+ result.push_back(indices(stack.last()));
+ stack.pop_back();
+ }
+ }
+ stack.add(left);
+ left = previous(left);
+ stackIsOnLeftSide = true;
+ } else {
+ if (stackIsOnLeftSide == true) {
+ for (int i = 0; i + 1 < stack.size(); ++i) {
+ result.push_back(indices(stack.at(i)));
+ result.push_back(indices(right));
+ result.push_back(indices(stack.at(i + 1)));
+ }
+ stack.first() = stack.last();
+ stack.resize(1);
+ } else {
+ while (stack.size() >= 2 && (clockwiseOrder ^ !leftOfEdge(right, stack.last(), stack.at(stack.size() - 2)))) {
+ result.push_back(indices(stack.last()));
+ result.push_back(indices(right));
+ result.push_back(indices(stack.at(stack.size() - 2)));
+ stack.pop_back();
+ }
+ }
+ stack.add(right);
+ right = next(right);
+ stackIsOnLeftSide = false;
+ }
+ }
+
+ m_first += m_length + 1;
+ }
+ m_parent->m_indices = result;
+}
+
+//============================================================================//
+// qTriangulate //
+//============================================================================//
+
+QTriangleSet qTriangulate(const qreal *polygon, int count, uint hint, const QTransform &matrix)
+{
+ QTriangulator triangulator;
+ triangulator.initialize(polygon, count, hint, matrix);
+ return triangulator.triangulate();
+}
+
+QTriangleSet qTriangulate(const QVectorPath &path, const QTransform &matrix, qreal lod)
+{
+ QTriangulator triangulator;
+ triangulator.initialize(path, matrix, lod);
+ return triangulator.triangulate();
+}
+
+QTriangleSet qTriangulate(const QPainterPath &path, const QTransform &matrix, qreal lod)
+{
+ QTriangulator triangulator;
+ triangulator.initialize(path, matrix, lod);
+ return triangulator.triangulate();
+}
+
+QPolylineSet qPolyline(const QVectorPath &path, const QTransform &matrix, qreal lod)
+{
+ QTriangulator triangulator;
+ triangulator.initialize(path, matrix, lod);
+ return triangulator.polyline();
+}
+
+QPolylineSet qPolyline(const QPainterPath &path, const QTransform &matrix, qreal lod)
+{
+ QTriangulator triangulator;
+ triangulator.initialize(path, matrix, lod);
+ return triangulator.polyline();
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/gl2paintengineex/qtriangulator_p.h b/src/opengl/gl2paintengineex/qtriangulator_p.h
new file mode 100644
index 0000000..e5eec39
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qtriangulator_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTRIANGULATOR_P_H
+#define QTRIANGULATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qvector.h>
+#include <QtGui/private/qvectorpath_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define Q_TRIANGULATE_END_OF_POLYGON quint32(-1)
+
+struct QTriangleSet
+{
+ inline QTriangleSet() { }
+ inline QTriangleSet(const QTriangleSet &other) : vertices(other.vertices), indices(other.indices) { }
+ QTriangleSet &operator = (const QTriangleSet &other) {vertices = other.vertices; indices = other.indices; return *this;}
+
+ // The vertices of a triangle are given by: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ...
+ QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...]
+ QVector<quint32> indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...]
+};
+
+struct QPolylineSet
+{
+ inline QPolylineSet() { }
+ inline QPolylineSet(const QPolylineSet &other) : vertices(other.vertices), indices(other.indices) { }
+ QPolylineSet &operator = (const QPolylineSet &other) {vertices = other.vertices; indices = other.indices; return *this;}
+
+ QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...]
+ QVector<quint32> indices;
+
+};
+
+// The vertex coordinates of the returned triangle set will be rounded to a grid with a mesh size
+// of 1/32. The polygon is first transformed, then scaled by 32, the coordinates are rounded to
+// integers, the polygon is triangulated, and then scaled back by 1/32.
+// 'hint' should be a combination of QVectorPath::Hints.
+// 'lod' is the level of detail. Default is 1. Curves are split into more lines when 'lod' is higher.
+QTriangleSet qTriangulate(const qreal *polygon, int count, uint hint = QVectorPath::PolygonHint | QVectorPath::OddEvenFill, const QTransform &matrix = QTransform());
+QTriangleSet qTriangulate(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1);
+QTriangleSet qTriangulate(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1);
+QPolylineSet qPolyline(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1);
+QPolylineSet qPolyline(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro
index 6076891..d6011cf 100644
--- a/src/opengl/opengl.pro
+++ b/src/opengl/opengl.pro
@@ -5,6 +5,7 @@ DEFINES += QT_BUILD_OPENGL_LIB
DEFINES += QT_NO_USING_NAMESPACE
win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x63000000
solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2
+irix-cc*:QMAKE_CXXFLAGS += -no_prelink -ptused
unix:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui
@@ -13,7 +14,6 @@ include(../qbase.pri)
!win32:!embedded:!mac:CONFIG += x11
contains(QT_CONFIG, opengl):CONFIG += opengl
contains(QT_CONFIG, opengles1):CONFIG += opengles1
-contains(QT_CONFIG, opengles1cl):CONFIG += opengles1cl
contains(QT_CONFIG, opengles2):CONFIG += opengles2
contains(QT_CONFIG, egl):CONFIG += egl
@@ -26,6 +26,7 @@ HEADERS += qgl.h \
qglframebufferobject_p.h \
qglextensions_p.h \
qglpaintdevice_p.h \
+ qglbuffer.h \
SOURCES += qgl.cpp \
@@ -34,6 +35,7 @@ SOURCES += qgl.cpp \
qglframebufferobject.cpp \
qglextensions.cpp \
qglpaintdevice.cpp \
+ qglbuffer.cpp \
!contains(QT_CONFIG, opengles2) {
@@ -41,7 +43,7 @@ SOURCES += qgl.cpp \
SOURCES += qpaintengine_opengl.cpp
}
-!contains(QT_CONFIG, opengles1):!contains(QT_CONFIG, opengles1cl) {
+!contains(QT_CONFIG, opengles1) {
HEADERS += qglshaderprogram.h \
qglpixmapfilter_p.h \
qgraphicsshadereffect_p.h \
@@ -55,6 +57,7 @@ SOURCES += qgl.cpp \
gl2paintengineex/qglengineshadersource_p.h \
gl2paintengineex/qglcustomshaderstage_p.h \
gl2paintengineex/qtriangulatingstroker_p.h \
+ gl2paintengineex/qtriangulator_p.h \
gl2paintengineex/qtextureglyphcache_gl_p.h
SOURCES += qglshaderprogram.cpp \
@@ -69,12 +72,13 @@ SOURCES += qgl.cpp \
gl2paintengineex/qpaintengineex_opengl2.cpp \
gl2paintengineex/qglcustomshaderstage.cpp \
gl2paintengineex/qtriangulatingstroker.cpp \
+ gl2paintengineex/qtriangulator.cpp \
gl2paintengineex/qtextureglyphcache_gl.cpp
}
x11 {
- contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, opengles1cl)|contains(QT_CONFIG, opengles2) {
+ contains(QT_CONFIG, egl) {
SOURCES += qgl_x11egl.cpp \
qglpixelbuffer_egl.cpp \
qgl_egl.cpp \
@@ -113,6 +117,7 @@ mac {
LIBS_PRIVATE += -framework AppKit -framework Carbon
}
win32:!wince*: {
+ DEFINES += QT_NO_EGL
SOURCES += qgl_win.cpp \
qglpixelbuffer_win.cpp
}
@@ -121,8 +126,7 @@ wince*: {
qglpixelbuffer_egl.cpp \
qgl_egl.cpp
- HEADERS += qgl_cl_p.h \
- qgl_egl_p.h \
+ HEADERS += qgl_egl_p.h
}
embedded {
diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp
index 0521bab..af016cf 100644
--- a/src/opengl/qgl.cpp
+++ b/src/opengl/qgl.cpp
@@ -49,7 +49,7 @@
#include "private/qpixmap_x11_p.h"
#define INT32 dummy_INT32
#define INT8 dummy_INT8
-#if !defined(QT_OPENGL_ES)
+#ifdef QT_NO_EGL
# include <GL/glx.h>
#endif
#undef INT32
@@ -67,7 +67,7 @@
#include "qimage.h"
#include "qgl_p.h"
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#if !defined(QT_OPENGL_ES_1)
#include "gl2paintengineex/qpaintengineex_opengl2_p.h"
#endif
@@ -91,15 +91,11 @@
#include "qcolormap.h"
#include "qfile.h"
#include "qlibrary.h"
+#include <qmutex.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
@@ -226,6 +222,9 @@ bool qt_gl_preferGL2Engine()
\value DirectRendering Specifies that the context is used for direct rendering to a display.
\value HasOverlay Enables the use of an overlay.
\value SampleBuffers Enables the use of sample buffers.
+ \value DeprecatedFunctions Enables the use of deprecated functionality for OpenGL 3.x
+ contexts. A context with deprecated functionality enabled is
+ called a full context in the OpenGL specification.
\value SingleBuffer Specifies the use of a single buffer, as opposed to double buffers.
\value NoDepthBuffer Disables the use of a depth buffer.
\value ColorIndex Specifies that the context should use a color index as its pixel format.
@@ -236,6 +235,9 @@ bool qt_gl_preferGL2Engine()
\value IndirectRendering Specifies that the context is used for indirect rendering to a buffer.
\value NoOverlay Disables the use of an overlay.
\value NoSampleBuffers Disables the use of sample buffers.
+ \value NoDeprecatedFunctions Disables the use of deprecated functionality for OpenGL 3.x
+ contexts. A context with deprecated functionality disabled is
+ called a forward compatible context in the OpenGL specification.
\sa {Sample Buffers Example}
*/
@@ -765,6 +767,7 @@ void QGLFormat::setSamples(int numSamples)
return;
}
d->numSamples = numSamples;
+ setSampleBuffers(numSamples > 0);
}
/*!
@@ -903,6 +906,7 @@ void QGLFormat::setDepthBufferSize(int size)
return;
}
d->depthSize = size;
+ setDepth(size > 0);
}
/*!
@@ -1016,7 +1020,7 @@ void QGLFormat::setAlphaBufferSize(int size)
return;
}
d->alphaSize = size;
- setOption(QGL::AlphaChannel);
+ setAlpha(size > 0);
}
/*!
@@ -1043,6 +1047,7 @@ void QGLFormat::setAccumBufferSize(int size)
return;
}
d->accumSize = size;
+ setAccum(size > 0);
}
/*!
@@ -1068,6 +1073,7 @@ void QGLFormat::setStencilBufferSize(int size)
return;
}
d->stencilSize = size;
+ setStencil(size > 0);
}
/*!
@@ -1081,6 +1087,90 @@ int QGLFormat::stencilBufferSize() const
}
/*!
+ \since 4.7
+
+ Set the OpenGL version to the \a major and \a minor numbers. If a
+ context compatible with the requested OpenGL version cannot be
+ created, a context compatible with version 1.x is created instead.
+
+ \sa majorVersion(), minorVersion()
+*/
+void QGLFormat::setVersion(int major, int minor)
+{
+ if (major < 1 || minor < 0) {
+ qWarning("QGLFormat::setVersion: Cannot set zero or negative version number %d.%d", major, minor);
+ return;
+ }
+ detach();
+ d->majorVersion = major;
+ d->minorVersion = minor;
+}
+
+/*!
+ \since 4.7
+
+ Returns the OpenGL major version.
+
+ \sa setVersion(), minorVersion()
+*/
+int QGLFormat::majorVersion() const
+{
+ return d->majorVersion;
+}
+
+/*!
+ \since 4.7
+
+ Returns the OpenGL minor version.
+
+ \sa setVersion(), majorVersion()
+*/
+int QGLFormat::minorVersion() const
+{
+ return d->minorVersion;
+}
+
+/*!
+ \enum QGLFormat::OpenGLContextProfile
+ \since 4.7
+
+ This enum describes the OpenGL context profiles that can be
+ specified for contexts implementing OpenGL version 3.2 or
+ higher. These profiles are different from OpenGL ES profiles.
+
+ \value NoProfile OpenGL version is lower than 3.2.
+ \value CoreProfile Functionality deprecated in OpenGL version 3.0 is not available.
+ \value CompatibilityProfile Functionality from earlier OpenGL versions is available.
+*/
+
+/*!
+ \since 4.7
+
+ Set the OpenGL context profile to \a profile. The \a profile is
+ ignored if the requested OpenGL version is less than 3.2.
+
+ \sa profile()
+*/
+void QGLFormat::setProfile(OpenGLContextProfile profile)
+{
+ detach();
+ d->profile = profile;
+}
+
+/*!
+ \since 4.7
+
+ Returns the OpenGL context profile.
+
+ \sa setProfile()
+*/
+QGLFormat::OpenGLContextProfile QGLFormat::profile() const
+{
+ return d->profile;
+}
+
+
+/*!
\fn bool QGLFormat::hasOpenGL()
Returns true if the window system has any OpenGL support;
@@ -1116,25 +1206,21 @@ QGLFormat::OpenGLVersionFlags Q_AUTOTEST_EXPORT qOpenGLVersionFlagsFromString(co
if (parts[2].startsWith(QLatin1String("1.1")))
versionFlags |= QGLFormat::OpenGL_ES_Common_Version_1_1 |
QGLFormat::OpenGL_ES_CommonLite_Version_1_1;
- }
- else {
+ } 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 {
+ } else {
// OpenGL ES version 2.0 or higher
versionFlags |= QGLFormat::OpenGL_ES_Version_2_0;
}
- }
- else {
+ } else {
// if < 3 parts to the name, it is an unrecognised OpenGL ES
qWarning("Unrecognised OpenGL ES version");
}
- }
- else {
+ } else {
// not ES, regular OpenGL, the version numbers are first in the string
if (versionString.startsWith(QLatin1String("1."))) {
switch (versionString[2].toAscii()) {
@@ -1151,30 +1237,66 @@ QGLFormat::OpenGLVersionFlags Q_AUTOTEST_EXPORT qOpenGLVersionFlagsFromString(co
default:
break;
}
- }
- else if (versionString.startsWith(QLatin1String("2."))) {
+ } 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')))
+ if (versionString[2].toAscii() == '1')
versionFlags |= QGLFormat::OpenGL_Version_2_1;
+ } else if (versionString.startsWith(QLatin1String("3."))) {
+ versionFlags |= QGLFormat::OpenGL_Version_1_1 |
+ QGLFormat::OpenGL_Version_1_2 |
+ QGLFormat::OpenGL_Version_1_3 |
+ QGLFormat::OpenGL_Version_1_4 |
+ QGLFormat::OpenGL_Version_1_5 |
+ QGLFormat::OpenGL_Version_2_0 |
+ QGLFormat::OpenGL_Version_2_1 |
+ QGLFormat::OpenGL_Version_3_0;
+ switch (versionString[2].toAscii()) {
+ case '3':
+ versionFlags |= QGLFormat::OpenGL_Version_3_3;
+ case '2':
+ versionFlags |= QGLFormat::OpenGL_Version_3_2;
+ case '1':
+ versionFlags |= QGLFormat::OpenGL_Version_3_1;
+ case '0':
+ break;
+ default:
+ versionFlags |= QGLFormat::OpenGL_Version_3_1 |
+ QGLFormat::OpenGL_Version_3_2 |
+ QGLFormat::OpenGL_Version_3_3;
+ break;
+ }
+ } else if (versionString.startsWith(QLatin1String("4."))) {
+ versionFlags |= QGLFormat::OpenGL_Version_1_1 |
+ QGLFormat::OpenGL_Version_1_2 |
+ QGLFormat::OpenGL_Version_1_3 |
+ QGLFormat::OpenGL_Version_1_4 |
+ QGLFormat::OpenGL_Version_1_5 |
+ QGLFormat::OpenGL_Version_2_0 |
+ QGLFormat::OpenGL_Version_2_1 |
+ QGLFormat::OpenGL_Version_3_0 |
+ QGLFormat::OpenGL_Version_3_1 |
+ QGLFormat::OpenGL_Version_3_2 |
+ QGLFormat::OpenGL_Version_3_3 |
+ QGLFormat::OpenGL_Version_4_0;
+ } else {
+ versionFlags |= QGLFormat::OpenGL_Version_1_1 |
+ QGLFormat::OpenGL_Version_1_2 |
+ QGLFormat::OpenGL_Version_1_3 |
+ QGLFormat::OpenGL_Version_1_4 |
+ QGLFormat::OpenGL_Version_1_5 |
+ QGLFormat::OpenGL_Version_2_0 |
+ QGLFormat::OpenGL_Version_2_1 |
+ QGLFormat::OpenGL_Version_3_0 |
+ QGLFormat::OpenGL_Version_3_1 |
+ QGLFormat::OpenGL_Version_3_2 |
+ QGLFormat::OpenGL_Version_3_3 |
+ QGLFormat::OpenGL_Version_4_0;
}
- 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;
}
@@ -1206,6 +1328,12 @@ QGLFormat::OpenGLVersionFlags Q_AUTOTEST_EXPORT qOpenGLVersionFlagsFromString(co
\value OpenGL_Version_3_0 OpenGL version 3.0 or higher is present.
+ \value OpenGL_Version_3_1 OpenGL version 3.1 or higher is present.
+ Note that OpenGL version 3.1 or higher does not necessarily support all the features of
+ version 3.0 and lower.
+
+ \value OpenGL_Version_3_2 OpenGL version 3.2 or higher is present.
+
\value OpenGL_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.
@@ -1377,15 +1505,47 @@ void QGLFormat::setDefaultOverlayFormat(const QGLFormat &f)
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
+ return (a.d == b.d) || ((int) a.d->opts == (int) b.d->opts
+ && a.d->pln == b.d->pln
+ && a.d->alphaSize == b.d->alphaSize
+ && a.d->accumSize == b.d->accumSize
+ && a.d->stencilSize == b.d->stencilSize
&& a.d->depthSize == b.d->depthSize
&& a.d->redSize == b.d->redSize
&& a.d->greenSize == b.d->greenSize
&& a.d->blueSize == b.d->blueSize
&& a.d->numSamples == b.d->numSamples
- && a.d->swapInterval == b.d->swapInterval;
+ && a.d->swapInterval == b.d->swapInterval
+ && a.d->majorVersion == b.d->majorVersion
+ && a.d->minorVersion == b.d->minorVersion
+ && a.d->profile == b.d->profile);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QGLFormat &f)
+{
+ const QGLFormatPrivate * const d = f.d;
+
+ dbg.nospace() << "QGLFormat("
+ << "options " << d->opts
+ << ", plane " << d->pln
+ << ", depthBufferSize " << d->depthSize
+ << ", accumBufferSize " << d->accumSize
+ << ", stencilBufferSize " << d->stencilSize
+ << ", redBufferSize " << d->redSize
+ << ", greenBufferSize " << d->greenSize
+ << ", blueBufferSize " << d->blueSize
+ << ", alphaBufferSize " << d->alphaSize
+ << ", samples " << d->numSamples
+ << ", swapInterval " << d->swapInterval
+ << ", majorVersion " << d->majorVersion
+ << ", minorVersion " << d->minorVersion
+ << ", profile " << d->profile
+ << ')';
+
+ return dbg.space();
}
+#endif
/*!
@@ -1400,10 +1560,33 @@ bool operator!=(const QGLFormat& a, const QGLFormat& b)
return !(a == b);
}
+struct QGLContextGroupList {
+ void append(QGLContextGroup *group) {
+ QMutexLocker locker(&m_mutex);
+ m_list.append(group);
+ }
+
+ void remove(QGLContextGroup *group) {
+ QMutexLocker locker(&m_mutex);
+ m_list.removeOne(group);
+ }
+
+ QList<QGLContextGroup *> m_list;
+ QMutex m_mutex;
+};
+
+Q_GLOBAL_STATIC(QGLContextGroupList, qt_context_groups)
+
/*****************************************************************************
QGLContext implementation
*****************************************************************************/
+QGLContextGroup::QGLContextGroup(const QGLContext *context)
+ : m_context(context), m_guards(0), m_refs(1)
+{
+ qt_context_groups()->append(this);
+}
+
QGLContextGroup::~QGLContextGroup()
{
// Clear any remaining QGLSharedResourceGuard objects on the group.
@@ -1413,6 +1596,7 @@ QGLContextGroup::~QGLContextGroup()
guard->m_id = 0;
guard = guard->m_next;
}
+ qt_context_groups()->remove(this);
}
void QGLContextGroup::addGuard(QGLSharedResourceGuard *guard)
@@ -1480,7 +1664,8 @@ void QGLContextPrivate::init(QPaintDevice *dev, const QGLFormat &format)
# endif
vi = 0;
#endif
-#if defined(QT_OPENGL_ES)
+#ifndef QT_NO_EGL
+ ownsEglContext = false;
eglContext = 0;
eglSurface = EGL_NO_SURFACE;
#endif
@@ -1496,6 +1681,9 @@ void QGLContextPrivate::init(QPaintDevice *dev, const QGLFormat &format)
current_fbo = 0;
default_fbo = 0;
active_engine = 0;
+ workaround_needsFullClearOnEveryFrame = false;
+ workaround_brokenFBOReadBack = false;
+ workaroundsCached = false;
for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i)
vertexAttributeArraysEnabledState[i] = false;
}
@@ -1566,7 +1754,7 @@ QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alp
QImage img(size, alpha_format ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
int w = size.width();
int h = size.height();
-#if !defined(QT_OPENGL_ES_2) && !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#if !defined(QT_OPENGL_ES_2) && !defined(QT_OPENGL_ES_1)
//### glGetTexImage not in GL ES 2.0, need to do something else here!
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
#endif
@@ -1594,14 +1782,12 @@ 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;
-static QGLTextureCache *qt_gl_texture_cache = 0;
+
+Q_GLOBAL_STATIC(QGLTextureCache, qt_gl_texture_cache)
QGLTextureCache::QGLTextureCache()
: m_cache(64*1024) // cache ~64 MB worth of textures - this is not accurate though
{
- Q_ASSERT(qt_gl_texture_cache == 0);
- qt_gl_texture_cache = this;
-
QImagePixmapCleanupHooks::instance()->addPixmapDataModificationHook(cleanupTexturesForPixampData);
QImagePixmapCleanupHooks::instance()->addPixmapDataDestructionHook(cleanupBeforePixmapDestruction);
QImagePixmapCleanupHooks::instance()->addImageHook(cleanupTexturesForCacheKey);
@@ -1609,8 +1795,6 @@ QGLTextureCache::QGLTextureCache()
QGLTextureCache::~QGLTextureCache()
{
- qt_gl_texture_cache = 0;
-
QImagePixmapCleanupHooks::instance()->removePixmapDataModificationHook(cleanupTexturesForPixampData);
QImagePixmapCleanupHooks::instance()->removePixmapDataDestructionHook(cleanupBeforePixmapDestruction);
QImagePixmapCleanupHooks::instance()->removeImageHook(cleanupTexturesForCacheKey);
@@ -1618,9 +1802,10 @@ QGLTextureCache::~QGLTextureCache()
void QGLTextureCache::insert(QGLContext* ctx, qint64 key, QGLTexture* texture, int cost)
{
+ QWriteLocker locker(&m_lock);
if (m_cache.totalCost() + cost > m_cache.maxCost()) {
// the cache is full - make an attempt to remove something
- const QList<qint64> keys = m_cache.keys();
+ const QList<QGLTextureCacheKey> keys = m_cache.keys();
int i = 0;
while (i < m_cache.count()
&& (m_cache.totalCost() + cost > m_cache.maxCost())) {
@@ -1630,12 +1815,26 @@ void QGLTextureCache::insert(QGLContext* ctx, qint64 key, QGLTexture* texture, i
++i;
}
}
- m_cache.insert(key, texture, cost);
+ const QGLTextureCacheKey cacheKey = {key, QGLContextPrivate::contextGroup(ctx)};
+ m_cache.insert(cacheKey, texture, cost);
+}
+
+void QGLTextureCache::remove(qint64 key)
+{
+ QWriteLocker locker(&m_lock);
+ QMutexLocker groupLocker(&qt_context_groups()->m_mutex);
+ QList<QGLContextGroup *>::const_iterator it = qt_context_groups()->m_list.constBegin();
+ while (it != qt_context_groups()->m_list.constEnd()) {
+ const QGLTextureCacheKey cacheKey = {key, *it};
+ m_cache.remove(cacheKey);
+ ++it;
+ }
}
bool QGLTextureCache::remove(QGLContext* ctx, GLuint textureId)
{
- QList<qint64> keys = m_cache.keys();
+ QWriteLocker locker(&m_lock);
+ QList<QGLTextureCacheKey> keys = m_cache.keys();
for (int i = 0; i < keys.size(); ++i) {
QGLTexture *tex = m_cache.object(keys.at(i));
if (tex->id == textureId && tex->context == ctx) {
@@ -1649,33 +1848,22 @@ bool QGLTextureCache::remove(QGLContext* ctx, GLuint textureId)
void QGLTextureCache::removeContextTextures(QGLContext* ctx)
{
- QList<qint64> keys = m_cache.keys();
+ QWriteLocker locker(&m_lock);
+ QList<QGLTextureCacheKey> keys = m_cache.keys();
for (int i = 0; i < keys.size(); ++i) {
- const qint64 &key = keys.at(i);
+ const QGLTextureCacheKey &key = keys.at(i);
if (m_cache.object(key)->context == ctx)
m_cache.remove(key);
}
}
-QGLTextureCache* QGLTextureCache::instance()
-{
- if (!qt_gl_texture_cache)
- qt_gl_texture_cache = new QGLTextureCache;
-
- return qt_gl_texture_cache;
-}
-
/*
a hook that removes textures from the cache when a pixmap/image
is deref'ed
*/
void QGLTextureCache::cleanupTexturesForCacheKey(qint64 cacheKey)
{
- // ### remove when the GL texture cache becomes thread-safe
- if (qApp->thread() == QThread::currentThread()) {
- instance()->remove(cacheKey);
- Q_ASSERT(instance()->getTexture(cacheKey) == 0);
- }
+ qt_gl_texture_cache()->remove(cacheKey);
}
@@ -1697,10 +1885,9 @@ void QGLTextureCache::cleanupBeforePixmapDestruction(QPixmapData* pmd)
#endif
}
-void QGLTextureCache::deleteIfEmpty()
+QGLTextureCache *QGLTextureCache::instance()
{
- if (instance()->size() == 0)
- delete instance();
+ return qt_gl_texture_cache();
}
// DDS format structure
@@ -1866,7 +2053,6 @@ QGLContext::~QGLContext()
{
// remove any textures cached in this context
QGLTextureCache::instance()->removeContextTextures(this);
- QGLTextureCache::deleteIfEmpty(); // ### thread safety
d_ptr->group->cleanupResources(this);
@@ -1906,6 +2092,16 @@ void QGLContextPrivate::syncGlState()
}
#undef ctx
+#ifdef QT_NO_EGL
+void QGLContextPrivate::swapRegion(const QRegion *)
+{
+ static bool firstWarning = true;
+ if (firstWarning) {
+ qWarning() << "::swapRegion called but not supported!";
+ firstWarning = false;
+ }
+}
+#endif
/*!
\overload
@@ -2060,6 +2256,13 @@ static void convertToGLFormatHelper(QImage &dst, const QImage &img, GLenum textu
}
}
+#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS)
+QGLExtensionFuncs& QGLContextPrivate::extensionFuncs(const QGLContext *)
+{
+ return qt_extensionFuncs;
+}
+#endif
+
QImage QGLContextPrivate::convertToGLFormat(const QImage &image, bool force_premul,
GLenum texture_format)
{
@@ -2126,8 +2329,8 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
Q_Q(QGLContext);
#ifdef QGL_BIND_TEXTURE_DEBUG
- printf("QGLContextPrivate::bindTexture(), imageSize=(%d,%d), internalFormat =0x%x, options=%x\n",
- image.width(), image.height(), internalFormat, int(options));
+ printf("QGLContextPrivate::bindTexture(), imageSize=(%d,%d), internalFormat =0x%x, options=%x, key=%llx\n",
+ image.width(), image.height(), internalFormat, int(options), key);
QTime time;
time.start();
#endif
@@ -2171,9 +2374,6 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
&& target == GL_TEXTURE_2D
&& (options & QGLContext::MipmapBindOption))
{
-#ifdef QGL_BIND_TEXTURE_DEBUG
- printf(" - generating mipmaps (%d ms)\n", time.elapsed());
-#endif
#if !defined(QT_OPENGL_ES_2)
glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
#ifndef QT_OPENGL_ES
@@ -2187,6 +2387,9 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
#endif
glTexParameterf(target, GL_TEXTURE_MIN_FILTER, options & QGLContext::LinearFilteringBindOption
? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - generating mipmaps (%d ms)\n", time.elapsed());
+#endif
} else {
glTexParameterf(target, GL_TEXTURE_MIN_FILTER, filtering);
}
@@ -2211,7 +2414,7 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
if (premul) {
img = img.convertToFormat(target_format = QImage::Format_ARGB32_Premultiplied);
#ifdef QGL_BIND_TEXTURE_DEBUG
- printf(" - converting ARGB32 -> ARGB32_Premultiplied (%d ms) \n", time.elapsed());
+ printf(" - converted ARGB32 -> ARGB32_Premultiplied (%d ms) \n", time.elapsed());
#endif
}
break;
@@ -2219,7 +2422,7 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
if (!premul) {
img = img.convertToFormat(target_format = QImage::Format_ARGB32);
#ifdef QGL_BIND_TEXTURE_DEBUG
- printf(" - converting ARGB32_Premultiplied -> ARGB32 (%d ms)\n", time.elapsed());
+ printf(" - converted ARGB32_Premultiplied -> ARGB32 (%d ms)\n", time.elapsed());
#endif
}
break;
@@ -2236,20 +2439,17 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
? QImage::Format_ARGB32_Premultiplied
: QImage::Format_ARGB32);
#ifdef QGL_BIND_TEXTURE_DEBUG
- printf(" - converting to 32-bit alpha format (%d ms)\n", time.elapsed());
+ printf(" - converted to 32-bit alpha format (%d ms)\n", time.elapsed());
#endif
} else {
img = img.convertToFormat(QImage::Format_RGB32);
#ifdef QGL_BIND_TEXTURE_DEBUG
- printf(" - converting to 32-bit (%d ms)\n", time.elapsed());
+ printf(" - converted to 32-bit (%d ms)\n", time.elapsed());
#endif
}
}
if (options & QGLContext::InvertedYBindOption) {
-#ifdef QGL_BIND_TEXTURE_DEBUG
- printf(" - flipping bits over y (%d ms)\n", time.elapsed());
-#endif
if (img.isDetached()) {
int ipl = img.bytesPerLine() / 4;
int h = img.height();
@@ -2266,17 +2466,20 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
// data twice. This version should only do it once.
img = img.mirrored();
}
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - flipped bits over y (%d ms)\n", time.elapsed());
+#endif
}
if (externalFormat == GL_RGBA) {
-#ifdef QGL_BIND_TEXTURE_DEBUG
- printf(" - doing byte swapping (%d ms)\n", time.elapsed());
-#endif
// The only case where we end up with a depth different from
// 32 in the switch above is for the RGB16 case, where we set
// the format to GL_RGB
Q_ASSERT(img.depth() == 32);
qgl_byteSwapImage(img, pixel_type);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - did byte swapping (%d ms)\n", time.elapsed());
+#endif
}
#ifdef QT_OPENGL_ES
// OpenGL/ES requires that the internal and external formats be
@@ -2298,14 +2501,14 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
#ifndef QT_NO_DEBUG
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
- qWarning(" - texture upload failed, error code 0x%x\n", error);
+ qWarning(" - texture upload failed, error code 0x%x, enum: %d (%x)\n", error, target, target);
}
#endif
#ifdef QGL_BIND_TEXTURE_DEBUG
static int totalUploadTime = 0;
totalUploadTime += time.elapsed();
- printf(" - upload done in (%d ms) time=%d\n", time.elapsed(), totalUploadTime);
+ printf(" - upload done in %d ms, (accumulated: %d ms)\n", time.elapsed(), totalUploadTime);
#endif
@@ -2320,7 +2523,7 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
QGLTexture *QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum target)
{
Q_Q(QGLContext);
- QGLTexture *texture = QGLTextureCache::instance()->getTexture(key);
+ QGLTexture *texture = QGLTextureCache::instance()->getTexture(q, key);
if (texture && texture->target == target
&& (texture->context == q || QGLContext::areSharing(q, texture->context)))
{
@@ -2335,7 +2538,7 @@ QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target,
{
Q_Q(QGLContext);
QPixmapData *pd = pixmap.pixmapData();
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#if !defined(QT_OPENGL_ES_1)
if (target == GL_TEXTURE_2D && pd->classId() == QPixmapData::OpenGLClass) {
const QGLPixmapData *data = static_cast<const QGLPixmapData *>(pd);
@@ -2360,9 +2563,10 @@ QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target,
// Try to use texture_from_pixmap
const QX11Info *xinfo = qt_x11Info(paintDevice);
if (pd->classId() == QPixmapData::X11Class && pd->pixelType() == QPixmapData::PixmapType
- && xinfo && xinfo->screen() == pixmap.x11Info().screen())
+ && xinfo && xinfo->screen() == pixmap.x11Info().screen()
+ && target == GL_TEXTURE_2D)
{
- texture = bindTextureFromNativePixmap(pd, key, options);
+ texture = bindTextureFromNativePixmap(const_cast<QPixmap*>(&pixmap), key, options);
if (texture) {
texture->options |= QGLContext::MemoryManagedBindOption;
texture->boundPixmap = pd;
@@ -2371,8 +2575,15 @@ QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target,
}
#endif
- if (!texture)
- texture = bindTexture(pixmap.toImage(), target, format, key, options);
+ if (!texture) {
+ QImage image = pixmap.toImage();
+ // If the system depth is 16 and the pixmap doesn't have an alpha channel
+ // then we convert it to RGB16 in the hope that it gets uploaded as a 16
+ // bit texture which is much faster to access than a 32-bit one.
+ if (pixmap.depth() == 16 && !image.hasAlphaChannel() )
+ image = image.convertToFormat(QImage::Format_RGB16);
+ texture = bindTexture(image, target, format, key, options);
+ }
// NOTE: bindTexture(const QImage&, GLenum, GLint, const qint64, bool) should never return null
Q_ASSERT(texture);
@@ -2465,7 +2676,7 @@ GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format,
return 0;
Q_D(QGLContext);
- QGLTexture *texture = d->bindTexture(image, target, format, false, options);
+ QGLTexture *texture = d->bindTexture(image, target, format, options);
return texture->id;
}
@@ -2477,7 +2688,7 @@ GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMa
return 0;
Q_D(QGLContext);
- QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), false, DefaultBindOption);
+ QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), DefaultBindOption);
return texture->id;
}
@@ -2489,7 +2700,7 @@ GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMa
return 0;
Q_D(QGLContext);
- QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), false, options);
+ QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), options);
return texture->id;
}
#endif
@@ -2587,41 +2798,41 @@ void QGLContext::deleteTexture(QMacCompatGLuint id)
}
#endif
-void qt_add_rect_to_array(const QRectF &r, q_vertexType *array)
+void qt_add_rect_to_array(const QRectF &r, GLfloat *array)
{
qreal left = r.left();
qreal right = r.right();
qreal top = r.top();
qreal bottom = r.bottom();
- array[0] = 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);
+ array[0] = left;
+ array[1] = top;
+ array[2] = right;
+ array[3] = top;
+ array[4] = right;
+ array[5] = bottom;
+ array[6] = left;
+ array[7] = bottom;
}
-void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array)
+void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, GLfloat *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);
+ array[0] = x1;
+ array[1] = y1;
+ array[2] = x2;
+ array[3] = y1;
+ array[4] = x2;
+ array[5] = y2;
+ array[6] = x1;
+ array[7] = y2;
}
#if !defined(QT_OPENGL_ES_2)
static void qDrawTextureRect(const QRectF &target, GLint textureWidth, GLint textureHeight, GLenum textureTarget)
{
- q_vertexType tx = f2vt(1);
- q_vertexType ty = f2vt(1);
+ GLfloat tx = 1.0f;
+ GLfloat ty = 1.0f;
#ifdef QT_OPENGL_ES
Q_UNUSED(textureWidth);
@@ -2634,20 +2845,20 @@ static void qDrawTextureRect(const QRectF &target, GLint textureWidth, GLint tex
glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_HEIGHT, &textureHeight);
}
- tx = f2vt(textureWidth);
- ty = f2vt(textureHeight);
+ tx = GLfloat(textureWidth);
+ ty = GLfloat(textureHeight);
}
#endif
- q_vertexType texCoordArray[4*2] = {
+ GLfloat texCoordArray[4*2] = {
0, ty, tx, ty, tx, 0, 0, 0
};
- q_vertexType vertexArray[4*2];
+ GLfloat vertexArray[4*2];
qt_add_rect_to_array(target, vertexArray);
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
- glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -2662,14 +2873,38 @@ static void qDrawTextureRect(const QRectF &target, GLint textureWidth, GLint tex
/*!
\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.
+ This function supports the following use cases:
+
+ \list
+ \i On OpenGL and OpenGL ES 1.x it draws the given texture, \a textureId,
+ to the given target rectangle, \a target, in OpenGL model space. The
+ \a textureTarget should be a 2D texture target.
+ \i On OpenGL and OpenGL ES 2.x, if a painter is active, not inside a
+ beginNativePainting / endNativePainting block, and uses the
+ engine with type QPaintEngine::OpenGL2, the function will draw the given
+ texture, \a textureId, to the given target rectangle, \a target,
+ respecting the current painter state. This will let you draw a texture
+ with the clip, transform, render hints, and composition mode set by the
+ painter. Note that the texture target needs to be GL_TEXTURE_2D for this
+ use case, and that this is the only supported use case under OpenGL ES 2.x.
+ \endlist
- \note This function is not supported under OpenGL/ES 2.0.
*/
void QGLContext::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
{
+#if !defined(QT_OPENGL_ES) || defined(QT_OPENGL_ES_2)
+ if (d_ptr->active_engine &&
+ d_ptr->active_engine->type() == QPaintEngine::OpenGL2) {
+ QGL2PaintEngineEx *eng = static_cast<QGL2PaintEngineEx*>(d_ptr->active_engine);
+ if (!eng->isNativePaintingActive()) {
+ QRectF src(0, 0, target.width(), target.height());
+ QSize size(target.width(), target.height());
+ if (eng->drawTexture(target, textureId, size, src))
+ return;
+ }
+ }
+#endif
+
#ifndef QT_OPENGL_ES_2
#ifdef QT_OPENGL_ES
if (textureTarget != GL_TEXTURE_2D) {
@@ -2698,7 +2933,7 @@ void QGLContext::drawTexture(const QRectF &target, GLuint textureId, GLenum text
Q_UNUSED(target);
Q_UNUSED(textureId);
Q_UNUSED(textureTarget);
- qWarning("drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget) not supported with OpenGL ES/2.0");
+ qWarning("drawTexture() with OpenGL ES 2.0 requires an active OpenGL2 paint engine");
#endif
}
@@ -2713,20 +2948,33 @@ void QGLContext::drawTexture(const QRectF &target, QMacCompatGLuint textureId, Q
/*!
\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.
+ This function supports the following use cases:
+
+ \list
+ \i By default it draws the given texture, \a textureId,
+ at the given \a point in OpenGL model space. The
+ \a textureTarget should be a 2D texture target.
+ \i If a painter is active, not inside a
+ beginNativePainting / endNativePainting block, and uses the
+ engine with type QPaintEngine::OpenGL2, the function will draw the given
+ texture, \a textureId, at the given \a point,
+ respecting the current painter state. This will let you draw a texture
+ with the clip, transform, render hints, and composition mode set by the
+ painter. Note that the texture target needs to be GL_TEXTURE_2D for this
+ use case.
+ \endlist
- \note This function is not supported under OpenGL/ES.
+ \note This function is not supported under any version of OpenGL ES.
*/
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);
@@ -2740,6 +2988,18 @@ void QGLContext::drawTexture(const QPointF &point, GLuint textureId, GLenum text
glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_WIDTH, &textureWidth);
glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_HEIGHT, &textureHeight);
+ if (d_ptr->active_engine &&
+ d_ptr->active_engine->type() == QPaintEngine::OpenGL2) {
+ QGL2PaintEngineEx *eng = static_cast<QGL2PaintEngineEx*>(d_ptr->active_engine);
+ if (!eng->isNativePaintingActive()) {
+ QRectF dest(point, QSizeF(textureWidth, textureHeight));
+ QRectF src(0, 0, textureWidth, textureHeight);
+ QSize size(textureWidth, textureHeight);
+ if (eng->drawTexture(dest, textureId, size, src))
+ return;
+ }
+ }
+
qDrawTextureRect(QRectF(point, QSizeF(textureWidth, textureHeight)), textureWidth, textureHeight, textureTarget);
if (!wasEnabled)
@@ -3200,6 +3460,7 @@ void QGLContextPrivate::setCurrentContext(QGLContext *context)
*/
+
/*****************************************************************************
QGLWidget implementation
*****************************************************************************/
@@ -3320,6 +3581,15 @@ void QGLContextPrivate::setCurrentContext(QGLContext *context)
One approach to doing this is shown in the
\l{Overpainting Example}{Overpainting} example.
+ \section1 Threading
+
+ It is possible to render into a QGLWidget from another thread, but it
+ requires that all access to the GL context is safe guarded. The Qt GUI
+ thread will try to use the context in resizeEvent and paintEvent, so in
+ order for threaded rendering using a GL widget to work, these functions
+ need to be intercepted in the GUI thread and handled accordingly in the
+ application.
+
\e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other
countries.}
@@ -3447,8 +3717,10 @@ QGLWidget::~QGLWidget()
bool doRelease = (glcx && glcx->windowCreated());
#endif
delete d->glcx;
+ d->glcx = 0;
#if defined(Q_WS_WIN)
delete d->olcx;
+ d->olcx = 0;
#endif
#if defined(GLX_MESA_release_buffers) && defined(QGL_USE_MESA_EXT)
if (doRelease)
@@ -3850,7 +4122,7 @@ bool QGLWidget::event(QEvent *e)
}
}
-#if defined(QT_OPENGL_ES)
+#ifndef QT_NO_EGL
// A re-parent is likely to destroy the X11 window and re-create it. It is important
// that we free the EGL surface _before_ the winID changes - otherwise we can leak.
if (e->type() == QEvent::ParentAboutToChange)
@@ -3859,7 +4131,7 @@ bool QGLWidget::event(QEvent *e)
if ((e->type() == QEvent::ParentChange) || (e->type() == QEvent::WindowStateChange)) {
// The window may have been re-created during re-parent or state change - if so, the EGL
// surface will need to be re-created.
- d->recreateEglSurface(false);
+ d->recreateEglSurface();
}
#endif
#elif defined(Q_WS_WIN)
@@ -4765,11 +5037,8 @@ void QGLWidget::deleteTexture(QMacCompatGLuint id)
/*!
\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().
+ Calls the corresponding QGLContext::drawTexture() on
+ this widget's context.
*/
void QGLWidget::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
{
@@ -4789,10 +5058,8 @@ void QGLWidget::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QM
/*!
\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().
+ Calls the corresponding QGLContext::drawTexture() on
+ this widget's context.
*/
void QGLWidget::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
{
@@ -4809,7 +5076,7 @@ void QGLWidget::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QM
}
#endif
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#ifndef QT_OPENGL_ES_1
Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_2_engine)
#endif
@@ -4819,7 +5086,7 @@ Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_engine)
Q_OPENGL_EXPORT QPaintEngine* qt_qgl_paint_engine()
{
-#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL)
+#if defined(QT_OPENGL_ES_1)
return qt_gl_engine();
#elif defined(QT_OPENGL_ES_2)
return qt_gl_2_engine();
@@ -4937,12 +5204,14 @@ QGLExtensions::Extensions QGLExtensions::currentContextExtensions()
glExtensions |= NVFloatBuffer;
if (extensions.match("GL_ARB_pixel_buffer_object"))
glExtensions |= PixelBufferObject;
+ if (extensions.match("GL_IMG_texture_format_BGRA8888"))
+ glExtensions |= BGRATextureFormat;
#if defined(QT_OPENGL_ES_2)
glExtensions |= FramebufferObject;
glExtensions |= GenerateMipmap;
glExtensions |= FragmentShader;
#endif
-#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL)
+#if defined(QT_OPENGL_ES_1)
if (extensions.match("GL_OES_framebuffer_object"))
glExtensions |= FramebufferObject;
#endif
@@ -4968,6 +5237,20 @@ QGLExtensions::Extensions QGLExtensions::currentContextExtensions()
return glExtensions;
}
+
+class QGLDefaultExtensions
+{
+public:
+ QGLDefaultExtensions() {
+ QGLTemporaryContext tempContext;
+ extensions = QGLExtensions::currentContextExtensions();
+ }
+
+ QGLExtensions::Extensions extensions;
+};
+
+Q_GLOBAL_STATIC(QGLDefaultExtensions, qtDefaultExtensions)
+
/*
Returns the GL extensions for the current QGLContext. If there is no
current QGLContext, a default context will be created and the extensions
@@ -4975,34 +5258,19 @@ QGLExtensions::Extensions QGLExtensions::currentContextExtensions()
*/
QGLExtensions::Extensions QGLExtensions::glExtensions()
{
- QGLTemporaryContext *tmpContext = 0;
- static bool cachedDefault = false;
- static Extensions defaultExtensions = 0;
+ Extensions extensionFlags = 0;
QGLContext *currentCtx = const_cast<QGLContext *>(QGLContext::currentContext());
if (currentCtx && currentCtx->d_func()->extension_flags_cached)
return currentCtx->d_func()->extension_flags;
if (!currentCtx) {
- if (cachedDefault) {
- return defaultExtensions;
- } else {
- tmpContext = new QGLTemporaryContext;
- cachedDefault = true;
- }
- }
-
- Extensions extensionFlags = currentContextExtensions();
- if (currentCtx) {
+ extensionFlags = qtDefaultExtensions()->extensions;
+ } else {
+ extensionFlags = currentContextExtensions();
currentCtx->d_func()->extension_flags_cached = true;
currentCtx->d_func()->extension_flags = extensionFlags;
- } else {
- defaultExtensions = extensionFlags;
}
-
- if (tmpContext)
- delete tmpContext;
-
return extensionFlags;
}
@@ -5037,11 +5305,17 @@ Q_OPENGL_EXPORT void qt_set_gl_library_name(const QString& 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 QLatin1String("GL");
-#else // Q_WS_MAC
+#ifdef Q_WS_MAC
return QLatin1String("/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib");
-#endif
+#else
+# if defined(QT_OPENGL_ES_1)
+ return QLatin1String("GLES_CM");
+# elif defined(QT_OPENGL_ES_2)
+ return QLatin1String("GLESv2");
+# else
+ return QLatin1String("GL");
+# endif
+#endif // defined Q_WS_MAC
}
return *qt_gl_lib_name();
}
diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h
index 1a04ff9..f85cad5 100644
--- a/src/opengl/qgl.h
+++ b/src/opengl/qgl.h
@@ -57,7 +57,7 @@ QT_BEGIN_HEADER
#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)
+#elif defined(QT_OPENGL_ES_1)
# include <GLES/gl.h>
#ifndef GL_DOUBLE
# define GL_DOUBLE GL_FLOAT
@@ -144,6 +144,7 @@ namespace QGL
DirectRendering = 0x0080,
HasOverlay = 0x0100,
SampleBuffers = 0x0200,
+ DeprecatedFunctions = 0x0400,
SingleBuffer = DoubleBuffer << 16,
NoDepthBuffer = DepthBuffer << 16,
ColorIndex = Rgba << 16,
@@ -153,7 +154,8 @@ namespace QGL
NoStereoBuffers = StereoBuffers << 16,
IndirectRendering = DirectRendering << 16,
NoOverlay = HasOverlay << 16,
- NoSampleBuffers = SampleBuffers << 16
+ NoSampleBuffers = SampleBuffers << 16,
+ NoDeprecatedFunctions = DeprecatedFunctions << 16
};
Q_DECLARE_FLAGS(FormatOptions, FormatOption)
}
@@ -235,7 +237,20 @@ public:
static bool hasOpenGL();
static bool hasOpenGLOverlays();
- enum OpenGLVersionFlag {
+ void setVersion(int major, int minor);
+ int majorVersion() const;
+ int minorVersion() const;
+
+ enum OpenGLContextProfile {
+ NoProfile,
+ CoreProfile,
+ CompatibilityProfile
+ };
+
+ void setProfile(OpenGLContextProfile profile);
+ OpenGLContextProfile profile() const;
+
+ enum OpenGLVersionFlag {
OpenGL_Version_None = 0x00000000,
OpenGL_Version_1_1 = 0x00000001,
OpenGL_Version_1_2 = 0x00000002,
@@ -249,7 +264,11 @@ public:
OpenGL_ES_Common_Version_1_1 = 0x00000200,
OpenGL_ES_CommonLite_Version_1_1 = 0x00000400,
OpenGL_ES_Version_2_0 = 0x00000800,
- OpenGL_Version_3_0 = 0x00001000
+ OpenGL_Version_3_0 = 0x00001000,
+ OpenGL_Version_3_1 = 0x00002000,
+ OpenGL_Version_3_2 = 0x00004000,
+ OpenGL_Version_3_3 = 0x00008000,
+ OpenGL_Version_4_0 = 0x00010000
};
Q_DECLARE_FLAGS(OpenGLVersionFlags, OpenGLVersionFlag)
@@ -262,6 +281,9 @@ private:
friend Q_OPENGL_EXPORT bool operator==(const QGLFormat&, const QGLFormat&);
friend Q_OPENGL_EXPORT bool operator!=(const QGLFormat&, const QGLFormat&);
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_OPENGL_EXPORT QDebug operator<<(QDebug, const QGLFormat &);
+#endif
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QGLFormat::OpenGLVersionFlags)
@@ -269,6 +291,10 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QGLFormat::OpenGLVersionFlags)
Q_OPENGL_EXPORT bool operator==(const QGLFormat&, const QGLFormat&);
Q_OPENGL_EXPORT bool operator!=(const QGLFormat&, const QGLFormat&);
+#ifndef QT_NO_DEBUG_STREAM
+Q_OPENGL_EXPORT QDebug operator<<(QDebug, const QGLFormat &);
+#endif
+
class Q_OPENGL_EXPORT QGLContext
{
Q_DECLARE_PRIVATE(QGLContext)
@@ -359,7 +385,7 @@ protected:
#if defined(Q_WS_WIN)
virtual int choosePixelFormat(void* pfd, HDC pdc);
#endif
-#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
+#if defined(Q_WS_X11) && defined(QT_NO_EGL)
virtual void* tryVisual(const QGLFormat& f, int bufDepth = 1);
virtual void* chooseVisual();
#endif
@@ -414,7 +440,9 @@ private:
friend class QGLFramebufferObjectPrivate;
friend class QGLFBOGLPaintDevice;
friend class QGLPaintDevice;
+ friend class QGLWidgetGLPaintDevice;
friend class QX11GLPixmapData;
+ friend class QX11GLSharedContexts;
private:
Q_DISABLE_COPY(QGLContext)
};
diff --git a/src/opengl/qgl_cl_p.h b/src/opengl/qgl_cl_p.h
deleted file mode 100644
index 82b492b..0000000
--- a/src/opengl/qgl_cl_p.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the QtOpenGL module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
-**
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-#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, FLOAT2X(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
index 3addea1..3763926 100644
--- a/src/opengl/qgl_egl.cpp
+++ b/src/opengl/qgl_egl.cpp
@@ -40,93 +40,114 @@
****************************************************************************/
#include <QtOpenGL/qgl.h>
+#include <QtOpenGL/qglpixelbuffer.h>
#include "qgl_p.h"
#include "qgl_egl_p.h"
+#include "qglpixelbuffer_p.h"
+
+#ifdef Q_WS_X11
+#include <QtGui/private/qpixmap_x11_p.h>
+#endif
QT_BEGIN_NAMESPACE
-// 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) == 0 || f.redBufferSize() != -1)
- props.setValue(EGL_RED_SIZE, f.redBufferSize() == -1 ? 1 : f.redBufferSize());
- if (props.value(EGL_GREEN_SIZE) == 0 || f.greenBufferSize() != -1)
- props.setValue(EGL_GREEN_SIZE, f.greenBufferSize() == -1 ? 1 : f.greenBufferSize());
- if (props.value(EGL_BLUE_SIZE) == 0 || f.blueBufferSize() != -1)
- props.setValue(EGL_BLUE_SIZE, f.blueBufferSize() == -1 ? 1 : f.blueBufferSize());
- if (f.alpha()) {
- if (props.value(EGL_ALPHA_SIZE) == 0 || f.alphaBufferSize() != -1)
- props.setValue(EGL_ALPHA_SIZE, f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize());
- }
+void qt_eglproperties_set_glformat(QEglProperties& eglProperties, const QGLFormat& glFormat)
+{
+ int redSize = glFormat.redBufferSize();
+ int greenSize = glFormat.greenBufferSize();
+ int blueSize = glFormat.blueBufferSize();
+ int alphaSize = glFormat.alphaBufferSize();
+ int depthSize = glFormat.depthBufferSize();
+ int stencilSize = glFormat.stencilBufferSize();
+ int sampleCount = glFormat.samples();
- 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() == -1 ? 1 : f.samples());
- } else {
- props.setValue(EGL_SAMPLE_BUFFERS, 0);
- }
- if (deviceType == QInternal::Widget)
- props.setValue(EGL_LEVEL, f.plane());
-}
+ // QGLFormat uses a magic value of -1 to indicate "don't care", even when a buffer of that
+ // type has been requested. So we must check QGLFormat's booleans too if size is -1:
+ if (glFormat.alpha() && alphaSize <= 0)
+ alphaSize = 1;
+ if (glFormat.depth() && depthSize <= 0)
+ depthSize = 1;
+ if (glFormat.stencil() && stencilSize <= 0)
+ stencilSize = 1;
+ if (glFormat.sampleBuffers() && sampleCount <= 0)
+ sampleCount = 1;
-// 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);
- }
+ // We want to make sure 16-bit configs are chosen over 32-bit configs as they will provide
+ // the best performance. The EGL config selection algorithm is a bit stange in this regard:
+ // The selection criteria for EGL_BUFFER_SIZE is "AtLeast", so we can't use it to discard
+ // 32-bit configs completely from the selection. So it then comes to the sorting algorithm.
+ // The red/green/blue sizes have a sort priority of 3, so they are sorted by first. The sort
+ // order is special and described as "by larger _total_ number of color bits.". So EGL will
+ // put 32-bit configs in the list before the 16-bit configs. However, the spec also goes on
+ // to say "If the requested number of bits in attrib_list for a particular component is 0,
+ // then the number of bits for that component is not considered". This part of the spec also
+ // seems to imply that setting the red/green/blue bits to zero means none of the components
+ // are considered and EGL disregards the entire sorting rule. It then looks to the next
+ // highest priority rule, which is EGL_BUFFER_SIZE. Despite the selection criteria being
+ // "AtLeast" for EGL_BUFFER_SIZE, it's sort order is "smaller" meaning 16-bit configs are
+ // put in the list before 32-bit configs. So, to make sure 16-bit is preffered over 32-bit,
+ // we must set the red/green/blue sizes to zero. This has an unfortunate consequence that
+ // if the application sets the red/green/blue size to 5/6/5 on the QGLFormat, they will
+ // probably get a 32-bit config, even when there's an RGB565 config avaliable. Oh well.
- if (context.configAttrib(EGL_DEPTH_SIZE, &value)) {
- format.setDepth(value != 0);
- if (format.depth())
- format.setDepthBufferSize(value);
- }
+ // Now normalize the values so -1 becomes 0
+ redSize = redSize > 0 ? redSize : 0;
+ greenSize = greenSize > 0 ? greenSize : 0;
+ blueSize = blueSize > 0 ? blueSize : 0;
+ alphaSize = alphaSize > 0 ? alphaSize : 0;
+ depthSize = depthSize > 0 ? depthSize : 0;
+ stencilSize = stencilSize > 0 ? stencilSize : 0;
+ sampleCount = sampleCount > 0 ? sampleCount : 0;
- if (context.configAttrib(EGL_LEVEL, &value))
- format.setPlane(value);
+ eglProperties.setValue(EGL_RED_SIZE, redSize);
+ eglProperties.setValue(EGL_GREEN_SIZE, greenSize);
+ eglProperties.setValue(EGL_BLUE_SIZE, blueSize);
+ eglProperties.setValue(EGL_ALPHA_SIZE, alphaSize);
+ eglProperties.setValue(EGL_DEPTH_SIZE, depthSize);
+ eglProperties.setValue(EGL_STENCIL_SIZE, stencilSize);
+ eglProperties.setValue(EGL_SAMPLES, sampleCount);
+ eglProperties.setValue(EGL_SAMPLE_BUFFERS, sampleCount ? 1 : 0);
+}
- if (context.configAttrib(EGL_SAMPLE_BUFFERS, &value)) {
- format.setSampleBuffers(value != 0);
- if (format.sampleBuffers()) {
- context.configAttrib(EGL_SAMPLES, &value);
- format.setSamples(value);
- }
- }
+// Updates "format" with the parameters of the selected configuration.
+void qt_glformat_from_eglconfig(QGLFormat& format, const EGLConfig config)
+{
+ EGLint redSize = 0;
+ EGLint greenSize = 0;
+ EGLint blueSize = 0;
+ EGLint alphaSize = 0;
+ EGLint depthSize = 0;
+ EGLint stencilSize = 0;
+ EGLint sampleCount = 0;
+ EGLint level = 0;
- if (context.configAttrib(EGL_STENCIL_SIZE, &value)) {
- format.setStencil(value != 0);
- if (format.stencil())
- format.setStencilBufferSize(value);
- }
+ EGLDisplay display = QEgl::display();
+ eglGetConfigAttrib(display, config, EGL_RED_SIZE, &redSize);
+ eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &greenSize);
+ eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blueSize);
+ eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &alphaSize);
+ eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE, &depthSize);
+ eglGetConfigAttrib(display, config, EGL_STENCIL_SIZE, &stencilSize);
+ eglGetConfigAttrib(display, config, EGL_SAMPLES, &sampleCount);
+ eglGetConfigAttrib(display, config, EGL_LEVEL, &level);
+
+ format.setRedBufferSize(redSize);
+ format.setGreenBufferSize(greenSize);
+ format.setBlueBufferSize(blueSize);
+ format.setAlphaBufferSize(alphaSize);
+ format.setDepthBufferSize(depthSize);
+ format.setStencilBufferSize(stencilSize);
+ format.setSamples(sampleCount);
+ format.setPlane(level);
+ format.setDirectRendering(true); // All EGL contexts are direct-rendered
+ format.setRgba(true); // EGL doesn't support colour index rendering
+ format.setStereo(false); // EGL doesn't support stereo buffers
+ format.setAccumBufferSize(0); // EGL doesn't support accululation buffers
// 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();
+ eglGetError();
}
bool QGLFormat::hasOpenGL()
@@ -141,10 +162,11 @@ void QGLContext::reset()
return;
d->cleanup();
doneCurrent();
- if (d->eglContext) {
+ if (d->eglContext && d->ownsEglContext) {
d->destroyEglSurfaceForDevice();
delete d->eglContext;
}
+ d->ownsEglContext = false;
d->eglContext = 0;
d->eglSurface = EGL_NO_SURFACE;
d->crWin = false;
@@ -158,13 +180,32 @@ void QGLContext::reset()
void QGLContext::makeCurrent()
{
Q_D(QGLContext);
- if (!d->valid || !d->eglContext || d->eglSurface == EGL_NO_SURFACE) {
+ if (!d->valid || !d->eglContext || d->eglSurfaceForDevice() == EGL_NO_SURFACE) {
qWarning("QGLContext::makeCurrent(): Cannot make invalid context current");
return;
}
- if (d->eglContext->makeCurrent(d->eglSurface))
+ if (d->eglContext->makeCurrent(d->eglSurfaceForDevice())) {
QGLContextPrivate::setCurrentContext(this);
+ if (!d->workaroundsCached) {
+ d->workaroundsCached = true;
+ const char *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
+ if (renderer && (strstr(renderer, "SGX") || strstr(renderer, "MBX"))) {
+ // PowerVR MBX/SGX chips needs to clear all buffers when starting to render
+ // a new frame, otherwise there will be a performance penalty to pay for
+ // each frame.
+ d->workaround_needsFullClearOnEveryFrame = true;
+
+ // Older PowerVR SGX drivers (like the one in the N900) have a
+ // bug which prevents glCopyTexSubImage2D() to work with a POT
+ // or GL_ALPHA texture bound to an FBO. The only way to
+ // identify that driver is to check the EGL version number for it.
+ const char *egl_version = eglQueryString(d->eglContext->display(), EGL_VERSION);
+ if (egl_version && strstr(egl_version, "1.3"))
+ d->workaround_brokenFBOReadBack = true;
+ }
+ }
+ }
}
void QGLContext::doneCurrent()
@@ -183,7 +224,7 @@ void QGLContext::swapBuffers() const
if (!d->valid || !d->eglContext)
return;
- d->eglContext->swapBuffers(d->eglSurface);
+ d->eglContext->swapBuffers(d->eglSurfaceForDevice());
}
void QGLContextPrivate::destroyEglSurfaceForDevice()
@@ -191,21 +232,56 @@ void QGLContextPrivate::destroyEglSurfaceForDevice()
if (eglSurface != EGL_NO_SURFACE) {
#ifdef Q_WS_X11
// Make sure we don't call eglDestroySurface on a surface which
- // was created for a different winId:
- if (paintDevice->devType() == QInternal::Widget) {
- QGLWidget* w = static_cast<QGLWidget*>(paintDevice);
-
- if (w->d_func()->eglSurfaceWindowId == w->winId())
- eglDestroySurface(eglContext->display(), eglSurface);
- else
- qWarning("WARNING: Potential EGL surface leak!");
- } else
+ // was created for a different winId. This applies only to QGLWidget
+ // paint device, so make sure this is the one we're operating on
+ // (as opposed to a QGLWindowSurface use case).
+ if (paintDevice && paintDevice->devType() == QInternal::Widget) {
+ QWidget *w = static_cast<QWidget *>(paintDevice);
+ if (QGLWidget *wgl = qobject_cast<QGLWidget *>(w)) {
+ if (wgl->d_func()->eglSurfaceWindowId != wgl->winId()) {
+ qWarning("WARNING: Potential EGL surface leak! Not destroying surface.");
+ return;
+ }
+ }
+ }
#endif
- eglDestroySurface(eglContext->display(), eglSurface);
+ eglDestroySurface(eglContext->display(), eglSurface);
eglSurface = EGL_NO_SURFACE;
}
}
+EGLSurface QGLContextPrivate::eglSurfaceForDevice() const
+{
+ // If a QPixmapData had to create the QGLContext, we don't have a paintDevice
+ if (!paintDevice)
+ return eglSurface;
+
+#ifdef Q_WS_X11
+ if (paintDevice->devType() == QInternal::Pixmap) {
+ QPixmapData *pmd = static_cast<QPixmap*>(paintDevice)->data_ptr().data();
+ if (pmd->classId() == QPixmapData::X11Class) {
+ QX11PixmapData* x11PixmapData = static_cast<QX11PixmapData*>(pmd);
+ return (EGLSurface)x11PixmapData->gl_surface;
+ }
+ }
+#endif
+
+ if (paintDevice->devType() == QInternal::Pbuffer) {
+ QGLPixelBuffer* pbuf = static_cast<QGLPixelBuffer*>(paintDevice);
+ return pbuf->d_func()->pbuf;
+ }
+
+ return eglSurface;
+}
+
+void QGLContextPrivate::swapRegion(const QRegion *region)
+{
+ if (!valid || !eglContext)
+ return;
+
+ eglContext->swapBuffersRegion2NOK(eglSurfaceForDevice(), region);
+}
+
void QGLWidget::setMouseTracking(bool enable)
{
QWidget::setMouseTracking(enable);
diff --git a/src/opengl/qgl_egl_p.h b/src/opengl/qgl_egl_p.h
index c503724..85d7f32 100644
--- a/src/opengl/qgl_egl_p.h
+++ b/src/opengl/qgl_egl_p.h
@@ -54,14 +54,15 @@
//
#include <QtGui/private/qegl_p.h>
+#include <QtGui/private/qeglcontext_p.h>
+#include <QtGui/private/qeglproperties_p.h>
QT_BEGIN_NAMESPACE
class QGLFormat;
-void qt_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);
+void qt_eglproperties_set_glformat(QEglProperties& props, const QGLFormat& format);
+void qt_glformat_from_eglconfig(QGLFormat& format, const EGLConfig config);
QT_END_NAMESPACE
diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h
index 4facb65..32feacd 100644
--- a/src/opengl/qgl_p.h
+++ b/src/opengl/qgl_p.h
@@ -64,36 +64,8 @@
#include "qcache.h"
#include "qglpaintdevice_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
-
-#if defined(QT_OPENGL_ES) || defined(QT_OPENGL_ES_2)
-QT_BEGIN_INCLUDE_NAMESPACE
-
-#if defined(QT_OPENGL_ES_2)
-# include <GLES2/gl2.h>
-#endif
-
-#if defined(QT_GLES_EGL)
-# include <GLES/egl.h>
-#else
-# include <EGL/egl.h>
-#endif
-
-QT_END_INCLUDE_NAMESPACE
+#ifndef QT_NO_EGL
+#include <QtGui/private/qegl_p.h>
#endif
QT_BEGIN_NAMESPACE
@@ -124,7 +96,7 @@ class QMacWindowChangeEvent;
class QWSGLWindowSurface;
#endif
-#if defined(QT_OPENGL_ES)
+#ifndef QT_NO_EGL
class QEglContext;
#endif
@@ -138,11 +110,15 @@ public:
QGLFormatPrivate()
: ref(1)
{
- opts = QGL::DoubleBuffer | QGL::DepthBuffer | QGL::Rgba | QGL::DirectRendering | QGL::StencilBuffer;
+ opts = QGL::DoubleBuffer | QGL::DepthBuffer | QGL::Rgba | QGL::DirectRendering
+ | QGL::StencilBuffer | QGL::DeprecatedFunctions;
pln = 0;
depthSize = accumSize = stencilSize = redSize = greenSize = blueSize = alphaSize = -1;
numSamples = -1;
swapInterval = -1;
+ majorVersion = 1;
+ minorVersion = 0;
+ profile = QGLFormat::NoProfile;
}
QGLFormatPrivate(const QGLFormatPrivate *other)
: ref(1),
@@ -156,7 +132,10 @@ public:
blueSize(other->blueSize),
alphaSize(other->alphaSize),
numSamples(other->numSamples),
- swapInterval(other->swapInterval)
+ swapInterval(other->swapInterval),
+ majorVersion(other->majorVersion),
+ minorVersion(other->minorVersion),
+ profile(other->profile)
{
}
QAtomicInt ref;
@@ -171,6 +150,9 @@ public:
int alphaSize;
int numSamples;
int swapInterval;
+ int majorVersion;
+ int minorVersion;
+ QGLFormat::OpenGLContextProfile profile;
};
class QGLWidgetPrivate : public QWidgetPrivate
@@ -182,7 +164,7 @@ public:
#ifdef Q_WS_QWS
, wsurf(0)
#endif
-#if defined(Q_WS_X11) && defined(QT_OPENGL_ES)
+#if defined(Q_WS_X11) && !defined(QT_NO_EGL)
, eglSurfaceWindowId(0)
#endif
{
@@ -195,6 +177,10 @@ public:
void initContext(QGLContext *context, const QGLWidget* shareWidget);
bool renderCxPm(QPixmap *pixmap);
void cleanupColormaps();
+ void aboutToDestroy() {
+ if (glcx)
+ glcx->reset();
+ }
QGLContext *glcx;
QGLWidgetGLPaintDevice glDevice;
@@ -212,8 +198,8 @@ public:
QGLContext *olcx;
#elif defined(Q_WS_X11)
QGLOverlayWidget *olw;
-#if defined(QT_OPENGL_ES)
- void recreateEglSurface(bool force);
+#ifndef QT_NO_EGL
+ void recreateEglSurface();
WId eglSurfaceWindowId;
#endif
#elif defined(Q_WS_MAC)
@@ -248,7 +234,7 @@ public:
static void addShare(const QGLContext *context, const QGLContext *share);
static void removeShare(const QGLContext *context);
private:
- QGLContextGroup(const QGLContext *context) : m_context(context), m_guards(0), m_refs(1) { }
+ QGLContextGroup(const QGLContext *context);
QGLExtensionFuncs m_extensionFuncs;
const QGLContext *m_context; // context group's representative
@@ -296,8 +282,6 @@ public:
Q_DECLARE_FLAGS(Extensions, Extension)
static Extensions glExtensions();
-
-private:
static Extensions currentContextExtensions();
};
@@ -350,6 +334,11 @@ public:
void setVertexAttribArrayEnabled(int arrayIndex, bool enabled = true);
void syncGlState(); // Makes sure the GL context's state is what we think it is
+ void swapRegion(const QRegion *region);
+
+#if defined(Q_WS_WIN)
+ void updateFormatVersion();
+#endif
#if defined(Q_WS_WIN)
HGLRC rc;
@@ -360,10 +349,12 @@ public:
HBITMAP hbitmap;
HDC hbitmap_hdc;
#endif
-#if defined(QT_OPENGL_ES)
+#ifndef QT_NO_EGL
+ uint ownsEglContext : 1;
QEglContext *eglContext;
EGLSurface eglSurface;
void destroyEglSurfaceForDevice();
+ EGLSurface eglSurfaceForDevice() const;
#elif defined(Q_WS_X11) || defined(Q_WS_MAC)
void* cx;
#endif
@@ -375,7 +366,7 @@ public:
quint32 gpm;
int screen;
QHash<QPixmapData*, QPixmap> boundPixmaps;
- QGLTexture *bindTextureFromNativePixmap(QPixmapData*, const qint64 key,
+ QGLTexture *bindTextureFromNativePixmap(QPixmap*, const qint64 key,
QGLContext::BindOptions options);
static void destroyGlSurfaceForPixmap(QPixmapData*);
static void unbindPixmapFromTexture(QPixmapData*);
@@ -396,6 +387,12 @@ public:
uint internal_context : 1;
uint version_flags_cached : 1;
uint extension_flags_cached : 1;
+
+ // workarounds for driver/hw bugs on different platforms
+ uint workaround_needsFullClearOnEveryFrame : 1;
+ uint workaround_brokenFBOReadBack : 1;
+ uint workaroundsCached : 1;
+
QPaintDevice *paintDevice;
QColor transpColor;
QGLContext *q_ptr;
@@ -419,7 +416,7 @@ public:
#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS)
static QGLExtensionFuncs qt_extensionFuncs;
- static inline QGLExtensionFuncs& extensionFuncs(const QGLContext *) { return qt_extensionFuncs; }
+ static Q_OPENGL_EXPORT QGLExtensionFuncs& extensionFuncs(const QGLContext *);
#endif
static void setCurrentContext(QGLContext *context);
@@ -530,30 +527,69 @@ public:
QSize bindCompressedTexturePVR(const char *buf, int len);
};
+struct QGLTextureCacheKey {
+ qint64 key;
+ QGLContextGroup *group;
+};
+
+inline bool operator==(const QGLTextureCacheKey &a, const QGLTextureCacheKey &b)
+{
+ return a.key == b.key && a.group == b.group;
+}
+
+inline uint qHash(const QGLTextureCacheKey &key)
+{
+ return qHash(key.key) ^ qHash(key.group);
+}
+
+
class Q_AUTOTEST_EXPORT QGLTextureCache {
public:
QGLTextureCache();
~QGLTextureCache();
void insert(QGLContext *ctx, qint64 key, QGLTexture *texture, int cost);
- void remove(quint64 key) { m_cache.remove(key); }
+ void remove(qint64 key);
+ inline int size();
+ inline void setMaxCost(int newMax);
+ inline int maxCost();
+ inline QGLTexture* getTexture(QGLContext *ctx, qint64 key);
+
bool remove(QGLContext *ctx, GLuint textureId);
void removeContextTextures(QGLContext *ctx);
- int size() { return m_cache.size(); }
- void setMaxCost(int newMax) { m_cache.setMaxCost(newMax); }
- int maxCost() {return m_cache.maxCost(); }
- QGLTexture* getTexture(quint64 key) { return m_cache.object(key); }
-
static QGLTextureCache *instance();
- static void deleteIfEmpty();
static void cleanupTexturesForCacheKey(qint64 cacheKey);
static void cleanupTexturesForPixampData(QPixmapData* pixmap);
static void cleanupBeforePixmapDestruction(QPixmapData* pixmap);
private:
- QCache<qint64, QGLTexture> m_cache;
+ QCache<QGLTextureCacheKey, QGLTexture> m_cache;
+ QReadWriteLock m_lock;
};
+int QGLTextureCache::size() {
+ QReadLocker locker(&m_lock);
+ return m_cache.size();
+}
+
+void QGLTextureCache::setMaxCost(int newMax)
+{
+ QWriteLocker locker(&m_lock);
+ m_cache.setMaxCost(newMax);
+}
+
+int QGLTextureCache::maxCost()
+{
+ QReadLocker locker(&m_lock);
+ return m_cache.maxCost();
+}
+
+QGLTexture* QGLTextureCache::getTexture(QGLContext *ctx, qint64 key)
+{
+ QReadLocker locker(&m_lock);
+ const QGLTextureCacheKey cacheKey = {key, QGLContextPrivate::contextGroup(ctx)};
+ return m_cache.object(cacheKey);
+}
extern Q_OPENGL_EXPORT QPaintEngine* qt_qgl_paint_engine();
diff --git a/src/opengl/qgl_qws.cpp b/src/opengl/qgl_qws.cpp
index fd17a27..38c3774 100644
--- a/src/opengl/qgl_qws.cpp
+++ b/src/opengl/qgl_qws.cpp
@@ -119,21 +119,6 @@ bool QGLFormat::hasOpenGLOverlays()
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());
-}
-
static EGLSurface qt_egl_create_surface
(QEglContext *context, QPaintDevice *device,
const QEglProperties *properties = 0)
@@ -197,12 +182,14 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
// Get the display and initialize it.
d->eglContext = new QEglContext();
+ d->ownsEglContext = true;
d->eglContext->setApi(QEgl::OpenGL);
// Construct the configuration we need for this surface.
QEglProperties configProps;
- qt_egl_add_platform_config(configProps, device());
- qt_egl_set_format(configProps, devType, d->glFormat);
+ qt_eglproperties_set_glformat(configProps, d->glFormat);
+ configProps.setDeviceType(devType);
+ configProps.setPaintDeviceFormat(device());
configProps.setRenderableType(QEgl::OpenGL);
// Search for a matching configuration, reducing the complexity
@@ -214,7 +201,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
}
// Inform the higher layers about the actual format properties.
- qt_egl_update_format(*(d->eglContext), d->glFormat);
+ qt_glformat_from_eglconfig(d->glFormat, d->eglContext->config());
// Create a new context for the configuration.
if (!d->eglContext->createContext
diff --git a/src/opengl/qgl_win.cpp b/src/opengl/qgl_win.cpp
index ed4814f..5ab944a 100644
--- a/src/opengl/qgl_win.cpp
+++ b/src/opengl/qgl_win.cpp
@@ -122,6 +122,30 @@ typedef bool (APIENTRY *PFNWGLCHOOSEPIXELFORMATARB)(HDC hdc,
#define WGL_TYPE_COLORINDEX_ARB 0x202C
#endif
+#ifndef WGL_ARB_create_context
+#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
+#define WGL_CONTEXT_FLAGS_ARB 0x2094
+#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
+#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
+#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x0001
+#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x0002
+// Error codes returned by GetLastError().
+#define ERROR_INVALID_VERSION_ARB 0x2095
+#define ERROR_INVALID_PROFILE_ARB 0x2096
+#endif
+
+#ifndef GL_VERSION_3_2
+#define GL_CONTEXT_PROFILE_MASK 0x9126
+#define GL_MAJOR_VERSION 0x821B
+#define GL_MINOR_VERSION 0x821C
+#define GL_NUM_EXTENSIONS 0x821D
+#define GL_CONTEXT_FLAGS 0x821E
+#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001
+#endif
+
QT_BEGIN_NAMESPACE
class QGLCmapPrivate
@@ -682,8 +706,118 @@ QGLTemporaryContext::~QGLTemporaryContext()
wglMakeCurrent(d->old_dc, d->old_context);
}
+static bool qgl_create_context(HDC hdc, QGLContextPrivate *d, QGLContextPrivate *shareContext)
+{
+ d->rc = 0;
+
+ typedef HGLRC (APIENTRYP PFNWGLCREATECONTEXTATTRIBSARB)(HDC, HGLRC, const int *);
+ PFNWGLCREATECONTEXTATTRIBSARB wglCreateContextAttribsARB =
+ (PFNWGLCREATECONTEXTATTRIBSARB) wglGetProcAddress("wglCreateContextAttribsARB");
+ if (wglCreateContextAttribsARB) {
+ int attributes[11];
+ int attribIndex = 0;
+ const int major = d->reqFormat.majorVersion();
+ const int minor = d->reqFormat.minorVersion();
+ attributes[attribIndex++] = WGL_CONTEXT_MAJOR_VERSION_ARB;
+ attributes[attribIndex++] = major;
+ attributes[attribIndex++] = WGL_CONTEXT_MINOR_VERSION_ARB;
+ attributes[attribIndex++] = minor;
+
+ if (major >= 3 && !d->reqFormat.testOption(QGL::DeprecatedFunctions)) {
+ attributes[attribIndex++] = WGL_CONTEXT_FLAGS_ARB;
+ attributes[attribIndex++] = WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
+ }
+
+ if ((major == 3 && minor >= 2) || major > 3) {
+ switch (d->reqFormat.profile()) {
+ case QGLFormat::NoProfile:
+ break;
+ case QGLFormat::CoreProfile:
+ attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB;
+ attributes[attribIndex++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
+ break;
+ case QGLFormat::CompatibilityProfile:
+ attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB;
+ attributes[attribIndex++] = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
+ break;
+ default:
+ qWarning("QGLContext::chooseContext(): Context profile not supported.");
+ return false;
+ }
+ }
+
+ if (d->reqFormat.plane() != 0) {
+ attributes[attribIndex++] = WGL_CONTEXT_LAYER_PLANE_ARB;
+ attributes[attribIndex++] = d->reqFormat.plane();
+ }
+
+ attributes[attribIndex++] = 0; // Terminate list.
+ d->rc = wglCreateContextAttribsARB(hdc, shareContext && shareContext->valid
+ ? shareContext->rc : 0, attributes);
+ if (d->rc) {
+ if (shareContext)
+ shareContext->sharing = d->sharing = true;
+ return true;
+ }
+ }
+
+ d->rc = wglCreateLayerContext(hdc, d->reqFormat.plane());
+ if (d->rc && shareContext && shareContext->valid)
+ shareContext->sharing = d->sharing = wglShareLists(shareContext->rc, d->rc);
+ return d->rc != 0;
+}
+
+void QGLContextPrivate::updateFormatVersion()
+{
+ const GLubyte *s = glGetString(GL_VERSION);
+
+ if (!(s && s[0] >= '0' && s[0] <= '9' && s[1] == '.' && s[2] >= '0' && s[2] <= '9')) {
+ if (!s)
+ qWarning("QGLContext::chooseContext(): OpenGL version string is null.");
+ else
+ qWarning("QGLContext::chooseContext(): Unexpected OpenGL version string format.");
+ glFormat.setVersion(0, 0);
+ glFormat.setProfile(QGLFormat::NoProfile);
+ glFormat.setOption(QGL::DeprecatedFunctions);
+ return;
+ }
+
+ int major = s[0] - '0';
+ int minor = s[2] - '0';
+ glFormat.setVersion(major, minor);
+
+ if (major < 3) {
+ glFormat.setProfile(QGLFormat::NoProfile);
+ glFormat.setOption(QGL::DeprecatedFunctions);
+ } else {
+ GLint value = 0;
+ if (major > 3 || minor >= 2)
+ glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);
+
+ switch (value) {
+ case WGL_CONTEXT_CORE_PROFILE_BIT_ARB:
+ glFormat.setProfile(QGLFormat::CoreProfile);
+ break;
+ case WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB:
+ glFormat.setProfile(QGLFormat::CompatibilityProfile);
+ break;
+ default:
+ glFormat.setProfile(QGLFormat::NoProfile);
+ break;
+ }
+
+ glGetIntegerv(GL_CONTEXT_FLAGS, &value);
+ if (value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)
+ glFormat.setOption(QGL::NoDeprecatedFunctions);
+ else
+ glFormat.setOption(QGL::DeprecatedFunctions);
+ }
+}
+
bool QGLContext::chooseContext(const QGLContext* shareContext)
{
+ QGLContextPrivate *share = shareContext ? const_cast<QGLContext *>(shareContext)->d_func() : 0;
+
Q_D(QGLContext);
// workaround for matrox driver:
// make a cheap call to opengl to force loading of DLL
@@ -741,8 +875,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
goto end;
}
- d->rc = wglCreateLayerContext(myDc, d->glFormat.plane());
- if (!d->rc) {
+ if (!qgl_create_context(myDc, d, share)) {
qwglError("QGLContext::chooseContext()", "CreateLayerContext");
result = false;
goto end;
@@ -792,16 +925,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
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;
- }
- {
+ } else {
PIXELFORMATDESCRIPTOR pfd;
PIXELFORMATDESCRIPTOR realPfd;
d->pixelFormatId = choosePixelFormat(&pfd, myDc);
@@ -840,17 +964,12 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
goto end;
}
- if (!(d->rc = wglCreateLayerContext(myDc, 0))) {
+ if (!qgl_create_context(myDc, d, share)) {
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) {
@@ -865,6 +984,9 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
end:
// vblanking
wglMakeCurrent(myDc, d->rc);
+ if (d->rc)
+ d->updateFormatVersion();
+
typedef BOOL (APIENTRYP PFNWGLSWAPINTERVALEXT) (int interval);
typedef int (APIENTRYP PFNWGLGETSWAPINTERVALEXT) (void);
PFNWGLSWAPINTERVALEXT wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXT) wglGetProcAddress("wglSwapIntervalEXT");
@@ -1158,8 +1280,9 @@ void QGLContext::reset()
void QGLContext::makeCurrent()
{
Q_D(QGLContext);
- if (d->rc == wglGetCurrentContext() || !d->valid) // already current
+ if (d->rc == wglGetCurrentContext() || !d->valid) // already current
return;
+
if (d->win) {
d->dc = GetDC(d->win);
if (!d->dc) {
diff --git a/src/opengl/qgl_wince.cpp b/src/opengl/qgl_wince.cpp
index f81115c..054a8d1 100644
--- a/src/opengl/qgl_wince.cpp
+++ b/src/opengl/qgl_wince.cpp
@@ -54,9 +54,9 @@
#include <windows.h>
-#include <private/qegl_p.h>
+#include <private/qeglproperties_p.h>
+#include <private/qeglcontext_p.h>
#include <private/qgl_egl_p.h>
-#include <private/qgl_cl_p.h>
QT_BEGIN_NAMESPACE
@@ -121,16 +121,6 @@ QGLTemporaryContext::~QGLTemporaryContext()
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);
-}
-
-
static bool opengl32dll = false;
bool QGLFormat::hasOpenGLOverlays()
@@ -154,12 +144,14 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
// Get the display and initialize it.
d->eglContext = new QEglContext();
+ d->ownsEglContext = true;
d->eglContext->setApi(QEgl::OpenGL);
// Construct the configuration we need for this surface.
QEglProperties configProps;
- qt_egl_add_platform_config(configProps, device());
- qt_egl_set_format(configProps, devType, d->glFormat);
+ qt_eglproperties_set_glformat(configProps, d->glFormat);
+ configProps.setDeviceType(devType);
+ configProps.setPaintDeviceFormat(device());
configProps.setRenderableType(QEgl::OpenGL);
// Search for a matching configuration, reducing the complexity
@@ -171,7 +163,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
}
// Inform the higher layers about the actual format properties.
- qt_egl_update_format(*(d->eglContext), d->glFormat);
+ qt_glformat_from_eglconfig(d->glFormat, d->eglContext->config());
// Create a new context for the configuration.
if (!d->eglContext->createContext
diff --git a/src/opengl/qgl_x11.cpp b/src/opengl/qgl_x11.cpp
index 326785e..bfb232d 100644
--- a/src/opengl/qgl_x11.cpp
+++ b/src/opengl/qgl_x11.cpp
@@ -115,6 +115,20 @@ extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
#define GLX_FRONT_LEFT_EXT 0x20DE
#endif
+#ifndef GLX_ARB_create_context
+#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001
+#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
+#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define GLX_CONTEXT_FLAGS_ARB 0x2094
+#endif
+
+#ifndef GLX_ARB_create_context_profile
+#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
+#endif
+
/*
The qt_gl_choose_cmap function is internal and used by QGLWidget::setContext()
and GLX (not Windows). If the application can't find any sharable
@@ -401,6 +415,148 @@ bool QGLFormat::hasOpenGLOverlays()
return trans_colors.size() > 0;
}
+static bool buildSpec(int* spec, const QGLFormat& f, QPaintDevice* paintDevice,
+ int bufDepth, bool onlyFBConfig = false)
+{
+ int i = 0;
+ spec[i++] = GLX_LEVEL;
+ spec[i++] = f.plane();
+ const QX11Info *xinfo = qt_x11Info(paintDevice);
+ bool useFBConfig = onlyFBConfig;
+
+#if defined(GLX_VERSION_1_3) && !defined(QT_NO_XRENDER) && !defined(Q_OS_HPUX)
+ /*
+ HPUX defines GLX_VERSION_1_3 but does not implement the corresponding functions.
+ Specifically glXChooseFBConfig and glXGetVisualFromFBConfig are not implemented.
+ */
+ QWidget* widget = 0;
+ if (paintDevice->devType() == QInternal::Widget)
+ widget = static_cast<QWidget*>(paintDevice);
+
+ // Only use glXChooseFBConfig for widgets if we're trying to get an ARGB visual
+ if (widget && widget->testAttribute(Qt::WA_TranslucentBackground) && X11->use_xrender)
+ useFBConfig = true;
+#endif
+
+#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_info)
+ static bool useTranspExt = false;
+ static bool useTranspExtChecked = false;
+ if (f.plane() && !useTranspExtChecked && paintDevice) {
+ QGLExtensionMatcher extensions(glXQueryExtensionsString(xinfo->display(), xinfo->screen()));
+ useTranspExt = extensions.match("GLX_EXT_visual_info");
+ //# (A bit simplistic; that could theoretically be a substring)
+ if (useTranspExt) {
+ QByteArray cstr(glXGetClientString(xinfo->display(), GLX_VENDOR));
+ useTranspExt = !cstr.contains("Xi Graphics"); // bug workaround
+ if (useTranspExt) {
+ // bug workaround - some systems (eg. FireGL) refuses to return an overlay
+ // visual if the GLX_TRANSPARENT_TYPE_EXT attribute is specified, even if
+ // the implementation supports transparent overlays
+ int tmpSpec[] = { GLX_LEVEL, f.plane(), GLX_TRANSPARENT_TYPE_EXT,
+ f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT,
+ XNone };
+ XVisualInfo * vinf = glXChooseVisual(xinfo->display(), xinfo->screen(), tmpSpec);
+ if (!vinf) {
+ useTranspExt = false;
+ }
+ }
+ }
+
+ useTranspExtChecked = true;
+ }
+ if (f.plane() && useTranspExt && !useFBConfig) {
+ // Required to avoid non-transparent overlay visual(!) on some systems
+ spec[i++] = GLX_TRANSPARENT_TYPE_EXT;
+ spec[i++] = f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT;
+ }
+#endif
+
+#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
+ // GLX_RENDER_TYPE is only in glx >=1.3
+ if (useFBConfig) {
+ spec[i++] = GLX_RENDER_TYPE;
+ spec[i++] = f.rgba() ? GLX_RGBA_BIT : GLX_COLOR_INDEX_BIT;
+ }
+#endif
+
+ if (f.doubleBuffer())
+ spec[i++] = GLX_DOUBLEBUFFER;
+ if (useFBConfig)
+ spec[i++] = True;
+ if (f.depth()) {
+ spec[i++] = GLX_DEPTH_SIZE;
+ spec[i++] = f.depthBufferSize() == -1 ? 1 : f.depthBufferSize();
+ }
+ if (f.stereo()) {
+ spec[i++] = GLX_STEREO;
+ if (useFBConfig)
+ spec[i++] = True;
+ }
+ if (f.stencil()) {
+ spec[i++] = GLX_STENCIL_SIZE;
+ spec[i++] = f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize();
+ }
+ if (f.rgba()) {
+ if (!useFBConfig)
+ spec[i++] = GLX_RGBA;
+ spec[i++] = GLX_RED_SIZE;
+ spec[i++] = f.redBufferSize() == -1 ? 1 : f.redBufferSize();
+ spec[i++] = GLX_GREEN_SIZE;
+ spec[i++] = f.greenBufferSize() == -1 ? 1 : f.greenBufferSize();
+ spec[i++] = GLX_BLUE_SIZE;
+ spec[i++] = f.blueBufferSize() == -1 ? 1 : f.blueBufferSize();
+ if (f.alpha()) {
+ spec[i++] = GLX_ALPHA_SIZE;
+ spec[i++] = f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize();
+ }
+ if (f.accum()) {
+ spec[i++] = GLX_ACCUM_RED_SIZE;
+ spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ spec[i++] = GLX_ACCUM_GREEN_SIZE;
+ spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ spec[i++] = GLX_ACCUM_BLUE_SIZE;
+ spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ if (f.alpha()) {
+ spec[i++] = GLX_ACCUM_ALPHA_SIZE;
+ spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
+ }
+ }
+ } else {
+ spec[i++] = GLX_BUFFER_SIZE;
+ spec[i++] = bufDepth;
+ }
+
+ if (f.sampleBuffers()) {
+ spec[i++] = GLX_SAMPLE_BUFFERS_ARB;
+ spec[i++] = 1;
+ spec[i++] = GLX_SAMPLES_ARB;
+ spec[i++] = f.samples() == -1 ? 4 : f.samples();
+ }
+
+#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
+ if (useFBConfig) {
+ spec[i++] = GLX_DRAWABLE_TYPE;
+ switch(paintDevice->devType()) {
+ case QInternal::Pixmap:
+ spec[i++] = GLX_PIXMAP_BIT;
+ break;
+ case QInternal::Pbuffer:
+ spec[i++] = GLX_PBUFFER_BIT;
+ break;
+ default:
+ qWarning("QGLContext: Unknown paint device type %d", paintDevice->devType());
+ // Fall-through & assume it's a window
+ case QInternal::Widget:
+ spec[i++] = GLX_WINDOW_BIT;
+ break;
+ };
+ }
+#endif
+
+ spec[i] = XNone;
+ return useFBConfig;
+}
+
/*****************************************************************************
QGLContext UNIX/GLX-specific code
*****************************************************************************/
@@ -493,21 +649,85 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
shareContext = 0;
}
+ const int major = d->reqFormat.majorVersion();
+ const int minor = d->reqFormat.minorVersion();
+ const int profile = d->reqFormat.profile() == QGLFormat::CompatibilityProfile
+ ? GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB
+ : GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
+
d->cx = 0;
- if (shareContext) {
+
+#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
+ /*
+ HPUX defines GLX_VERSION_1_3 but does not implement the corresponding functions.
+ Specifically glXChooseFBConfig and glXGetVisualFromFBConfig are not implemented.
+ */
+ if ((major == 3 && minor >= 2) || major > 3) {
+ QGLTemporaryContext *tmpContext = 0;
+ if (!QGLContext::currentContext())
+ tmpContext = new QGLTemporaryContext;
+
+ int attributes[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, major,
+ GLX_CONTEXT_MINOR_VERSION_ARB, minor,
+ GLX_CONTEXT_PROFILE_MASK_ARB, profile,
+ 0 };
+
+ typedef GLXContext ( * Q_PFNGLXCREATECONTEXTATTRIBSARBPROC)
+ (Display* dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list);
+
+
+ Q_PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs =
+ (Q_PFNGLXCREATECONTEXTATTRIBSARBPROC) qglx_getProcAddress("glXCreateContextAttribsARB");
+
+ if (glXCreateContextAttribs) {
+ int spec[45];
+ glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_BUFFER_SIZE, &res);
+ buildSpec(spec, format(), d->paintDevice, res, true);
+
+ GLXFBConfig *configs;
+ int configCount = 0;
+ configs = glXChooseFBConfig(disp, xinfo->screen(), spec, &configCount);
+
+ if (configs && configCount > 0) {
+ d->cx = glXCreateContextAttribs(disp, configs[0],
+ shareContext ? (GLXContext)shareContext->d_func()->cx : 0, direct, attributes);
+ if (!d->cx && shareContext) {
+ shareContext = 0;
+ d->cx = glXCreateContextAttribs(disp, configs[0], 0, direct, attributes);
+ }
+ d->screen = ((XVisualInfo*)d->vi)->screen;
+ }
+ XFree(configs);
+ } else {
+ qWarning("QGLContext::chooseContext(): OpenGL %d.%d is not supported", major, minor);
+ }
+
+ if (tmpContext)
+ delete tmpContext;
+ }
+#else
+ Q_UNUSED(major);
+ Q_UNUSED(minor);
+ Q_UNUSED(profile);
+#endif
+
+ if (!d->cx && shareContext) {
d->cx = glXCreateContext(disp, (XVisualInfo *)d->vi,
(GLXContext)shareContext->d_func()->cx, direct);
d->screen = ((XVisualInfo*)d->vi)->screen;
- if (d->cx) {
- 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;
+ shareContext = 0;
+ }
+
+ if (shareContext && d->cx) {
+ QGLContext *share = const_cast<QGLContext *>(shareContext);
+ d->sharing = true;
+ share->d_func()->sharing = true;
}
+
if (!d->cx)
return false;
d->glFormat.setDirectRendering(glXIsDirect(disp, (GLXContext)d->cx));
@@ -606,143 +826,8 @@ void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth)
{
Q_D(QGLContext);
int spec[45];
- int i = 0;
- spec[i++] = GLX_LEVEL;
- spec[i++] = f.plane();
const QX11Info *xinfo = qt_x11Info(d->paintDevice);
- bool useFBConfig = false;
-
-#if defined(GLX_VERSION_1_3) && !defined(QT_NO_XRENDER) && !defined(Q_OS_HPUX)
- /*
- HPUX defines GLX_VERSION_1_3 but does not implement the corresponding functions.
- Specifically glXChooseFBConfig and glXGetVisualFromFBConfig are not implemented.
- */
- QWidget* widget = 0;
- if (d->paintDevice->devType() == QInternal::Widget)
- widget = static_cast<QWidget*>(d->paintDevice);
-
- // Only use glXChooseFBConfig for widgets if we're trying to get an ARGB visual
- if (widget && widget->testAttribute(Qt::WA_TranslucentBackground) && X11->use_xrender)
- useFBConfig = true;
-#endif
-
-#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_info)
- static bool useTranspExt = false;
- static bool useTranspExtChecked = false;
- if (f.plane() && !useTranspExtChecked && d->paintDevice) {
- QGLExtensionMatcher extensions(glXQueryExtensionsString(xinfo->display(), xinfo->screen()));
- useTranspExt = extensions.match("GLX_EXT_visual_info");
- //# (A bit simplistic; that could theoretically be a substring)
- if (useTranspExt) {
- QByteArray cstr(glXGetClientString(xinfo->display(), GLX_VENDOR));
- useTranspExt = !cstr.contains("Xi Graphics"); // bug workaround
- if (useTranspExt) {
- // bug workaround - some systems (eg. FireGL) refuses to return an overlay
- // visual if the GLX_TRANSPARENT_TYPE_EXT attribute is specified, even if
- // the implementation supports transparent overlays
- int tmpSpec[] = { GLX_LEVEL, f.plane(), GLX_TRANSPARENT_TYPE_EXT,
- f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT,
- XNone };
- XVisualInfo * vinf = glXChooseVisual(xinfo->display(), xinfo->screen(), tmpSpec);
- if (!vinf) {
- useTranspExt = false;
- }
- }
- }
-
- useTranspExtChecked = true;
- }
- if (f.plane() && useTranspExt && !useFBConfig) {
- // Required to avoid non-transparent overlay visual(!) on some systems
- spec[i++] = GLX_TRANSPARENT_TYPE_EXT;
- spec[i++] = f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT;
- }
-#endif
-
-#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
- // GLX_RENDER_TYPE is only in glx >=1.3
- if (useFBConfig) {
- spec[i++] = GLX_RENDER_TYPE;
- spec[i++] = f.rgba() ? GLX_RGBA_BIT : GLX_COLOR_INDEX_BIT;
- }
-#endif
-
- if (f.doubleBuffer())
- spec[i++] = GLX_DOUBLEBUFFER;
- if (useFBConfig)
- spec[i++] = True;
- if (f.depth()) {
- spec[i++] = GLX_DEPTH_SIZE;
- spec[i++] = f.depthBufferSize() == -1 ? 1 : f.depthBufferSize();
- }
- if (f.stereo()) {
- spec[i++] = GLX_STEREO;
- if (useFBConfig)
- spec[i++] = True;
- }
- if (f.stencil()) {
- spec[i++] = GLX_STENCIL_SIZE;
- spec[i++] = f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize();
- }
- if (f.rgba()) {
- if (!useFBConfig)
- spec[i++] = GLX_RGBA;
- spec[i++] = GLX_RED_SIZE;
- spec[i++] = f.redBufferSize() == -1 ? 1 : f.redBufferSize();
- spec[i++] = GLX_GREEN_SIZE;
- spec[i++] = f.greenBufferSize() == -1 ? 1 : f.greenBufferSize();
- spec[i++] = GLX_BLUE_SIZE;
- spec[i++] = f.blueBufferSize() == -1 ? 1 : f.blueBufferSize();
- if (f.alpha()) {
- spec[i++] = GLX_ALPHA_SIZE;
- spec[i++] = f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize();
- }
- if (f.accum()) {
- spec[i++] = GLX_ACCUM_RED_SIZE;
- spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
- spec[i++] = GLX_ACCUM_GREEN_SIZE;
- spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
- spec[i++] = GLX_ACCUM_BLUE_SIZE;
- spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
- if (f.alpha()) {
- spec[i++] = GLX_ACCUM_ALPHA_SIZE;
- spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
- }
- }
- } else {
- spec[i++] = GLX_BUFFER_SIZE;
- spec[i++] = bufDepth;
- }
-
- if (f.sampleBuffers()) {
- spec[i++] = GLX_SAMPLE_BUFFERS_ARB;
- spec[i++] = 1;
- spec[i++] = GLX_SAMPLES_ARB;
- spec[i++] = f.samples() == -1 ? 4 : f.samples();
- }
-
-#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
- if (useFBConfig) {
- spec[i++] = GLX_DRAWABLE_TYPE;
- switch(d->paintDevice->devType()) {
- case QInternal::Pixmap:
- spec[i++] = GLX_PIXMAP_BIT;
- break;
- case QInternal::Pbuffer:
- spec[i++] = GLX_PBUFFER_BIT;
- break;
- default:
- qWarning("QGLContext: Unknown paint device type %d", d->paintDevice->devType());
- // Fall-through & assume it's a window
- case QInternal::Widget:
- spec[i++] = GLX_WINDOW_BIT;
- break;
- };
- }
-#endif
-
- spec[i] = XNone;
-
+ bool useFBConfig = buildSpec(spec, f, d->paintDevice, bufDepth, false);
XVisualInfo* chosenVisualInfo = 0;
@@ -755,7 +840,7 @@ void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth)
if (!configs)
break; // fallback to trying glXChooseVisual
- for (i = 0; i < configCount; ++i) {
+ for (int i = 0; i < configCount; ++i) {
XVisualInfo* vi;
vi = glXGetVisualFromFBConfig(xinfo->display(), configs[i]);
if (!vi)
@@ -843,7 +928,7 @@ void QGLContext::makeCurrent()
} 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);
+ ok = glXMakeCurrent(xinfo->display(), ((QWidget *)d->paintDevice)->internalWinId(), (GLXContext)d->cx);
}
if (!ok)
qWarning("QGLContext::makeCurrent(): Failed.");
@@ -1671,20 +1756,28 @@ static bool qt_resolveTextureFromPixmap(QPaintDevice *paintDevice)
#endif //defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
-QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData *pmd, const qint64 key,
+QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pixmap, const qint64 key,
QGLContext::BindOptions options)
{
#if !defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX)
return 0;
#else
+
+ // Check we have GLX 1.3, as it is needed for glXCreatePixmap & glXDestroyPixmap
+ int majorVersion = 0;
+ int minorVersion = 0;
+ glXQueryVersion(X11->display, &majorVersion, &minorVersion);
+ if (majorVersion < 1 || (majorVersion == 1 && minorVersion < 3))
+ return 0;
+
Q_Q(QGLContext);
- Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pixmap->data_ptr().data());
+ Q_ASSERT(pixmapData->classId() == QPixmapData::X11Class);
if (!qt_resolveTextureFromPixmap(paintDevice))
return 0;
- QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
const QX11Info &x11Info = pixmapData->xinfo;
// Store the configs (Can be static because configs aren't dependent on current context)
@@ -1753,7 +1846,7 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData *pmd, con
if (!glxPixmap)
return 0;
- pixmapData->gl_surface = (Qt::HANDLE)glxPixmap;
+ pixmapData->gl_surface = (void*)glxPixmap;
// Make sure the cleanup hook gets called so we can delete the glx pixmap
QImagePixmapCleanupHooks::enableCleanupHooks(pixmapData);
diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp
index 3d183ee..9d28de0 100644
--- a/src/opengl/qgl_x11egl.cpp
+++ b/src/opengl/qgl_x11egl.cpp
@@ -42,20 +42,17 @@
#include "qgl.h"
#include <private/qt_x11_p.h>
#include <private/qpixmap_x11_p.h>
-#include <private/qimagepixmapcleanuphooks_p.h>
#include <private/qgl_p.h>
#include <private/qpaintengine_opengl_p.h>
#include "qgl_egl_p.h"
#include "qcolormap.h"
#include <QDebug>
+#include <QPixmap>
QT_BEGIN_NAMESPACE
-bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig config,
- const QX11Info &x11Info, bool useArgbVisual);
-
/*
QGLTemporaryContext implementation
*/
@@ -79,12 +76,7 @@ QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
d->surface = 0;
int screen = 0;
- d->display = eglGetDisplay(EGLNativeDisplayType(X11->display));
-
- if (!eglInitialize(d->display, NULL, NULL)) {
- qWarning("QGLTemporaryContext: Unable to initialize EGL display.");
- return;
- }
+ d->display = QEgl::display();
EGLConfig config;
int numConfigs = 0;
@@ -107,15 +99,7 @@ QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
int numVisuals;
EGLint id = 0;
- eglGetConfigAttrib(d->display, config, EGL_NATIVE_VISUAL_ID, &id);
- if (id == 0) {
- // EGL_NATIVE_VISUAL_ID is optional and might not be supported
- // on some implementations - we'll have to do it the hard way
- QX11Info xinfo;
- qt_egl_setup_x11_visual(visualInfo, d->display, config, xinfo, false);
- } else {
- visualInfo.visualid = id;
- }
+ visualInfo.visualid = QEgl::getCompatibleVisualId(config);
vi = XGetVisualInfo(X11->display, VisualIDMask, &visualInfo, &numVisuals);
if (!vi || numVisuals < 1) {
qWarning("QGLTemporaryContext: Unable to get X11 visual info id.");
@@ -170,12 +154,6 @@ 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());
-}
-
// Chooses the EGL config and creates the EGL context
bool QGLContext::chooseContext(const QGLContext* shareContext)
{
@@ -186,54 +164,54 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
int devType = device()->devType();
- // Get the display and initialize it.
+ QX11PixmapData *x11PixmapData = 0;
+ if (devType == QInternal::Pixmap) {
+ QPixmapData *pmd = static_cast<QPixmap*>(device())->data_ptr().data();
+ if (pmd->classId() == QPixmapData::X11Class)
+ x11PixmapData = static_cast<QX11PixmapData*>(pmd);
+ else {
+ // TODO: Replace the pixmap's data with a new QX11PixmapData
+ qWarning("WARNING: Creating a QGLContext on a QPixmap is only supported for X11 pixmap backend");
+ return false;
+ }
+ } else if ((devType != QInternal::Widget) && (devType != QInternal::Pbuffer)) {
+ qWarning("WARNING: Creating a QGLContext not supported on device type %d", devType);
+ return false;
+ }
+
+ // Only create the eglContext if we don't already have one:
if (d->eglContext == 0) {
d->eglContext = new QEglContext();
+ d->ownsEglContext = true;
d->eglContext->setApi(QEgl::OpenGL);
+ // If the device is a widget with WA_TranslucentBackground set, make sure the glFormat
+ // has the alpha channel option set:
+ if (devType == QInternal::Widget) {
+ QWidget* widget = static_cast<QWidget*>(device());
+ if (widget->testAttribute(Qt::WA_TranslucentBackground))
+ d->glFormat.setAlpha(true);
+ }
+
// Construct the configuration we need for this surface.
QEglProperties configProps;
- qt_egl_set_format(configProps, devType, d->glFormat);
- qt_egl_add_platform_config(configProps, device());
+ configProps.setDeviceType(devType);
configProps.setRenderableType(QEgl::OpenGL);
+ qt_eglproperties_set_glformat(configProps, d->glFormat);
-#if We_have_an_EGL_library_which_bothers_to_check_EGL_BUFFER_SIZE
- if (device()->depth() == 16 && configProps.value(EGL_ALPHA_SIZE) <= 0) {
- qDebug("Setting EGL_BUFFER_SIZE to 16");
- configProps.setValue(EGL_BUFFER_SIZE, 16);
- configProps.setValue(EGL_ALPHA_SIZE, 0);
- }
+ // Set buffer preserved for regular QWidgets, QGLWidgets are ok with either preserved or destroyed:
+ if ((devType == QInternal::Widget) && qobject_cast<QGLWidget*>(static_cast<QWidget*>(device())) == 0)
+ configProps.setValue(EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) {
delete d->eglContext;
d->eglContext = 0;
return false;
}
-#else
- QEgl::PixelFormatMatch matchType = QEgl::BestPixelFormat;
- if ((device()->depth() == 16) && configProps.value(EGL_ALPHA_SIZE) == 0) {
- configProps.setValue(EGL_RED_SIZE, 5);
- configProps.setValue(EGL_GREEN_SIZE, 6);
- configProps.setValue(EGL_BLUE_SIZE, 5);
- configProps.setValue(EGL_ALPHA_SIZE, 0);
- matchType = QEgl::ExactPixelFormat;
- }
-
- // Search for a matching configuration, reducing the complexity
- // each time until we get something that matches.
- if (!d->eglContext->chooseConfig(configProps, matchType)) {
- delete d->eglContext;
- d->eglContext = 0;
- return false;
- }
-#endif
-
-// qDebug("QGLContext::chooseContext() - using EGL config %d:", d->eglContext->config());
-// qDebug() << QEglProperties(d->eglContext->config()).toString();
// Create a new context for the configuration.
- if (!d->eglContext->createContext
- (shareContext ? shareContext->d_func()->eglContext : 0)) {
+ QEglContext* eglSharedContext = shareContext ? shareContext->d_func()->eglContext : 0;
+ if (!d->eglContext->createContext(eglSharedContext)) {
delete d->eglContext;
d->eglContext = 0;
return false;
@@ -241,15 +219,32 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
d->sharing = d->eglContext->isSharing();
if (d->sharing && shareContext)
const_cast<QGLContext *>(shareContext)->d_func()->sharing = true;
+ }
-#if defined(EGL_VERSION_1_1)
- if (d->glFormat.swapInterval() != -1 && devType == QInternal::Widget)
- eglSwapInterval(d->eglContext->display(), d->glFormat.swapInterval());
-#endif
+ // Inform the higher layers about the actual format properties
+ qt_glformat_from_eglconfig(d->glFormat, d->eglContext->config());
+
+ // Do don't create the EGLSurface for everything.
+ // QWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface
+ // QGLWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface
+ // QPixmap - yes, create the EGLSurface but store it in QX11PixmapData::gl_surface
+ // QGLPixelBuffer - no, it creates the surface itself and stores it in QGLPixelBufferPrivate::pbuf
+
+ if (devType == QInternal::Widget) {
+ if (d->eglSurface != EGL_NO_SURFACE)
+ eglDestroySurface(d->eglContext->display(), d->eglSurface);
+ d->eglSurface = QEgl::createSurface(device(), d->eglContext->config());
+ XFlush(X11->display);
+ setWindowCreated(true);
}
- // Inform the higher layers about the actual format properties.
- qt_egl_update_format(*(d->eglContext), d->glFormat);
+ if (x11PixmapData) {
+ // TODO: Actually check to see if the existing surface can be re-used
+ if (x11PixmapData->gl_surface)
+ eglDestroySurface(d->eglContext->display(), (EGLSurface)x11PixmapData->gl_surface);
+
+ x11PixmapData->gl_surface = (void*)QEgl::createSurface(device(), d->eglContext->config());
+ }
return true;
}
@@ -281,142 +276,6 @@ void QGLWidget::updateOverlayGL()
//handle overlay
}
-//#define QT_DEBUG_X11_VISUAL_SELECTION 1
-
-bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig config, const QX11Info &x11Info, bool useArgbVisual)
-{
- bool foundVisualIsArgb = useArgbVisual;
-
-#ifdef QT_DEBUG_X11_VISUAL_SELECTION
- qDebug("qt_egl_setup_x11_visual() - useArgbVisual=%d", useArgbVisual);
-#endif
-
- memset(&vi, 0, sizeof(XVisualInfo));
-
- EGLint eglConfigColorSize;
- eglGetConfigAttrib(display, config, EGL_BUFFER_SIZE, &eglConfigColorSize);
-
- // Check to see if EGL is suggesting an appropriate visual id:
- EGLint nativeVisualId;
- eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &nativeVisualId);
- vi.visualid = nativeVisualId;
-
- if (vi.visualid) {
- // EGL has suggested a visual id, so get the rest of the visual info for that id:
- XVisualInfo *chosenVisualInfo;
- int matchingCount = 0;
- chosenVisualInfo = XGetVisualInfo(x11Info.display(), VisualIDMask, &vi, &matchingCount);
- if (chosenVisualInfo) {
-#if !defined(QT_NO_XRENDER)
- if (useArgbVisual) {
- // Check to make sure the visual provided by EGL is ARGB
- XRenderPictFormat *format;
- format = XRenderFindVisualFormat(x11Info.display(), chosenVisualInfo->visual);
- if (format->type == PictTypeDirect && format->direct.alphaMask) {
-#ifdef QT_DEBUG_X11_VISUAL_SELECTION
- qDebug("Using ARGB X Visual ID (%d) provided by EGL", (int)vi.visualid);
-#endif
- foundVisualIsArgb = true;
- vi = *chosenVisualInfo;
- }
- else {
- qWarning("Warning: EGL suggested using X visual ID %d for config %d, but this is not ARGB",
- nativeVisualId, (int)config);
- vi.visualid = 0;
- }
- } else
-#endif
- {
- if (eglConfigColorSize == chosenVisualInfo->depth) {
-#ifdef QT_DEBUG_X11_VISUAL_SELECTION
- qDebug("Using opaque X Visual ID (%d) provided by EGL", (int)vi.visualid);
-#endif
- vi = *chosenVisualInfo;
- } else
- qWarning("Warning: EGL suggested using X visual ID %d (%d bpp) for config %d (%d bpp), but the depths do not match!",
- nativeVisualId, chosenVisualInfo->depth, (int)config, eglConfigColorSize);
- }
- XFree(chosenVisualInfo);
- }
- else {
- qWarning("Warning: EGL suggested using X visual ID %d for config %d, but this seems to be invalid!",
- nativeVisualId, (int)config);
- vi.visualid = 0;
- }
- }
-
- // If EGL does not know the visual ID, so try to select an appropriate one ourselves, first
- // using XRender if we're supposed to have an alpha, then falling back to XGetVisualInfo
-
-#if !defined(QT_NO_XRENDER)
- if (vi.visualid == 0 && useArgbVisual) {
- // Try to use XRender to find an ARGB visual we can use
- vi.screen = x11Info.screen();
- vi.depth = 32; //### We might at some point (soon) get ARGB4444
- vi.c_class = TrueColor;
- XVisualInfo *matchingVisuals;
- int matchingCount = 0;
- matchingVisuals = XGetVisualInfo(x11Info.display(),
- VisualScreenMask|VisualDepthMask|VisualClassMask,
- &vi, &matchingCount);
-
- for (int i = 0; i < matchingCount; ++i) {
- XRenderPictFormat *format;
- format = XRenderFindVisualFormat(x11Info.display(), matchingVisuals[i].visual);
- if (format->type == PictTypeDirect && format->direct.alphaMask) {
- vi = matchingVisuals[i];
- foundVisualIsArgb = true;
-#ifdef QT_DEBUG_X11_VISUAL_SELECTION
- qDebug("Using X Visual ID (%d) for ARGB visual as provided by XRender", (int)vi.visualid);
-#endif
- break;
- }
- }
- XFree(matchingVisuals);
- }
-#endif
-
- if (vi.visualid == 0) {
- EGLint depth;
- eglGetConfigAttrib(display, config, EGL_BUFFER_SIZE, &depth);
- int err;
- err = XMatchVisualInfo(x11Info.display(), x11Info.screen(), depth, TrueColor, &vi);
- if (err == 0) {
- qWarning("Warning: Can't find an X visual which matches the EGL config(%d)'s depth (%d)!",
- (int)config, depth);
- depth = x11Info.depth();
- err = XMatchVisualInfo(x11Info.display(), x11Info.screen(), depth, TrueColor, &vi);
- if (err == 0) {
- qWarning("Error: Couldn't get any matching X visual!");
- return false;
- } else
- qWarning(" - Falling back to X11 suggested depth (%d)", depth);
- }
-#ifdef QT_DEBUG_X11_VISUAL_SELECTION
- else
- qDebug("Using X Visual ID (%d) for EGL provided depth (%d)", (int)vi.visualid, depth);
-#endif
-
- // Don't try to use ARGB now unless the visual is 32-bit - even then it might stil fail :-(
- if (useArgbVisual)
- foundVisualIsArgb = vi.depth == 32; //### We might at some point (soon) get ARGB4444
- }
-
-#ifdef QT_DEBUG_X11_VISUAL_SELECTION
- qDebug("Visual Info:");
- qDebug(" bits_per_rgb=%d", vi.bits_per_rgb);
- qDebug(" red_mask=0x%x", vi.red_mask);
- qDebug(" green_mask=0x%x", vi.green_mask);
- qDebug(" blue_mask=0x%x", vi.blue_mask);
- qDebug(" colormap_size=%d", vi.colormap_size);
- qDebug(" c_class=%d", vi.c_class);
- qDebug(" depth=%d", vi.depth);
- qDebug(" screen=%d", vi.screen);
- qDebug(" visualid=%d", vi.visualid);
-#endif
- return foundVisualIsArgb;
-}
-
void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
{
Q_D(QGLWidget);
@@ -434,20 +293,6 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext,
QGLContext* oldcx = d->glcx;
d->glcx = context;
- if (parentWidget()) {
- // force creation of delay-created widgets
- parentWidget()->winId();
- if (parentWidget()->x11Info().screen() != x11Info().screen())
- d_func()->xinfo = parentWidget()->d_func()->xinfo;
- }
-
- // If the application has set WA_TranslucentBackground and not explicitly set
- // the alpha buffer size to zero, modify the format so it have an alpha channel
- QGLFormat& fmt = d->glcx->d_func()->glFormat;
- const bool tryArgbVisual = testAttribute(Qt::WA_TranslucentBackground) || fmt.alpha();
- if (tryArgbVisual && fmt.alphaBufferSize() == -1)
- fmt.setAlphaBufferSize(1);
-
bool createFailed = false;
if (!d->glcx->isValid()) {
// Create the QGLContext here, which in turn chooses the EGL config
@@ -461,63 +306,8 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext,
return;
}
- if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) {
- if (deleteOldContext)
- delete oldcx;
- return;
- }
-
- bool visible = isVisible();
- if (visible)
- hide();
- XVisualInfo vi;
- QEglContext *eglContext = d->glcx->d_func()->eglContext;
- bool usingArgbVisual = qt_egl_setup_x11_visual(vi, eglContext->display(), eglContext->config(),
- x11Info(), tryArgbVisual);
-
- XSetWindowAttributes a;
-
- Window p = RootWindow(x11Info().display(), x11Info().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);
-
- unsigned int valueMask = CWBackPixel|CWBorderPixel;
- if (usingArgbVisual) {
- a.colormap = XCreateColormap(x11Info().display(), p, vi.visual, AllocNone);
- valueMask |= CWColormap;
- }
-
- Window w = XCreateWindow(x11Info().display(), p, x(), y(), width(), height(),
- 0, vi.depth, InputOutput, vi.visual, valueMask, &a);
-
- if (deleteOldContext)
- delete oldcx;
- oldcx = 0;
-
- create(w); // Create with the ID of the window we've just created
-
-
- // Create the EGL surface to draw into.
- QGLContextPrivate *ctxpriv = d->glcx->d_func();
- ctxpriv->eglSurface = ctxpriv->eglContext->createSurface(this);
- if (ctxpriv->eglSurface == EGL_NO_SURFACE) {
- delete ctxpriv->eglContext;
- ctxpriv->eglContext = 0;
- return;
- }
-
- d->eglSurfaceWindowId = w; // Remember the window id we created the surface for
-
- if (visible)
- show();
-
- XFlush(X11->display);
- d->glcx->setWindowCreated(true);
+ d->eglSurfaceWindowId = winId(); // Remember the window id we created the surface for
}
void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget* shareWidget)
@@ -526,7 +316,7 @@ void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget* shareWidget)
initContext(context, shareWidget);
- if(q->isValid() && glcx->format().hasOverlay()) {
+ if (q->isValid() && glcx->format().hasOverlay()) {
//no overlay
qWarning("QtOpenGL ES doesn't currently support overlays");
}
@@ -545,138 +335,29 @@ void QGLWidget::setColormap(const QGLColormap &)
{
}
-// Re-creates the EGL surface if the window ID has changed or if force is true
-void QGLWidgetPrivate::recreateEglSurface(bool force)
+// Re-creates the EGL surface if the window ID has changed or if there isn't a surface
+void QGLWidgetPrivate::recreateEglSurface()
{
Q_Q(QGLWidget);
Window currentId = q->winId();
- if ( force || (currentId != eglSurfaceWindowId) ) {
- // The window id has changed so we need to re-create the EGL surface
- QEglContext *ctx = glcx->d_func()->eglContext;
- EGLSurface surface = glcx->d_func()->eglSurface;
- if (surface != EGL_NO_SURFACE)
- ctx->destroySurface(surface); // Will force doneCurrent() if nec.
- surface = ctx->createSurface(q);
- if (surface == EGL_NO_SURFACE)
- qWarning("Error creating EGL window surface: 0x%x", eglGetError());
- glcx->d_func()->eglSurface = surface;
-
- eglSurfaceWindowId = currentId;
- }
-}
-
-// Selects which configs should be used
-EGLConfig Q_OPENGL_EXPORT qt_chooseEGLConfigForPixmap(bool hasAlpha, bool readOnly)
-{
- // Cache the configs we select as they wont change:
- static EGLConfig roPixmapRGBConfig = 0;
- static EGLConfig roPixmapRGBAConfig = 0;
- static EGLConfig rwPixmapRGBConfig = 0;
- static EGLConfig rwPixmapRGBAConfig = 0;
-
- EGLConfig* targetConfig;
-
- if (hasAlpha) {
- if (readOnly)
- targetConfig = &roPixmapRGBAConfig;
- else
- targetConfig = &rwPixmapRGBAConfig;
- }
- else {
- if (readOnly)
- targetConfig = &roPixmapRGBConfig;
- else
- targetConfig = &rwPixmapRGBConfig;
- }
-
- if (*targetConfig == 0) {
- QEglProperties configAttribs;
- configAttribs.setValue(EGL_SURFACE_TYPE, EGL_PIXMAP_BIT);
- configAttribs.setRenderableType(QEgl::OpenGL);
- if (hasAlpha)
- configAttribs.setValue(EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE);
- else
- configAttribs.setValue(EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE);
-
- // If this is going to be a render target, it needs to have a depth, stencil & sample buffer
- if (!readOnly) {
- configAttribs.setValue(EGL_DEPTH_SIZE, 1);
- configAttribs.setValue(EGL_STENCIL_SIZE, 1);
- configAttribs.setValue(EGL_SAMPLE_BUFFERS, 1);
- }
-
- EGLint configCount = 0;
- do {
- eglChooseConfig(QEglContext::display(), configAttribs.properties(), targetConfig, 1, &configCount);
- if (configCount > 0) {
- // Got one
- qDebug() << "Found an" << (hasAlpha ? "ARGB" : "RGB") << (readOnly ? "readonly" : "target" )
- << "config (" << int(*targetConfig) << ") to create a pixmap surface:";
-
-// QEglProperties configProps(*targetConfig);
-// qDebug() << configProps.toString();
- break;
- }
- qWarning("choosePixmapConfig() - No suitible config found, reducing requirements");
- } while (configAttribs.reduceConfiguration());
+ // If the window ID has changed since the surface was created, we need to delete the
+ // old surface before re-creating a new one. Note: This should not be the case as the
+ // surface should be deleted before the old window id.
+ if (glcx->d_func()->eglSurface != EGL_NO_SURFACE && (currentId != eglSurfaceWindowId)) {
+ qWarning("EGL surface for deleted window %x was not destroyed", eglSurfaceWindowId);
+ glcx->d_func()->destroyEglSurfaceForDevice();
}
- if (*targetConfig == 0)
- qWarning("choosePixmapConfig() - Couldn't find a suitable config");
-
- return *targetConfig;
-}
-
-bool Q_OPENGL_EXPORT qt_createEGLSurfaceForPixmap(QPixmapData* pmd, bool readOnly)
-{
- Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
- QX11PixmapData* pixmapData = static_cast<QX11PixmapData*>(pmd);
-
- bool hasAlpha = pixmapData->hasAlphaChannel();
-
- EGLConfig pixmapConfig = qt_chooseEGLConfigForPixmap(hasAlpha, readOnly);
-
- QEglProperties pixmapAttribs;
-
- // If the pixmap can't be bound to a texture, it's pretty useless
- pixmapAttribs.setValue(EGL_TEXTURE_TARGET, EGL_TEXTURE_2D);
- if (hasAlpha)
- pixmapAttribs.setValue(EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA);
- else
- pixmapAttribs.setValue(EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB);
-
- EGLSurface pixmapSurface;
- pixmapSurface = eglCreatePixmapSurface(QEglContext::display(),
- pixmapConfig,
- (EGLNativePixmapType) pixmapData->handle(),
- pixmapAttribs.properties());
-// qDebug("qt_createEGLSurfaceForPixmap() created surface 0x%x for pixmap 0x%x",
-// pixmapSurface, pixmapData->handle());
- if (pixmapSurface == EGL_NO_SURFACE) {
- qWarning() << "Failed to create a pixmap surface using config" << (int)pixmapConfig
- << ":" << QEglContext::errorString(eglGetError());
- return false;
- }
-
- static bool doneOnce = false;
- if (!doneOnce) {
- // Make sure QGLTextureCache is instanciated so it can install cleanup hooks
- // which cleanup the EGL surface.
- QGLTextureCache::instance();
- doneOnce = true;
+ if (glcx->d_func()->eglSurface == EGL_NO_SURFACE) {
+ glcx->d_func()->eglSurface = glcx->d_func()->eglContext->createSurface(q);
+ eglSurfaceWindowId = currentId;
}
-
- Q_ASSERT(sizeof(Qt::HANDLE) >= sizeof(EGLSurface)); // Just to make totally sure!
- pixmapData->gl_surface = (Qt::HANDLE)pixmapSurface;
- QImagePixmapCleanupHooks::enableCleanupHooks(pixmapData); // Make sure the cleanup hook gets called
-
- return true;
}
-QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, const qint64 key,
+QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pixmap, const qint64 key,
QGLContext::BindOptions options)
{
Q_Q(QGLContext);
@@ -685,82 +366,156 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, cons
if (!(options & QGLContext::CanFlipNativePixmapBindOption))
return 0;
- Q_ASSERT(pd->classId() == QPixmapData::X11Class);
static bool checkedForTFP = false;
static bool haveTFP = false;
+ static bool checkedForEglImageTFP = false;
+ static bool haveEglImageTFP = false;
+
+
+ if (!checkedForEglImageTFP) {
+ checkedForEglImageTFP = true;
+
+ // We need to be able to create an EGLImage from a native pixmap, which was split
+ // into a seperate EGL extension, EGL_KHR_image_pixmap. It is possible to have
+ // eglCreateImageKHR & eglDestroyImageKHR without support for pixmaps, so we must
+ // check we have the EGLImage from pixmap functionality.
+ if (QEgl::hasExtension("EGL_KHR_image") || QEgl::hasExtension("EGL_KHR_image_pixmap")) {
+
+ // Being able to create an EGLImage from a native pixmap is also pretty useless
+ // without the ability to bind that EGLImage as a texture, which is provided by
+ // the GL_OES_EGL_image extension, which we try to resolve here:
+ haveEglImageTFP = qt_resolve_eglimage_gl_extensions(q);
+
+ if (haveEglImageTFP)
+ qDebug("Found EGL_KHR_image_pixmap & GL_OES_EGL_image extensions (preferred method)!");
+ }
+ }
if (!checkedForTFP) {
// Check for texture_from_pixmap egl extension
checkedForTFP = true;
- if (eglContext->hasExtension("EGL_NOKIA_texture_from_pixmap") ||
- eglContext->hasExtension("EGL_EXT_texture_from_pixmap"))
+ if (QEgl::hasExtension("EGL_NOKIA_texture_from_pixmap") ||
+ QEgl::hasExtension("EGL_EXT_texture_from_pixmap"))
{
qDebug("Found texture_from_pixmap EGL extension!");
haveTFP = true;
}
}
- if (!haveTFP)
+ if (!haveTFP && !haveEglImageTFP)
return 0;
- QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pd);
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pixmap->data_ptr().data());
+ Q_ASSERT(pixmapData->classId() == QPixmapData::X11Class);
bool hasAlpha = pixmapData->hasAlphaChannel();
+ bool pixmapHasValidSurface = false;
+ bool textureIsBound = false;
+ GLuint textureId;
+ glGenTextures(1, &textureId);
+ glBindTexture(GL_TEXTURE_2D, textureId);
- // Check to see if the surface is still valid
- if (pixmapData->gl_surface &&
- hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
+ if (haveTFP && pixmapData->gl_surface &&
+ hasAlpha == (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
{
- // Surface is invalid!
- destroyGlSurfaceForPixmap(pixmapData);
+ pixmapHasValidSurface = true;
}
- if (pixmapData->gl_surface == 0) {
- bool success = qt_createEGLSurfaceForPixmap(pixmapData, true);
- if (!success) {
- haveTFP = false;
- return 0;
+ // If we already have a valid EGL surface for the pixmap, we should use it
+ if (pixmapHasValidSurface) {
+ EGLBoolean success;
+ success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER);
+ if (success == EGL_FALSE) {
+ qWarning() << "eglBindTexImage() failed:" << QEgl::errorString();
+ eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
+ pixmapData->gl_surface = (void*)EGL_NO_SURFACE;
+ } else
+ textureIsBound = true;
+ }
+
+ // If the pixmap doesn't already have a valid surface, try binding it via EGLImage
+ // first, as going through EGLImage should be faster and better supported:
+ if (!textureIsBound && haveEglImageTFP) {
+ EGLImageKHR eglImage;
+
+ EGLint attribs[] = {
+ EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
+ EGL_NONE
+ };
+ eglImage = QEgl::eglCreateImageKHR(QEgl::display(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
+ (EGLClientBuffer)QEgl::nativePixmap(pixmap), attribs);
+
+ QGLContext* ctx = q;
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage);
+
+ GLint err = glGetError();
+ if (err == GL_NO_ERROR)
+ textureIsBound = true;
+
+ // Once the egl image is bound, the texture becomes a new sibling image and we can safely
+ // destroy the EGLImage we created for the pixmap:
+ if (eglImage != EGL_NO_IMAGE_KHR)
+ QEgl::eglDestroyImageKHR(QEgl::display(), eglImage);
+ }
+
+ if (!textureIsBound && haveTFP) {
+ // Check to see if the surface is still valid
+ if (pixmapData->gl_surface &&
+ hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
+ {
+ // Surface is invalid!
+ destroyGlSurfaceForPixmap(pixmapData);
}
- }
- Q_ASSERT(pixmapData->gl_surface);
+ if (pixmapData->gl_surface == 0) {
+ EGLConfig config = QEgl::defaultConfig(QInternal::Pixmap,
+ QEgl::OpenGL,
+ hasAlpha ? QEgl::Translucent : QEgl::NoOptions);
- GLuint textureId;
- glGenTextures(1, &textureId);
- glBindTexture(GL_TEXTURE_2D, textureId);
+ pixmapData->gl_surface = (void*)QEgl::createSurface(pixmap, config);
+ if (pixmapData->gl_surface == (void*)EGL_NO_SURFACE)
+ return false;
+ }
- // bind the egl pixmap surface to a texture
- EGLBoolean success;
- success = eglBindTexImage(eglContext->display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER);
- if (success == EGL_FALSE) {
- qWarning() << "eglBindTexImage() failed:" << eglContext->errorString(eglGetError());
- eglDestroySurface(eglContext->display(), (EGLSurface)pixmapData->gl_surface);
- pixmapData->gl_surface = (Qt::HANDLE)EGL_NO_SURFACE;
- haveTFP = false;
- return 0;
+ EGLBoolean success;
+ success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER);
+ if (success == EGL_FALSE) {
+ qWarning() << "eglBindTexImage() failed:" << QEgl::errorString();
+ eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
+ pixmapData->gl_surface = (void*)EGL_NO_SURFACE;
+ haveTFP = false; // If TFP isn't working, disable it's use
+ } else
+ textureIsBound = true;
}
- QGLTexture *texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options);
- pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture;
+ QGLTexture *texture = 0;
- // We assume the cost of bound pixmaps is zero
- QGLTextureCache::instance()->insert(q, key, texture, 0);
+ if (textureIsBound) {
+ texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options);
+ pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture;
+
+ // We assume the cost of bound pixmaps is zero
+ QGLTextureCache::instance()->insert(q, key, texture, 0);
+
+ glBindTexture(GL_TEXTURE_2D, textureId);
+ } else
+ glDeleteTextures(1, &textureId);
- glBindTexture(GL_TEXTURE_2D, textureId);
return texture;
}
+
void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd)
{
Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
if (pixmapData->gl_surface) {
EGLBoolean success;
- success = eglDestroySurface(QEglContext::display(), (EGLSurface)pixmapData->gl_surface);
+ success = eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
if (success == EGL_FALSE) {
qWarning() << "destroyGlSurfaceForPixmap() - Error deleting surface: "
- << QEglContext::errorString(eglGetError());
+ << QEgl::errorString();
}
pixmapData->gl_surface = 0;
}
@@ -772,12 +527,12 @@ void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd)
QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
if (pixmapData->gl_surface) {
EGLBoolean success;
- success = eglReleaseTexImage(QEglContext::display(),
+ success = eglReleaseTexImage(QEgl::display(),
(EGLSurface)pixmapData->gl_surface,
EGL_BACK_BUFFER);
if (success == EGL_FALSE) {
qWarning() << "unbindPixmapFromTexture() - Unable to release bound texture: "
- << QEglContext::errorString(eglGetError());
+ << QEgl::errorString();
}
}
}
diff --git a/src/opengl/qglbuffer.cpp b/src/opengl/qglbuffer.cpp
new file mode 100644
index 0000000..d6e0109
--- /dev/null
+++ b/src/opengl/qglbuffer.cpp
@@ -0,0 +1,576 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtOpenGL/qgl.h>
+#include <QtOpenGL/private/qgl_p.h>
+#include <QtOpenGL/private/qglextensions_p.h>
+#include <QtCore/qatomic.h>
+#include "qglbuffer.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLBuffer
+ \brief The QGLBuffer class provides functions for creating and managing GL buffer objects.
+ \since 4.7
+ \ingroup painting-3D
+
+ Buffer objects are created in the GL server so that the
+ client application can avoid uploading vertices, indices,
+ texture image data, etc every time they are needed.
+
+ QGLBuffer objects can be copied around as a reference to the
+ underlying GL buffer object:
+
+ \code
+ QGLBuffer buffer1(QGLBuffer::IndexBuffer);
+ buffer1.create();
+
+ QGLBuffer buffer2 = buffer1;
+ \endcode
+
+ QGLBuffer performs a shallow copy when objects are copied in this
+ manner, but does not implement copy-on-write semantics. The original
+ object will be affected whenever the copy is modified.
+*/
+
+/*!
+ \enum QGLBuffer::Type
+ This enum defines the type of GL buffer object to create with QGLBuffer.
+
+ \value VertexBuffer Vertex buffer object for use when specifying
+ vertex arrays.
+ \value IndexBuffer Index buffer object for use with \c{glDrawElements()}.
+ \value PixelPackBuffer Pixel pack buffer object for reading pixel
+ data from the GL server (for example, with \c{glReadPixels()}).
+ Not supported under OpenGL/ES.
+ \value PixelUnpackBuffer Pixel unpack buffer object for writing pixel
+ data to the GL server (for example, with \c{glTexImage2D()}).
+ Not supported under OpenGL/ES.
+*/
+
+/*!
+ \enum QGLBuffer::UsagePattern
+ This enum defines the usage pattern of a QGLBuffer object.
+
+ \value StreamDraw The data will be set once and used a few times
+ for drawing operations. Under OpenGL/ES 1.1 this is identical
+ to StaticDraw.
+ \value StreamRead The data will be set once and used a few times
+ for reading data back from the GL server. Not supported
+ under OpenGL/ES.
+ \value StreamCopy The data will be set once and used a few times
+ for reading data back from the GL server for use in further
+ drawing operations. Not supported under OpenGL/ES.
+ \value StaticDraw The data will be set once and used many times
+ for drawing operations.
+ \value StaticRead The data will be set once and used many times
+ for reading data back from the GL server. Not supported
+ under OpenGL/ES.
+ \value StaticCopy The data will be set once and used many times
+ for reading data back from the GL server for use in further
+ drawing operations. Not supported under OpenGL/ES.
+ \value DynamicDraw The data will be modified repeatedly and used
+ many times for drawing operations.
+ \value DynamicRead The data will be modified repeatedly and used
+ many times for reading data back from the GL server.
+ Not supported under OpenGL/ES.
+ \value DynamicCopy The data will be modified repeatedly and used
+ many times for reading data back from the GL server for
+ use in further drawing operations. Not supported under OpenGL/ES.
+*/
+
+/*!
+ \enum QGLBuffer::Access
+ This enum defines the access mode for QGLBuffer::map().
+
+ \value ReadOnly The buffer will be mapped for reading only.
+ \value WriteOnly The buffer will be mapped for writing only.
+ \value ReadWrite The buffer will be mapped for reading and writing.
+*/
+
+class QGLBufferPrivate
+{
+public:
+ QGLBufferPrivate(QGLBuffer::Type t)
+ : ref(1),
+ type(t),
+ guard(0),
+ usagePattern(QGLBuffer::StaticDraw),
+ actualUsagePattern(QGLBuffer::StaticDraw)
+ {
+ }
+
+ QAtomicInt ref;
+ QGLBuffer::Type type;
+ QGLSharedResourceGuard guard;
+ QGLBuffer::UsagePattern usagePattern;
+ QGLBuffer::UsagePattern actualUsagePattern;
+};
+
+/*!
+ Constructs a new buffer object of type QGLBuffer::VertexBuffer.
+
+ Note: this constructor just creates the QGLBuffer instance. The actual
+ buffer object in the GL server is not created until create() is called.
+
+ \sa create()
+*/
+QGLBuffer::QGLBuffer()
+ : d_ptr(new QGLBufferPrivate(QGLBuffer::VertexBuffer))
+{
+}
+
+/*!
+ Constructs a new buffer object of \a type.
+
+ Note: this constructor just creates the QGLBuffer instance. The actual
+ buffer object in the GL server is not created until create() is called.
+
+ \sa create()
+*/
+QGLBuffer::QGLBuffer(QGLBuffer::Type type)
+ : d_ptr(new QGLBufferPrivate(type))
+{
+}
+
+/*!
+ Constructs a shallow copy of \a other.
+
+ Note: QGLBuffer does not implement copy-on-write semantics,
+ so \a other will be affected whenever the copy is modified.
+*/
+QGLBuffer::QGLBuffer(const QGLBuffer &other)
+ : d_ptr(other.d_ptr)
+{
+ d_ptr->ref.ref();
+}
+
+#define ctx d->guard.context()
+
+/*!
+ Destroys this buffer object, including the storage being
+ used in the GL server.
+*/
+QGLBuffer::~QGLBuffer()
+{
+ if (!d_ptr->ref.deref()) {
+ destroy();
+ delete d_ptr;
+ }
+}
+
+/*!
+ Assigns a shallow copy of \a other to this object.
+
+ Note: QGLBuffer does not implement copy-on-write semantics,
+ so \a other will be affected whenever the copy is modified.
+*/
+QGLBuffer &QGLBuffer::operator=(const QGLBuffer &other)
+{
+ if (d_ptr != other.d_ptr) {
+ other.d_ptr->ref.ref();
+ if (!d_ptr->ref.deref())
+ destroy();
+ d_ptr = other.d_ptr;
+ }
+ return *this;
+}
+
+/*!
+ Returns the type of buffer represented by this object.
+*/
+QGLBuffer::Type QGLBuffer::type() const
+{
+ Q_D(const QGLBuffer);
+ return d->type;
+}
+
+/*!
+ Returns the usage pattern for this buffer object.
+ The default value is StaticDraw.
+
+ \sa setUsagePattern()
+*/
+QGLBuffer::UsagePattern QGLBuffer::usagePattern() const
+{
+ Q_D(const QGLBuffer);
+ return d->usagePattern;
+}
+
+/*!
+ Sets the usage pattern for this buffer object to \a value.
+ This function must be called before allocate() or write().
+
+ \sa usagePattern(), allocate(), write()
+*/
+void QGLBuffer::setUsagePattern(QGLBuffer::UsagePattern value)
+{
+ Q_D(QGLBuffer);
+#if defined(QT_OPENGL_ES_1)
+ // OpenGL/ES 1.1 does not support GL_STREAM_DRAW, so use GL_STATIC_DRAW.
+ // OpenGL/ES 2.0 does support GL_STREAM_DRAW.
+ d->usagePattern = value;
+ if (value == StreamDraw)
+ d->actualUsagePattern = StaticDraw;
+ else
+ d->actualUsagePattern = value;
+#else
+ d->usagePattern = d->actualUsagePattern = value;
+#endif
+}
+
+#undef ctx
+
+/*!
+ Creates the buffer object in the GL server. Returns true if
+ the object was created; false otherwise.
+
+ This function must be called with a current QGLContext.
+ The buffer will be bound to and can only be used in
+ that context (or any other context that is shared with it).
+
+ This function will return false if the GL implementation
+ does not support buffers, or there is no current QGLContext.
+
+ \sa isCreated(), allocate(), write(), destroy()
+*/
+bool QGLBuffer::create()
+{
+ Q_D(QGLBuffer);
+ if (d->guard.id())
+ return true;
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (ctx) {
+ if (!qt_resolve_buffer_extensions(const_cast<QGLContext *>(ctx)))
+ return false;
+ GLuint bufferId = 0;
+ glGenBuffers(1, &bufferId);
+ if (bufferId) {
+ d->guard.setContext(ctx);
+ d->guard.setId(bufferId);
+ return true;
+ }
+ }
+ return false;
+}
+
+#define ctx d->guard.context()
+
+/*!
+ Returns true if this buffer has been created; false otherwise.
+
+ \sa create(), destroy()
+*/
+bool QGLBuffer::isCreated() const
+{
+ Q_D(const QGLBuffer);
+ return d->guard.id() != 0;
+}
+
+/*!
+ Destroys this buffer object, including the storage being
+ used in the GL server. All references to the buffer will
+ become invalid.
+*/
+void QGLBuffer::destroy()
+{
+ Q_D(QGLBuffer);
+ GLuint bufferId = d->guard.id();
+ if (bufferId) {
+ // Switch to the original creating context to destroy it.
+ QGLShareContextScope scope(d->guard.context());
+ glDeleteBuffers(1, &bufferId);
+ }
+ d->guard.setId(0);
+ d->guard.setContext(0);
+}
+
+/*!
+ Reads the \a count bytes in this buffer starting at \a offset
+ into \a data. Returns true on success; false if reading from
+ the buffer is not supported. Buffer reading is not supported
+ under OpenGL/ES.
+
+ It is assumed that this buffer has been bound to the current context.
+
+ \sa write(), bind()
+*/
+bool QGLBuffer::read(int offset, void *data, int count)
+{
+#if !defined(QT_OPENGL_ES)
+ Q_D(QGLBuffer);
+ if (!glGetBufferSubData || !d->guard.id())
+ return false;
+ while (glGetError() != GL_NO_ERROR) ; // Clear error state.
+ glGetBufferSubData(d->type, offset, count, data);
+ return glGetError() == GL_NO_ERROR;
+#else
+ Q_UNUSED(offset);
+ Q_UNUSED(data);
+ Q_UNUSED(count);
+ return false;
+#endif
+}
+
+/*!
+ Replaces the \a count bytes of this buffer starting at \a offset
+ with the contents of \a data. Any other bytes in the buffer
+ will be left unmodified.
+
+ It is assumed that create() has been called on this buffer and that
+ it has been bound to the current context.
+
+ \sa create(), read(), allocate()
+*/
+void QGLBuffer::write(int offset, const void *data, int count)
+{
+#ifndef QT_NO_DEBUG
+ if (!isCreated())
+ qWarning("QGLBuffer::allocate(): buffer not created");
+#endif
+ Q_D(QGLBuffer);
+ if (d->guard.id())
+ glBufferSubData(d->type, offset, count, data);
+}
+
+/*!
+ Allocates \a count bytes of space to the buffer, initialized to
+ the contents of \a data. Any previous contents will be removed.
+
+ It is assumed that create() has been called on this buffer and that
+ it has been bound to the current context.
+
+ \sa create(), read(), write()
+*/
+void QGLBuffer::allocate(const void *data, int count)
+{
+#ifndef QT_NO_DEBUG
+ if (!isCreated())
+ qWarning("QGLBuffer::allocate(): buffer not created");
+#endif
+ Q_D(QGLBuffer);
+ if (d->guard.id())
+ glBufferData(d->type, count, data, d->actualUsagePattern);
+}
+
+/*!
+ \fn void QGLBuffer::allocate(int count)
+ \overload
+
+ Allocates \a count bytes of space to the buffer. Any previous
+ contents will be removed.
+
+ It is assumed that create() has been called on this buffer and that
+ it has been bound to the current context.
+
+ \sa create(), write()
+*/
+
+/*!
+ Binds the buffer associated with this object to the current
+ GL context. Returns false if binding was not possible, usually because
+ type() is not supported on this GL implementation.
+
+ The buffer must be bound to the same QGLContext current when create()
+ was called, or to another QGLContext that is sharing with it.
+ Otherwise, false will be returned from this function.
+
+ \sa release(), create()
+*/
+bool QGLBuffer::bind() const
+{
+#ifndef QT_NO_DEBUG
+ if (!isCreated())
+ qWarning("QGLBuffer::bind(): buffer not created");
+#endif
+ Q_D(const QGLBuffer);
+ GLuint bufferId = d->guard.id();
+ if (bufferId) {
+ if (!QGLContext::areSharing(QGLContext::currentContext(),
+ d->guard.context())) {
+#ifndef QT_NO_DEBUG
+ qWarning("QGLBuffer::bind: buffer is not valid in the current context");
+#endif
+ return false;
+ }
+ glBindBuffer(d->type, bufferId);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/*!
+ Releases the buffer associated with this object from the
+ current GL context.
+
+ This function must be called with the same QGLContext current
+ as when bind() was called on the buffer.
+
+ \sa bind()
+*/
+void QGLBuffer::release() const
+{
+#ifndef QT_NO_DEBUG
+ if (!isCreated())
+ qWarning("QGLBuffer::release(): buffer not created");
+#endif
+ Q_D(const QGLBuffer);
+ if (d->guard.id())
+ glBindBuffer(d->type, 0);
+}
+
+#undef ctx
+
+/*!
+ Releases the buffer associated with \a type in the current
+ QGLContext.
+
+ This function is a direct call to \c{glBindBuffer(type, 0)}
+ for use when the caller does not know which QGLBuffer has
+ been bound to the context but wants to make sure that it
+ is released.
+
+ \code
+ QGLBuffer::release(QGLBuffer::VertexBuffer);
+ \endcode
+*/
+void QGLBuffer::release(QGLBuffer::Type type)
+{
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (ctx && qt_resolve_buffer_extensions(const_cast<QGLContext *>(ctx)))
+ glBindBuffer(GLenum(type), 0);
+}
+
+#define ctx d->guard.context()
+
+/*!
+ Returns the GL identifier associated with this buffer; zero if
+ the buffer has not been created.
+
+ \sa isCreated()
+*/
+GLuint QGLBuffer::bufferId() const
+{
+ Q_D(const QGLBuffer);
+ return d->guard.id();
+}
+
+#ifndef GL_BUFFER_SIZE
+#define GL_BUFFER_SIZE 0x8764
+#endif
+
+/*!
+ Returns the size of the data in this buffer, for reading operations.
+ Returns -1 if fetching the buffer size is not supported, or the
+ buffer has not been created.
+
+ It is assumed that this buffer has been bound to the current context.
+
+ \sa isCreated(), bind()
+*/
+int QGLBuffer::size() const
+{
+ Q_D(const QGLBuffer);
+ if (!d->guard.id())
+ return -1;
+ GLint value = -1;
+ glGetBufferParameteriv(d->type, GL_BUFFER_SIZE, &value);
+ return value;
+}
+
+/*!
+ Maps the contents of this buffer into the application's memory
+ space and returns a pointer to it. Returns null if memory
+ mapping is not possible. The \a access parameter indicates the
+ type of access to be performed.
+
+ It is assumed that create() has been called on this buffer and that
+ it has been bound to the current context.
+
+ This function is only supported under OpenGL/ES if the
+ \c{GL_OES_mapbuffer} extension is present.
+
+ \sa unmap(), create(), bind()
+*/
+void *QGLBuffer::map(QGLBuffer::Access access)
+{
+ Q_D(QGLBuffer);
+#ifndef QT_NO_DEBUG
+ if (!isCreated())
+ qWarning("QGLBuffer::map(): buffer not created");
+#endif
+ if (!d->guard.id())
+ return 0;
+ if (!glMapBufferARB)
+ return 0;
+ return glMapBufferARB(d->type, access);
+}
+
+/*!
+ Unmaps the buffer after it was mapped into the application's
+ memory space with a previous call to map(). Returns true if
+ the unmap succeeded; false otherwise.
+
+ It is assumed that this buffer has been bound to the current context,
+ and that it was previously mapped with map().
+
+ This function is only supported under OpenGL/ES if the
+ \c{GL_OES_mapbuffer} extension is present.
+
+ \sa map()
+*/
+bool QGLBuffer::unmap()
+{
+ Q_D(QGLBuffer);
+#ifndef QT_NO_DEBUG
+ if (!isCreated())
+ qWarning("QGLBuffer::unmap(): buffer not created");
+#endif
+ if (!d->guard.id())
+ return false;
+ if (!glUnmapBufferARB)
+ return false;
+ return glUnmapBufferARB(d->type) == GL_TRUE;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglbuffer.h b/src/opengl/qglbuffer.h
new file mode 100644
index 0000000..a1b45ff
--- /dev/null
+++ b/src/opengl/qglbuffer.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLBUFFER_H
+#define QGLBUFFER_H
+
+#include <QtCore/qscopedpointer.h>
+#include <QtOpenGL/qgl.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLBufferPrivate;
+
+class Q_OPENGL_EXPORT QGLBuffer
+{
+public:
+ enum Type
+ {
+ VertexBuffer = 0x8892, // GL_ARRAY_BUFFER
+ IndexBuffer = 0x8893, // GL_ELEMENT_ARRAY_BUFFER
+ PixelPackBuffer = 0x88EB, // GL_PIXEL_PACK_BUFFER
+ PixelUnpackBuffer = 0x88EC // GL_PIXEL_UNPACK_BUFFER
+ };
+
+ QGLBuffer();
+ explicit QGLBuffer(QGLBuffer::Type type);
+ QGLBuffer(const QGLBuffer &other);
+ ~QGLBuffer();
+
+ QGLBuffer &operator=(const QGLBuffer &other);
+
+ enum UsagePattern
+ {
+ StreamDraw = 0x88E0, // GL_STREAM_DRAW
+ StreamRead = 0x88E1, // GL_STREAM_READ
+ StreamCopy = 0x88E2, // GL_STREAM_COPY
+ StaticDraw = 0x88E4, // GL_STATIC_DRAW
+ StaticRead = 0x88E5, // GL_STATIC_READ
+ StaticCopy = 0x88E6, // GL_STATIC_COPY
+ DynamicDraw = 0x88E8, // GL_DYNAMIC_DRAW
+ DynamicRead = 0x88E9, // GL_DYNAMIC_READ
+ DynamicCopy = 0x88EA // GL_DYNAMIC_COPY
+ };
+
+ enum Access
+ {
+ ReadOnly = 0x88B8, // GL_READ_ONLY
+ WriteOnly = 0x88B9, // GL_WRITE_ONLY
+ ReadWrite = 0x88BA // GL_READ_WRITE
+ };
+
+ QGLBuffer::Type type() const;
+
+ QGLBuffer::UsagePattern usagePattern() const;
+ void setUsagePattern(QGLBuffer::UsagePattern value);
+
+ bool create();
+ bool isCreated() const;
+
+ void destroy();
+
+ bool bind() const;
+ void release() const;
+
+ static void release(QGLBuffer::Type type);
+
+ GLuint bufferId() const;
+
+ int size() const;
+
+ bool read(int offset, void *data, int count);
+ void write(int offset, const void *data, int count);
+
+ void allocate(const void *data, int count);
+ inline void allocate(int count) { allocate(0, count); }
+
+ void *map(QGLBuffer::Access access);
+ bool unmap();
+
+private:
+ QGLBufferPrivate *d_ptr;
+
+ Q_DECLARE_PRIVATE(QGLBuffer)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/opengl/qglextensions.cpp b/src/opengl/qglextensions.cpp
index c091191..8e2bbd4 100644
--- a/src/opengl/qglextensions.cpp
+++ b/src/opengl/qglextensions.cpp
@@ -191,35 +191,51 @@ bool qt_resolve_frag_program_extensions(QGLContext *ctx)
bool qt_resolve_buffer_extensions(QGLContext *ctx)
{
- if (glMapBufferARB && glUnmapBufferARB
-#if !defined(QT_OPENGL_ES_2)
- && glBindBuffer && glDeleteBuffers && glGenBuffers && glBufferData
-#endif
- )
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
+ if (glBindBuffer && glDeleteBuffers && glGenBuffers && glBufferData
+ && glBufferSubData && glGetBufferParameteriv)
return true;
+#endif
-#if !defined(QT_OPENGL_ES_2)
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
glBindBuffer = (_glBindBuffer) qt_gl_getProcAddressARB(ctx, "glBindBuffer");
glDeleteBuffers = (_glDeleteBuffers) qt_gl_getProcAddressARB(ctx, "glDeleteBuffers");
glGenBuffers = (_glGenBuffers) qt_gl_getProcAddressARB(ctx, "glGenBuffers");
glBufferData = (_glBufferData) qt_gl_getProcAddressARB(ctx, "glBufferData");
+ glBufferSubData = (_glBufferSubData) qt_gl_getProcAddressARB(ctx, "glBufferSubData");
+ glGetBufferSubData = (_glGetBufferSubData) qt_gl_getProcAddressARB(ctx, "glGetBufferSubData");
+ glGetBufferParameteriv = (_glGetBufferParameteriv) qt_gl_getProcAddressARB(ctx, "glGetBufferParameteriv");
#endif
glMapBufferARB = (_glMapBufferARB) qt_gl_getProcAddressARB(ctx, "glMapBuffer");
glUnmapBufferARB = (_glUnmapBufferARB) qt_gl_getProcAddressARB(ctx, "glUnmapBuffer");
- return glMapBufferARB
- && glUnmapBufferARB
-#if !defined(QT_OPENGL_ES_2)
- && glBindBuffer
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
+ return glBindBuffer
&& glDeleteBuffers
&& glGenBuffers
&& glBufferData
+ && glBufferSubData
+ && glGetBufferParameteriv;
+ // glGetBufferSubData() is optional
+#else
+ return true;
#endif
- ;
}
+#ifndef QT_NO_EGL
+bool qt_resolve_eglimage_gl_extensions(QGLContext *ctx)
+{
+ if (glEGLImageTargetTexture2DOES || glEGLImageTargetRenderbufferStorageOES)
+ return true;
+ glEGLImageTargetTexture2DOES = (_glEGLImageTargetTexture2DOES) ctx->getProcAddress(QLatin1String("glEGLImageTargetTexture2DOES"));
+ glEGLImageTargetRenderbufferStorageOES = (_glEGLImageTargetRenderbufferStorageOES) ctx->getProcAddress(QLatin1String("glEGLImageTargetRenderbufferStorageOES"));
+ return glEGLImageTargetTexture2DOES && glEGLImageTargetRenderbufferStorageOES;
+}
+#endif
+
bool qt_resolve_glsl_extensions(QGLContext *ctx)
{
+
#if defined(QT_OPENGL_ES_2)
// The GLSL shader functions are always present in OpenGL/ES 2.0.
// The only exceptions are glGetProgramBinaryOES and glProgramBinaryOES.
@@ -233,6 +249,12 @@ bool qt_resolve_glsl_extensions(QGLContext *ctx)
if (glCreateShader)
return true;
+ // Geometry shaders are optional...
+ glProgramParameteriEXT = (_glProgramParameteriEXT) ctx->getProcAddress(QLatin1String("glProgramParameteriEXT"));
+ glFramebufferTextureEXT = (_glFramebufferTextureEXT) ctx->getProcAddress(QLatin1String("glFramebufferTextureEXT"));
+ glFramebufferTextureLayerEXT = (_glFramebufferTextureLayerEXT) ctx->getProcAddress(QLatin1String("glFramebufferTextureLayerEXT"));
+ glFramebufferTextureFaceEXT = (_glFramebufferTextureFaceEXT) ctx->getProcAddress(QLatin1String("glFramebufferTextureFaceEXT"));
+
glCreateShader = (_glCreateShader) ctx->getProcAddress(QLatin1String("glCreateShader"));
if (glCreateShader) {
glShaderSource = (_glShaderSource) ctx->getProcAddress(QLatin1String("glShaderSource"));
diff --git a/src/opengl/qglextensions_p.h b/src/opengl/qglextensions_p.h
index b0cb429..6259cca 100644
--- a/src/opengl/qglextensions_p.h
+++ b/src/opengl/qglextensions_p.h
@@ -68,9 +68,15 @@
# define APIENTRYP *
#endif
+#ifndef QT_NO_EGL
+// Needed for EGLImageKHR definition:
+#include <QtGui/private/qegl_p.h>
+#endif
+
#include <QtCore/qglobal.h>
#ifndef GL_ARB_vertex_buffer_object
+typedef ptrdiff_t GLintptrARB;
typedef ptrdiff_t GLsizeiptrARB;
#endif
@@ -78,13 +84,25 @@ typedef ptrdiff_t GLsizeiptrARB;
typedef char GLchar;
#endif
-// ARB_pixel_buffer_object
+// ARB_vertex_buffer_object
typedef void (APIENTRY *_glBindBuffer) (GLenum, GLuint);
typedef void (APIENTRY *_glDeleteBuffers) (GLsizei, const GLuint *);
typedef void (APIENTRY *_glGenBuffers) (GLsizei, GLuint *);
typedef void (APIENTRY *_glBufferData) (GLenum, GLsizeiptrARB, const GLvoid *, GLenum);
+typedef void (APIENTRY *_glBufferSubData) (GLenum, GLintptrARB, GLsizeiptrARB, const GLvoid *);
+typedef void (APIENTRY *_glGetBufferSubData) (GLenum, GLintptrARB, GLsizeiptrARB, GLvoid *);
+typedef void (APIENTRY *_glGetBufferParameteriv) (GLenum, GLenum, GLint *);
typedef GLvoid* (APIENTRY *_glMapBufferARB) (GLenum, GLenum);
typedef GLboolean (APIENTRY *_glUnmapBufferARB) (GLenum);
+// We can call the buffer functions directly in OpenGL/ES 1.1 or higher,
+// but all other platforms need to resolve the extensions.
+#if defined(QT_OPENGL_ES)
+#if defined(GL_OES_VERSION_1_0) && !defined(GL_OES_VERSION_1_1)
+#define QGL_RESOLVE_BUFFER_FUNCS 1
+#endif
+#else
+#define QGL_RESOLVE_BUFFER_FUNCS 1
+#endif
// ARB_fragment_program
typedef void (APIENTRY *_glProgramStringARB) (GLenum, GLenum, GLsizei, const GLvoid *);
@@ -184,10 +202,27 @@ typedef void (APIENTRY *_glBlitFramebufferEXT) (int srcX0, int srcY0, int srcX1,
typedef void (APIENTRY *_glRenderbufferStorageMultisampleEXT) (GLenum target, GLsizei samples,
GLenum internalformat, GLsizei width, GLsizei height);
+// GL_EXT_geometry_shader4
+typedef void (APIENTRY *_glProgramParameteriEXT)(GLuint program, GLenum pname, GLint value);
+typedef void (APIENTRY *_glFramebufferTextureEXT)(GLenum target, GLenum attachment,
+ GLuint texture, GLint level);
+typedef void (APIENTRY *_glFramebufferTextureLayerEXT)(GLenum target, GLenum attachment,
+ GLuint texture, GLint level, GLint layer);
+typedef void (APIENTRY *_glFramebufferTextureFaceEXT)(GLenum target, GLenum attachment,
+ GLuint texture, GLint level, GLenum face);
+
// ARB_texture_compression
typedef void (APIENTRY *_glCompressedTexImage2DARB) (GLenum, GLint, GLenum, GLsizei,
GLsizei, GLint, GLsizei, const GLvoid *);
+#ifndef QT_NO_EGL
+// OES_EGL_image
+// Note: We define these to take EGLImage whereas spec says they take a new GLeglImageOES
+// type, which the EGL image should be cast to.
+typedef void (APIENTRY *_glEGLImageTargetTexture2DOES) (GLenum, EGLImageKHR);
+typedef void (APIENTRY *_glEGLImageTargetRenderbufferStorageOES) (GLenum, EGLImageKHR);
+#endif
+
QT_BEGIN_NAMESPACE
struct QGLExtensionFuncs
@@ -285,19 +320,32 @@ struct QGLExtensionFuncs
qt_glRenderbufferStorageMultisampleEXT = 0;
// Buffer objects:
-#if !defined(QT_OPENGL_ES_2)
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
qt_glBindBuffer = 0;
qt_glDeleteBuffers = 0;
qt_glGenBuffers = 0;
qt_glBufferData = 0;
+ qt_glBufferSubData = 0;
+ qt_glGetBufferSubData = 0;
+ qt_glGetBufferParameteriv = 0;
#endif
qt_glMapBufferARB = 0;
qt_glUnmapBufferARB = 0;
+ qt_glProgramParameteriEXT = 0;
+ qt_glFramebufferTextureEXT = 0;
+ qt_glFramebufferTextureLayerEXT = 0;
+ qt_glFramebufferTextureFaceEXT = 0;
#if !defined(QT_OPENGL_ES)
// Texture compression
qt_glCompressedTexImage2DARB = 0;
#endif
+
+#ifndef QT_NO_EGL
+ // OES_EGL_image
+ qt_glEGLImageTargetTexture2DOES = 0;
+ qt_glEGLImageTargetRenderbufferStorageOES = 0;
+#endif
}
@@ -397,24 +445,47 @@ struct QGLExtensionFuncs
_glRenderbufferStorageMultisampleEXT qt_glRenderbufferStorageMultisampleEXT;
// Buffer objects
-#if !defined(QT_OPENGL_ES_2)
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
_glBindBuffer qt_glBindBuffer;
_glDeleteBuffers qt_glDeleteBuffers;
_glGenBuffers qt_glGenBuffers;
_glBufferData qt_glBufferData;
+ _glBufferSubData qt_glBufferSubData;
+ _glGetBufferSubData qt_glGetBufferSubData;
+ _glGetBufferParameteriv qt_glGetBufferParameteriv;
#endif
_glMapBufferARB qt_glMapBufferARB;
_glUnmapBufferARB qt_glUnmapBufferARB;
+ // Geometry shaders...
+ _glProgramParameteriEXT qt_glProgramParameteriEXT;
+ _glFramebufferTextureEXT qt_glFramebufferTextureEXT;
+ _glFramebufferTextureLayerEXT qt_glFramebufferTextureLayerEXT;
+ _glFramebufferTextureFaceEXT qt_glFramebufferTextureFaceEXT;
#if !defined(QT_OPENGL_ES)
// Texture compression
_glCompressedTexImage2DARB qt_glCompressedTexImage2DARB;
#endif
+
+#ifndef QT_NO_EGL
+ // OES_EGL_image
+ _glEGLImageTargetTexture2DOES qt_glEGLImageTargetTexture2DOES;
+ _glEGLImageTargetRenderbufferStorageOES qt_glEGLImageTargetRenderbufferStorageOES;
+#endif
+
};
// OpenGL constants
+#ifndef GL_ARRAY_BUFFER
+#define GL_ARRAY_BUFFER 0x8892
+#endif
+
+#ifndef GL_STATIC_DRAW
+#define GL_STATIC_DRAW 0x88E4
+#endif
+
/* NV_texture_rectangle */
#ifndef GL_NV_texture_rectangle
#define GL_TEXTURE_RECTANGLE_NV 0x84F5
@@ -428,11 +499,11 @@ struct QGLExtensionFuncs
#endif
#ifndef GL_RGB16
-#define GL_RGB16 32852
+#define GL_RGB16 0x8054
#endif
#ifndef GL_UNSIGNED_SHORT_5_6_5
-#define GL_UNSIGNED_SHORT_5_6_5 33635
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
#endif
#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
@@ -599,6 +670,20 @@ struct QGLExtensionFuncs
#define GL_DECR_WRAP 0x8508
#endif
+#ifndef GL_VERSION_1_5
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define GL_STREAM_DRAW 0x88E0
+#define GL_STREAM_READ 0x88E1
+#define GL_STREAM_COPY 0x88E2
+#define GL_STATIC_DRAW 0x88E4
+#define GL_STATIC_READ 0x88E5
+#define GL_STATIC_COPY 0x88E6
+#define GL_DYNAMIC_DRAW 0x88E8
+#define GL_DYNAMIC_READ 0x88E9
+#define GL_DYNAMIC_COPY 0x88EA
+#endif
+
#ifndef GL_VERSION_2_0
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
@@ -628,6 +713,29 @@ struct QGLExtensionFuncs
#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
#endif
+// Geometry shader defines
+#ifndef GL_GEOMETRY_SHADER_EXT
+# define GL_GEOMETRY_SHADER_EXT 0x8DD9
+# define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA
+# define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB
+# define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC
+# define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29
+# define GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD
+# define GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE
+# define GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B
+# define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF
+# define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0
+# define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1
+# define GL_LINES_ADJACENCY_EXT 0xA
+# define GL_LINE_STRIP_ADJACENCY_EXT 0xB
+# define GL_TRIANGLES_ADJACENCY_EXT 0xC
+# define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0xD
+# define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8
+# define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9
+# define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7
+# define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4
+# define GL_PROGRAM_POINT_SIZE_EXT 0x8642
+#endif
#if !defined(QT_OPENGL_ES_2)
#define glProgramStringARB QGLContextPrivate::extensionFuncs(ctx).qt_glProgramStringARB
@@ -667,11 +775,14 @@ struct QGLExtensionFuncs
// Buffer objects
-#if !defined(QT_OPENGL_ES_2)
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
#define glBindBuffer QGLContextPrivate::extensionFuncs(ctx).qt_glBindBuffer
#define glDeleteBuffers QGLContextPrivate::extensionFuncs(ctx).qt_glDeleteBuffers
#define glGenBuffers QGLContextPrivate::extensionFuncs(ctx).qt_glGenBuffers
#define glBufferData QGLContextPrivate::extensionFuncs(ctx).qt_glBufferData
+#define glBufferSubData QGLContextPrivate::extensionFuncs(ctx).qt_glBufferSubData
+#define glGetBufferSubData QGLContextPrivate::extensionFuncs(ctx).qt_glGetBufferSubData
+#define glGetBufferParameteriv QGLContextPrivate::extensionFuncs(ctx).qt_glGetBufferParameteriv
#endif
#define glMapBufferARB QGLContextPrivate::extensionFuncs(ctx).qt_glMapBufferARB
#define glUnmapBufferARB QGLContextPrivate::extensionFuncs(ctx).qt_glUnmapBufferARB
@@ -745,10 +856,21 @@ struct QGLExtensionFuncs
#define glClearDepth glClearDepthf
#endif
+#define glProgramParameteriEXT QGLContextPrivate::extensionFuncs(ctx).qt_glProgramParameteriEXT
+#define glFramebufferTextureEXT QGLContextPrivate::extensionFuncs(ctx).qt_glFramebufferTextureEXT
+#define glFramebufferTextureLayerEXT QGLContextPrivate::extensionFuncs(ctx).qt_glFramebufferTextureLayerEXT
+#define glFramebufferTextureFaceEXT QGLContextPrivate::extensionFuncs(ctx).qt_glFramebufferTextureFaceEXT
+
#if !defined(QT_OPENGL_ES)
#define glCompressedTexImage2D QGLContextPrivate::extensionFuncs(ctx).qt_glCompressedTexImage2DARB
#endif
+#ifndef QT_NO_EGL
+// OES_EGL_image
+#define glEGLImageTargetTexture2DOES QGLContextPrivate::extensionFuncs(ctx).qt_glEGLImageTargetTexture2DOES
+#define glEGLImageTargetRenderbufferStorageOES QGLContextPrivate::extensionFuncs(ctx).qt_glEGLImageTargetRenderbufferStorageOES
+#endif
+
extern bool qt_resolve_framebufferobject_extensions(QGLContext *ctx);
bool qt_resolve_buffer_extensions(QGLContext *ctx);
@@ -759,6 +881,10 @@ bool qt_resolve_frag_program_extensions(QGLContext *ctx);
bool qt_resolve_glsl_extensions(QGLContext *ctx);
+#ifndef QT_NO_EGL
+Q_OPENGL_EXPORT bool qt_resolve_eglimage_gl_extensions(QGLContext *ctx);
+#endif
+
QT_END_NAMESPACE
#endif // QGL_EXTENSIONS_P_H
diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp
index dd6a3d5..deffc20 100644
--- a/src/opengl/qglframebufferobject.cpp
+++ b/src/opengl/qglframebufferobject.cpp
@@ -44,7 +44,7 @@
#include <qdebug.h>
#include <private/qgl_p.h>
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#if !defined(QT_OPENGL_ES_1)
#include <private/qpaintengineex_opengl2_p.h>
#endif
@@ -56,10 +56,6 @@
#include <qlibrary.h>
#include <qimage.h>
-#ifdef QT_OPENGL_ES_1_CL
-#include "qgl_cl_p.h"
-#endif
-
QT_BEGIN_NAMESPACE
extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool);
@@ -132,7 +128,7 @@ void QGLFramebufferObjectFormat::detach()
attachments, texture target \c GL_TEXTURE_2D, and internal format \c GL_RGBA8.
On OpenGL/ES systems, the default internal format is \c GL_RGBA.
- \sa samples(), attachment(), target(), internalTextureFormat()
+ \sa samples(), attachment(), internalTextureFormat()
*/
QGLFramebufferObjectFormat::QGLFramebufferObjectFormat()
@@ -987,7 +983,7 @@ QImage QGLFramebufferObject::toImage() const
return image;
}
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#if !defined(QT_OPENGL_ES_1)
Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_buffer_2_engine)
#endif
@@ -1002,7 +998,7 @@ QPaintEngine *QGLFramebufferObject::paintEngine() const
if (d->engine)
return d->engine;
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#if !defined(QT_OPENGL_ES_1)
#if !defined (QT_OPENGL_ES_2)
if (qt_gl_preferGL2Engine()) {
#endif
@@ -1028,6 +1024,36 @@ QPaintEngine *QGLFramebufferObject::paintEngine() const
}
/*!
+ \fn bool QGLFramebufferObject::bindDefault()
+ \internal
+
+ Switches rendering back to the default, windowing system provided
+ framebuffer.
+ Returns true upon success, false otherwise.
+
+ \sa bind(), release()
+*/
+bool QGLFramebufferObject::bindDefault()
+{
+ QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
+
+ if (ctx) {
+ bool ext_detected = (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject);
+ if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx)))
+ return false;
+
+ ctx->d_ptr->current_fbo = ctx->d_ptr->default_fbo;
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->default_fbo);
+#ifdef QT_DEBUG
+ } else {
+ qWarning("QGLFramebufferObject::bindDefault() called without current context.");
+#endif
+ }
+
+ return ctx != 0;
+}
+
+/*!
\fn bool QGLFramebufferObject::hasOpenGLFramebufferObjects()
Returns true if the OpenGL \c{GL_EXT_framebuffer_object} extension
diff --git a/src/opengl/qglframebufferobject.h b/src/opengl/qglframebufferobject.h
index 306b6ff..6ff6645 100644
--- a/src/opengl/qglframebufferobject.h
+++ b/src/opengl/qglframebufferobject.h
@@ -108,6 +108,8 @@ public:
QPaintEngine *paintEngine() const;
GLuint handle() const;
+ static bool bindDefault();
+
static bool hasOpenGLFramebufferObjects();
void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D);
diff --git a/src/opengl/qglpaintdevice.cpp b/src/opengl/qglpaintdevice.cpp
index 8ba0108..e1dcbfd 100644
--- a/src/opengl/qglpaintdevice.cpp
+++ b/src/opengl/qglpaintdevice.cpp
@@ -48,14 +48,10 @@
#include <private/qpixmapdata_x11gl_p.h>
#endif
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#if !defined(QT_OPENGL_ES_1)
#include <private/qpixmapdata_gl_p.h>
#endif
-#if defined(QT_OPENGL_ES_1_CL)
-#include "qgl_cl_p.h"
-#endif
-
QT_BEGIN_NAMESPACE
QGLPaintDevice::QGLPaintDevice()
@@ -67,6 +63,22 @@ QGLPaintDevice::~QGLPaintDevice()
{
}
+int QGLPaintDevice::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ switch(metric) {
+ case PdmWidth:
+ return size().width();
+ case PdmHeight:
+ return size().height();
+ case PdmDepth: {
+ const QGLFormat f = format();
+ return f.redBufferSize() + f.greenBufferSize() + f.blueBufferSize() + f.alphaBufferSize();
+ }
+ default:
+ qWarning("QGLPaintDevice::metric() - metric %d not known", metric);
+ return 0;
+ }
+}
void QGLPaintDevice::beginPaint()
{
@@ -163,7 +175,10 @@ void QGLWidgetGLPaintDevice::beginPaint()
float alpha = c.alphaF();
glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha);
}
- glClear(GL_COLOR_BUFFER_BIT);
+ if (context()->d_func()->workaround_needsFullClearOnEveryFrame)
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ else
+ glClear(GL_COLOR_BUFFER_BIT);
}
}
@@ -203,7 +218,7 @@ QGLPaintDevice* QGLPaintDevice::getDevice(QPaintDevice* pd)
glpd = &(static_cast<QGLFramebufferObject*>(pd)->d_func()->glDevice);
break;
case QInternal::Pixmap: {
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#if !defined(QT_OPENGL_ES_1)
QPixmapData* pmd = static_cast<QPixmap*>(pd)->pixmapData();
if (pmd->classId() == QPixmapData::OpenGLClass)
glpd = static_cast<QGLPixmapData*>(pmd)->glDevice();
diff --git a/src/opengl/qglpaintdevice_p.h b/src/opengl/qglpaintdevice_p.h
index 3d669da..04f9c3c 100644
--- a/src/opengl/qglpaintdevice_p.h
+++ b/src/opengl/qglpaintdevice_p.h
@@ -81,6 +81,7 @@ public:
static QGLPaintDevice* getDevice(QPaintDevice*);
protected:
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
GLuint m_previousFBO;
GLuint m_thisFBO;
};
diff --git a/src/opengl/qglpixelbuffer.cpp b/src/opengl/qglpixelbuffer.cpp
index 46f7697..9a8b243 100644
--- a/src/opengl/qglpixelbuffer.cpp
+++ b/src/opengl/qglpixelbuffer.cpp
@@ -78,7 +78,7 @@
#include <QtCore/qglobal.h>
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#if !defined(QT_OPENGL_ES_1)
#include <private/qpaintengineex_opengl2_p.h>
#endif
@@ -137,14 +137,14 @@ void QGLPixelBufferPrivate::common_init(const QSize &size, const QGLFormat &form
#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))
+#elif (defined(Q_WS_X11) && defined(QT_NO_EGL))
qctx->d_func()->cx = ctx;
qctx->d_func()->pbuf = (void *) pbuf;
qctx->d_func()->vi = 0;
#elif defined(Q_WS_MAC)
qctx->d_func()->cx = ctx;
qctx->d_func()->vi = 0;
-#elif defined(QT_OPENGL_ES)
+#elif !defined(QT_NO_EGL)
qctx->d_func()->eglContext = ctx;
qctx->d_func()->eglSurface = pbuf;
#endif
@@ -254,7 +254,7 @@ bool QGLPixelBuffer::doneCurrent()
\sa size()
*/
-#if (defined(Q_WS_X11) || defined(Q_WS_WIN)) && !defined(QT_OPENGL_ES)
+#if (defined(Q_WS_X11) || defined(Q_WS_WIN)) && defined(QT_NO_EGL)
GLuint QGLPixelBuffer::generateDynamicTexture() const
{
Q_D(const QGLPixelBuffer);
@@ -387,7 +387,7 @@ bool QGLPixelBuffer::isValid() const
return !d->invalid;
}
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#if !defined(QT_OPENGL_ES_1)
Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_buffer_2_engine)
#endif
@@ -398,7 +398,7 @@ Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_engine)
/*! \reimp */
QPaintEngine *QGLPixelBuffer::paintEngine() const
{
-#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL)
+#if defined(QT_OPENGL_ES_1)
return qt_buffer_engine();
#elif defined(QT_OPENGL_ES_2)
return qt_buffer_2_engine();
diff --git a/src/opengl/qglpixelbuffer.h b/src/opengl/qglpixelbuffer.h
index 3304dd8..d9c7e3e 100644
--- a/src/opengl/qglpixelbuffer.h
+++ b/src/opengl/qglpixelbuffer.h
@@ -112,6 +112,7 @@ private:
friend class QGLWindowSurface;
friend class QGLPaintDevice;
friend class QGLPBufferGLPaintDevice;
+ friend class QGLContextPrivate;
};
QT_END_NAMESPACE
diff --git a/src/opengl/qglpixelbuffer_egl.cpp b/src/opengl/qglpixelbuffer_egl.cpp
index dbe919d..0b94f5a 100644
--- a/src/opengl/qglpixelbuffer_egl.cpp
+++ b/src/opengl/qglpixelbuffer_egl.cpp
@@ -47,10 +47,6 @@
#include <qimage.h>
#include <private/qgl_p.h>
-#ifdef QT_OPENGL_ES_1_CL
-#include "qgl_cl_p.h"
-#endif
-
QT_BEGIN_NAMESPACE
#ifdef EGL_BIND_TO_TEXTURE_RGBA
@@ -79,14 +75,15 @@ bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidge
ctx->setConfig(shareContext->config());
#if QGL_RENDER_TEXTURE
EGLint value = EGL_FALSE;
- if (ctx->configAttrib(EGL_BIND_TO_TEXTURE_RGBA, &value) && value)
+ if (ctx->configAttrib(EGL_BIND_TO_TEXTURE_RGBA) == EGL_TRUE)
textureFormat = EGL_TEXTURE_RGBA;
- else if (ctx->configAttrib(EGL_BIND_TO_TEXTURE_RGB, &value) && value)
+ else if (ctx->configAttrib(EGL_BIND_TO_TEXTURE_RGB) == EGL_TRUE)
textureFormat = EGL_TEXTURE_RGB;
#endif
} else {
QEglProperties configProps;
- qt_egl_set_format(configProps, QInternal::Pbuffer, f);
+ qt_eglproperties_set_glformat(configProps, f);
+ configProps.setDeviceType(QInternal::Pbuffer);
configProps.setRenderableType(ctx->api());
bool ok = false;
#if QGL_RENDER_TEXTURE
@@ -116,7 +113,7 @@ bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidge
}
// Retrieve the actual format properties.
- qt_egl_update_format(*ctx, format);
+ qt_glformat_from_eglconfig(format, ctx->config());
// Create the attributes needed for the pbuffer.
QEglProperties attribs;
@@ -141,7 +138,7 @@ bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidge
}
#endif
if (pbuf == EGL_NO_SURFACE) {
- qWarning() << "QGLPixelBufferPrivate::init(): Unable to create EGL pbuffer surface:" << QEglContext::errorString(eglGetError());
+ qWarning() << "QGLPixelBufferPrivate::init(): Unable to create EGL pbuffer surface:" << QEgl::errorString();
return false;
}
@@ -208,11 +205,12 @@ GLuint QGLPixelBuffer::generateDynamicTexture() const
bool QGLPixelBuffer::hasOpenGLPbuffers()
{
// See if we have at least 1 configuration that matches the default format.
- EGLDisplay dpy = QEglContext::display();
+ EGLDisplay dpy = QEgl::display();
if (dpy == EGL_NO_DISPLAY)
return false;
QEglProperties configProps;
- qt_egl_set_format(configProps, QInternal::Pbuffer, QGLFormat::defaultFormat());
+ qt_eglproperties_set_glformat(configProps, QGLFormat::defaultFormat());
+ configProps.setDeviceType(QInternal::Pbuffer);
configProps.setRenderableType(QEgl::OpenGL);
do {
EGLConfig cfg = 0;
diff --git a/src/opengl/qglpixelbuffer_p.h b/src/opengl/qglpixelbuffer_p.h
index c85dc5a..2a1f671 100644
--- a/src/opengl/qglpixelbuffer_p.h
+++ b/src/opengl/qglpixelbuffer_p.h
@@ -60,7 +60,7 @@ QT_BEGIN_INCLUDE_NAMESPACE
#include <private/qgl_p.h>
#include <private/qglpaintdevice_p.h>
-#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
+#if defined(Q_WS_X11) && defined(QT_NO_EGL)
#include <GL/glx.h>
// The below is needed to for compilation on HPUX, due to broken GLX
@@ -127,10 +127,8 @@ struct GLXFBConfig {
#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>
+#elif !defined(QT_NO_EGL)
+#include <QtGui/private/qegl_p.h>
#endif
QT_END_INCLUDE_NAMESPACE
@@ -174,7 +172,7 @@ public:
QPointer<QGLWidget> req_shareWidget;
QSize req_size;
-#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
+#if defined(Q_WS_X11) && defined(QT_NO_EGL)
GLXPbuffer pbuf;
GLXContext ctx;
#elif defined(Q_WS_WIN)
@@ -195,7 +193,7 @@ public:
AGLContext share_ctx;
# endif
#endif
-#if defined(QT_OPENGL_ES)
+#ifndef QT_NO_EGL
EGLSurface pbuf;
QEglContext *ctx;
int textureFormat;
diff --git a/src/opengl/qglshaderprogram.cpp b/src/opengl/qglshaderprogram.cpp
index bbfc2d5..c7689b8 100644
--- a/src/opengl/qglshaderprogram.cpp
+++ b/src/opengl/qglshaderprogram.cpp
@@ -50,7 +50,7 @@
QT_BEGIN_NAMESPACE
-#if !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
+#if !defined(QT_OPENGL_ES_1)
/*!
\class QGLShaderProgram
@@ -143,6 +143,8 @@ QT_BEGIN_NAMESPACE
\value Vertex Vertex shader written in the OpenGL Shading Language (GLSL).
\value Fragment Fragment shader written in the OpenGL Shading Language (GLSL).
+ \value Geometry Geometry shaders written in the OpenGL Shading
+ Language (GLSL), based on the GL_EXT_geometry_shader4 extension.
*/
#ifndef GL_FRAGMENT_SHADER
@@ -226,6 +228,8 @@ bool QGLShaderPrivate::create()
GLuint shader;
if (shaderType == QGLShader::Vertex)
shader = glCreateShader(GL_VERTEX_SHADER);
+ else if (shaderType == QGLShader::Geometry)
+ shader = glCreateShader(GL_GEOMETRY_SHADER_EXT);
else
shader = glCreateShader(GL_FRAGMENT_SHADER);
if (!shader) {
@@ -509,6 +513,10 @@ GLuint QGLShader::shaderId() const
return d->shaderGuard.id();
}
+
+
+
+
#undef ctx
#define ctx programGuard.context()
@@ -521,8 +529,9 @@ public:
, linked(false)
, inited(false)
, removingShaders(false)
- , vertexShader(0)
- , fragmentShader(0)
+ , geometryVertexCount(64)
+ , geometryInputType(0)
+ , geometryOutputType(0)
{
}
~QGLShaderProgramPrivate();
@@ -531,11 +540,14 @@ public:
bool linked;
bool inited;
bool removingShaders;
+
+ int geometryVertexCount;
+ GLenum geometryInputType;
+ GLenum geometryOutputType;
+
QString log;
QList<QGLShader *> shaders;
QList<QGLShader *> anonShaders;
- QGLShader *vertexShader;
- QGLShader *fragmentShader;
bool hasShader(QGLShader::ShaderType type) const;
};
@@ -604,6 +616,7 @@ bool QGLShaderProgram::init()
context = QGLContext::currentContext();
d->programGuard.setContext(context);
}
+
if (!context)
return false;
if (qt_resolve_glsl_extensions(const_cast<QGLContext *>(context))) {
@@ -831,6 +844,7 @@ bool QGLShaderProgram::link()
GLuint program = d->programGuard.id();
if (!program)
return false;
+
GLint value;
if (d->shaders.isEmpty()) {
// If there are no explicit shaders, then it is possible that the
@@ -843,6 +857,22 @@ bool QGLShaderProgram::link()
if (d->linked)
return true;
}
+
+ // Set up the geometry shader parameters
+ if (glProgramParameteriEXT) {
+ foreach (QGLShader *shader, d->shaders) {
+ if (shader->shaderType() & QGLShader::Geometry) {
+ glProgramParameteriEXT(program, GL_GEOMETRY_INPUT_TYPE_EXT,
+ d->geometryInputType);
+ glProgramParameteriEXT(program, GL_GEOMETRY_OUTPUT_TYPE_EXT,
+ d->geometryOutputType);
+ glProgramParameteriEXT(program, GL_GEOMETRY_VERTICES_OUT_EXT,
+ d->geometryVertexCount);
+ break;
+ }
+ }
+ }
+
glLinkProgram(program);
value = 0;
glGetProgramiv(program, GL_LINK_STATUS, &value);
@@ -1267,7 +1297,8 @@ void QGLShaderProgram::setAttributeValue(int location, const QColor& value)
Q_D(QGLShaderProgram);
Q_UNUSED(d);
if (location != -1) {
- GLfloat values[4] = {value.redF(), value.greenF(), value.blueF(), value.alphaF()};
+ GLfloat values[4] = {GLfloat(value.redF()), GLfloat(value.greenF()),
+ GLfloat(value.blueF()), GLfloat(value.alphaF())};
glVertexAttrib4fv(location, values);
}
}
@@ -1433,6 +1464,38 @@ void QGLShaderProgram::setAttributeArray
}
/*!
+ Sets an array of vertex \a values on the attribute at \a location
+ in this shader program. The \a stride indicates the number of bytes
+ between vertices. A default \a stride value of zero indicates that
+ the vertices are densely packed in \a values.
+
+ The \a type indicates the type of elements in the \a values array,
+ usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a tupleSize
+ indicates the number of components per vertex: 1, 2, 3, or 4.
+
+ The array will become active when enableAttributeArray() is called
+ on the \a location. Otherwise the value specified with
+ setAttributeValue() for \a location will be used.
+
+ The setAttributeBuffer() function can be used to set the attribute
+ array to an offset within a vertex buffer.
+
+ \sa setAttributeValue(), setUniformValue(), enableAttributeArray()
+ \sa disableAttributeArray(), setAttributeBuffer()
+ \since 4.7
+*/
+void QGLShaderProgram::setAttributeArray
+ (int location, GLenum type, const void *values, int tupleSize, int stride)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ glVertexAttribPointer(location, tupleSize, type, GL_TRUE,
+ stride, values);
+ }
+}
+
+/*!
\overload
Sets an array of vertex \a values on the attribute called \a name
@@ -1518,6 +1581,92 @@ void QGLShaderProgram::setAttributeArray
}
/*!
+ \overload
+
+ Sets an array of vertex \a values on the attribute called \a name
+ in this shader program. The \a stride indicates the number of bytes
+ between vertices. A default \a stride value of zero indicates that
+ the vertices are densely packed in \a values.
+
+ The \a type indicates the type of elements in the \a values array,
+ usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a tupleSize
+ indicates the number of components per vertex: 1, 2, 3, or 4.
+
+ The array will become active when enableAttributeArray() is called
+ on the \a name. Otherwise the value specified with
+ setAttributeValue() for \a name will be used.
+
+ The setAttributeBuffer() function can be used to set the attribute
+ array to an offset within a vertex buffer.
+
+ \sa setAttributeValue(), setUniformValue(), enableAttributeArray()
+ \sa disableAttributeArray(), setAttributeBuffer()
+ \since 4.7
+*/
+void QGLShaderProgram::setAttributeArray
+ (const char *name, GLenum type, const void *values, int tupleSize, int stride)
+{
+ setAttributeArray(attributeLocation(name), type, values, tupleSize, stride);
+}
+
+/*!
+ Sets an array of vertex values on the attribute at \a location in
+ this shader program, starting at a specific \a offset in the
+ currently bound vertex buffer. The \a stride indicates the number
+ of bytes between vertices. A default \a stride value of zero
+ indicates that the vertices are densely packed in the value array.
+
+ The \a type indicates the type of elements in the vertex value
+ array, usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a
+ tupleSize indicates the number of components per vertex: 1, 2, 3,
+ or 4.
+
+ The array will become active when enableAttributeArray() is called
+ on the \a location. Otherwise the value specified with
+ setAttributeValue() for \a location will be used.
+
+ \sa setAttributeArray()
+ \since 4.7
+*/
+void QGLShaderProgram::setAttributeBuffer
+ (int location, GLenum type, int offset, int tupleSize, int stride)
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1) {
+ glVertexAttribPointer(location, tupleSize, type, GL_TRUE, stride,
+ reinterpret_cast<const void *>(offset));
+ }
+}
+
+/*!
+ \overload
+
+ Sets an array of vertex values on the attribute called \a name
+ in this shader program, starting at a specific \a offset in the
+ currently bound vertex buffer. The \a stride indicates the number
+ of bytes between vertices. A default \a stride value of zero
+ indicates that the vertices are densely packed in the value array.
+
+ The \a type indicates the type of elements in the vertex value
+ array, usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a
+ tupleSize indicates the number of components per vertex: 1, 2, 3,
+ or 4.
+
+ The array will become active when enableAttributeArray() is called
+ on the \a name. Otherwise the value specified with
+ setAttributeValue() for \a name will be used.
+
+ \sa setAttributeArray()
+ \since 4.7
+*/
+void QGLShaderProgram::setAttributeBuffer
+ (const char *name, GLenum type, int offset, int tupleSize, int stride)
+{
+ setAttributeBuffer(attributeLocation(name), type, offset, tupleSize, stride);
+}
+
+/*!
Enables the vertex array at \a location in this shader program
so that the value set by setAttributeArray() on \a location
will be used by the shader program.
@@ -1884,7 +2033,8 @@ void QGLShaderProgram::setUniformValue(int location, const QColor& color)
Q_D(QGLShaderProgram);
Q_UNUSED(d);
if (location != -1) {
- GLfloat values[4] = {color.redF(), color.greenF(), color.blueF(), color.alphaF()};
+ GLfloat values[4] = {GLfloat(color.redF()), GLfloat(color.greenF()),
+ GLfloat(color.blueF()), GLfloat(color.alphaF())};
glUniform4fv(location, 1, values);
}
}
@@ -1913,7 +2063,7 @@ void QGLShaderProgram::setUniformValue(int location, const QPoint& point)
Q_D(QGLShaderProgram);
Q_UNUSED(d);
if (location != -1) {
- GLfloat values[4] = {point.x(), point.y()};
+ GLfloat values[4] = {GLfloat(point.x()), GLfloat(point.y())};
glUniform2fv(location, 1, values);
}
}
@@ -1942,7 +2092,7 @@ void QGLShaderProgram::setUniformValue(int location, const QPointF& point)
Q_D(QGLShaderProgram);
Q_UNUSED(d);
if (location != -1) {
- GLfloat values[4] = {point.x(), point.y()};
+ GLfloat values[4] = {GLfloat(point.x()), GLfloat(point.y())};
glUniform2fv(location, 1, values);
}
}
@@ -1971,7 +2121,7 @@ void QGLShaderProgram::setUniformValue(int location, const QSize& size)
Q_D(QGLShaderProgram);
Q_UNUSED(d);
if (location != -1) {
- GLfloat values[4] = {size.width(), size.width()};
+ GLfloat values[4] = {GLfloat(size.width()), GLfloat(size.width())};
glUniform2fv(location, 1, values);
}
}
@@ -2000,7 +2150,7 @@ void QGLShaderProgram::setUniformValue(int location, const QSizeF& size)
Q_D(QGLShaderProgram);
Q_UNUSED(d);
if (location != -1) {
- GLfloat values[4] = {size.width(), size.height()};
+ GLfloat values[4] = {GLfloat(size.width()), GLfloat(size.height())};
glUniform2fv(location, 1, values);
}
}
@@ -2314,6 +2464,42 @@ void QGLShaderProgram::setUniformValue(const char *name, const QMatrix4x4& value
\overload
Sets the uniform variable at \a location in the current context
+ to a 2x2 matrix \a value. The matrix elements must be specified
+ in column-major order.
+
+ \sa setAttributeValue()
+ \since 4.7
+*/
+void QGLShaderProgram::setUniformValue(int location, const GLfloat value[2][2])
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ glUniformMatrix2fv(location, 1, GL_FALSE, value[0]);
+}
+
+/*!
+ \overload
+
+ Sets the uniform variable at \a location in the current context
+ to a 3x3 matrix \a value. The matrix elements must be specified
+ in column-major order.
+
+ \sa setAttributeValue()
+ \since 4.7
+*/
+void QGLShaderProgram::setUniformValue(int location, const GLfloat value[3][3])
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ glUniformMatrix3fv(location, 1, GL_FALSE, value[0]);
+}
+
+/*!
+ \overload
+
+ Sets the uniform variable at \a location in the current context
to a 4x4 matrix \a value. The matrix elements must be specified
in column-major order.
@@ -2327,6 +2513,37 @@ void QGLShaderProgram::setUniformValue(int location, const GLfloat value[4][4])
glUniformMatrix4fv(location, 1, GL_FALSE, value[0]);
}
+
+/*!
+ \overload
+
+ Sets the uniform variable called \a name in the current context
+ to a 2x2 matrix \a value. The matrix elements must be specified
+ in column-major order.
+
+ \sa setAttributeValue()
+ \since 4.7
+*/
+void QGLShaderProgram::setUniformValue(const char *name, const GLfloat value[2][2])
+{
+ setUniformValue(uniformLocation(name), value);
+}
+
+/*!
+ \overload
+
+ Sets the uniform variable called \a name in the current context
+ to a 3x3 matrix \a value. The matrix elements must be specified
+ in column-major order.
+
+ \sa setAttributeValue()
+ \since 4.7
+*/
+void QGLShaderProgram::setUniformValue(const char *name, const GLfloat value[3][3])
+{
+ setUniformValue(uniformLocation(name), value);
+}
+
/*!
\overload
@@ -2354,9 +2571,9 @@ void QGLShaderProgram::setUniformValue(int location, const QTransform& value)
Q_UNUSED(d);
if (location != -1) {
GLfloat mat[3][3] = {
- {value.m11(), value.m12(), value.m13()},
- {value.m21(), value.m22(), value.m23()},
- {value.m31(), value.m32(), value.m33()}
+ {GLfloat(value.m11()), GLfloat(value.m12()), GLfloat(value.m13())},
+ {GLfloat(value.m21()), GLfloat(value.m22()), GLfloat(value.m23())},
+ {GLfloat(value.m31()), GLfloat(value.m32()), GLfloat(value.m33())}
};
glUniformMatrix3fv(location, 1, GL_FALSE, mat[0]);
}
@@ -2869,6 +3086,108 @@ void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x4 *
#undef ctx
/*!
+ Returns the hardware limit for how many vertices a geometry shader
+ can output.
+
+ \since 4.7
+
+ \sa setGeometryOutputVertexCount()
+*/
+int QGLShaderProgram::maxGeometryOutputVertices() const
+{
+ GLint n;
+ glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT, &n);
+ return n;
+}
+
+/*!
+ Sets the maximum number of vertices the current geometry shader
+ program will produce, if active, to \a count.
+
+ \since 4.7
+
+ This parameter takes effect the next time the program is linked.
+*/
+void QGLShaderProgram::setGeometryOutputVertexCount(int count)
+{
+#ifndef QT_NO_DEBUG
+ int max = maxGeometryOutputVertices();
+ if (count > max) {
+ qWarning("QGLShaderProgram::setGeometryOutputVertexCount: count: %d higher than maximum: %d",
+ count, max);
+ }
+#endif
+ d_func()->geometryVertexCount = count;
+}
+
+
+/*!
+ Returns the maximum number of vertices the current geometry shader
+ program will produce, if active.
+
+ \since 4.7
+
+ This parameter takes effect the ntext time the program is linked.
+*/
+int QGLShaderProgram::geometryOutputVertexCount() const
+{
+ return d_func()->geometryVertexCount;
+}
+
+
+/*!
+ Sets the input type from \a inputType.
+
+ This parameter takes effect the next time the program is linked.
+*/
+void QGLShaderProgram::setGeometryInputType(GLenum inputType)
+{
+ d_func()->geometryInputType = inputType;
+}
+
+
+/*!
+ Returns the geometry shader input type, if active.
+
+ This parameter takes effect the next time the program is linked.
+
+ \since 4.7
+ */
+
+GLenum QGLShaderProgram::geometryInputType() const
+{
+ return d_func()->geometryInputType;
+}
+
+
+/*!
+ Sets the output type from the geometry shader, if active, to
+ \a outputType.
+
+ This parameter takes effect the next time the program is linked.
+
+ \since 4.7
+*/
+void QGLShaderProgram::setGeometryOutputType(GLenum outputType)
+{
+ d_func()->geometryOutputType = outputType;
+}
+
+
+/*!
+ Returns the geometry shader output type, if active.
+
+ This parameter takes effect the next time the program is linked.
+
+ \since 4.7
+ */
+GLenum QGLShaderProgram::geometryOutputType() const
+{
+ return d_func()->geometryOutputType;
+}
+
+
+/*!
Returns true if shader programs written in the OpenGL Shading
Language (GLSL) are supported on this system; false otherwise.
@@ -2900,8 +3219,71 @@ void QGLShaderProgram::shaderDestroyed()
removeShader(shader);
}
+
+#undef ctx
+#undef context
+
+/*!
+ Returns true if shader programs of type \a type are supported on
+ this system; false otherwise.
+
+ The \a context is used to resolve the GLSL extensions.
+ If \a context is null, then QGLContext::currentContext() is used.
+
+ \since 4.7
+*/
+bool QGLShader::hasOpenGLShaders(ShaderType type, const QGLContext *context)
+{
+ if (!context)
+ context = QGLContext::currentContext();
+ if (!context)
+ return false;
+
+ if ((type & ~(Geometry | Vertex | Fragment)) || type == 0)
+ return false;
+
+ bool resolved = qt_resolve_glsl_extensions(const_cast<QGLContext *>(context));
+ if (!resolved)
+ return false;
+
+ if ((type & Geometry) && !QByteArray((const char *) glGetString(GL_EXTENSIONS)).contains("GL_EXT_geometry_shader4"))
+ return false;
+
+ return true;
+}
+
+
+
#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
/*! \internal */
+void QGLShaderProgram::setAttributeArray
+ (int location, QMacCompatGLenum type, const void *values, int tupleSize, int stride)
+{
+ setAttributeArray(location, GLenum(type), values, tupleSize, stride);
+}
+
+/*! \internal */
+void QGLShaderProgram::setAttributeArray
+ (const char *name, QMacCompatGLenum type, const void *values, int tupleSize, int stride)
+{
+ setAttributeArray(name, GLenum(type), values, tupleSize, stride);
+}
+
+/*! \internal */
+void QGLShaderProgram::setAttributeBuffer
+ (int location, QMacCompatGLenum type, int offset, int tupleSize, int stride)
+{
+ setAttributeBuffer(location, GLenum(type), offset, tupleSize, stride);
+}
+
+/*! \internal */
+void QGLShaderProgram::setAttributeBuffer
+ (const char *name, QMacCompatGLenum type, int offset, int tupleSize, int stride)
+{
+ setAttributeBuffer(name, GLenum(type), offset, tupleSize, stride);
+}
+
+/*! \internal */
void QGLShaderProgram::setUniformValue(int location, QMacCompatGLint value)
{
setUniformValue(location, GLint(value));
@@ -2950,6 +3332,6 @@ void QGLShaderProgram::setUniformValueArray(const char *name, const QMacCompatGL
}
#endif
-#endif // !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
+#endif // !defined(QT_OPENGL_ES_1)
QT_END_NAMESPACE
diff --git a/src/opengl/qglshaderprogram.h b/src/opengl/qglshaderprogram.h
index 24ab986..d612b05 100644
--- a/src/opengl/qglshaderprogram.h
+++ b/src/opengl/qglshaderprogram.h
@@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE
QT_MODULE(OpenGL)
-#if !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
+#if !defined(QT_OPENGL_ES_1)
class QGLShaderProgram;
class QGLShaderPrivate;
@@ -66,7 +66,8 @@ public:
enum ShaderTypeBit
{
Vertex = 0x0001,
- Fragment = 0x0002
+ Fragment = 0x0002,
+ Geometry = 0x0004
};
Q_DECLARE_FLAGS(ShaderType, ShaderTypeBit)
@@ -88,6 +89,8 @@ public:
GLuint shaderId() const;
+ static bool hasOpenGLShaders(ShaderType type, const QGLContext *context = 0);
+
private:
friend class QGLShaderProgram;
@@ -100,6 +103,14 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QGLShader::ShaderType)
class QGLShaderProgramPrivate;
+#ifndef GL_EXT_geometry_shader4
+# define GL_LINES_ADJACENCY_EXT 0xA
+# define GL_LINE_STRIP_ADJACENCY_EXT 0xB
+# define GL_TRIANGLES_ADJACENCY_EXT 0xC
+# define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0xD
+#endif
+
+
class Q_OPENGL_EXPORT QGLShaderProgram : public QObject
{
Q_OBJECT
@@ -128,6 +139,17 @@ public:
GLuint programId() const;
+ int maxGeometryOutputVertices() const;
+
+ void setGeometryOutputVertexCount(int count);
+ int geometryOutputVertexCount() const;
+
+ void setGeometryInputType(GLenum inputType);
+ GLenum geometryInputType() const;
+
+ void setGeometryOutputType(GLenum outputType);
+ GLenum geometryOutputType() const;
+
void bindAttributeLocation(const char *name, int location);
void bindAttributeLocation(const QByteArray& name, int location);
void bindAttributeLocation(const QString& name, int location);
@@ -165,6 +187,8 @@ public:
void setAttributeArray
(int location, const QVector4D *values, int stride = 0);
void setAttributeArray
+ (int location, GLenum type, const void *values, int tupleSize, int stride = 0);
+ void setAttributeArray
(const char *name, const GLfloat *values, int tupleSize, int stride = 0);
void setAttributeArray
(const char *name, const QVector2D *values, int stride = 0);
@@ -172,6 +196,24 @@ public:
(const char *name, const QVector3D *values, int stride = 0);
void setAttributeArray
(const char *name, const QVector4D *values, int stride = 0);
+ void setAttributeArray
+ (const char *name, GLenum type, const void *values, int tupleSize, int stride = 0);
+
+ void setAttributeBuffer
+ (int location, GLenum type, int offset, int tupleSize, int stride = 0);
+ void setAttributeBuffer
+ (const char *name, GLenum type, int offset, int tupleSize, int stride = 0);
+
+#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
+ void setAttributeArray
+ (int location, QMacCompatGLenum type, const void *values, int tupleSize, int stride = 0);
+ void setAttributeArray
+ (const char *name, QMacCompatGLenum type, const void *values, int tupleSize, int stride = 0);
+ void setAttributeBuffer
+ (int location, QMacCompatGLenum type, int offset, int tupleSize, int stride = 0);
+ void setAttributeBuffer
+ (const char *name, QMacCompatGLenum type, int offset, int tupleSize, int stride = 0);
+#endif
void enableAttributeArray(int location);
void enableAttributeArray(const char *name);
@@ -216,6 +258,8 @@ public:
void setUniformValue(int location, const QMatrix4x2& value);
void setUniformValue(int location, const QMatrix4x3& value);
void setUniformValue(int location, const QMatrix4x4& value);
+ void setUniformValue(int location, const GLfloat value[2][2]);
+ void setUniformValue(int location, const GLfloat value[3][3]);
void setUniformValue(int location, const GLfloat value[4][4]);
void setUniformValue(int location, const QTransform& value);
@@ -242,6 +286,8 @@ public:
void setUniformValue(const char *name, const QMatrix4x2& value);
void setUniformValue(const char *name, const QMatrix4x3& value);
void setUniformValue(const char *name, const QMatrix4x4& value);
+ void setUniformValue(const char *name, const GLfloat value[2][2]);
+ void setUniformValue(const char *name, const GLfloat value[3][3]);
void setUniformValue(const char *name, const GLfloat value[4][4]);
void setUniformValue(const char *name, const QTransform& value);
diff --git a/src/opengl/qgraphicsshadereffect.cpp b/src/opengl/qgraphicsshadereffect.cpp
index dddc85d..f53ef54 100644
--- a/src/opengl/qgraphicsshadereffect.cpp
+++ b/src/opengl/qgraphicsshadereffect.cpp
@@ -40,7 +40,7 @@
****************************************************************************/
#include "qgraphicsshadereffect_p.h"
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#if !defined(QT_OPENGL_ES_1)
#include "qglshaderprogram.h"
#include "gl2paintengineex/qglcustomshaderstage_p.h"
#define QGL_HAVE_CUSTOM_SHADERS 1
diff --git a/src/opengl/qgraphicssystem_gl.cpp b/src/opengl/qgraphicssystem_gl.cpp
index 3a399ae..58cc28a 100644
--- a/src/opengl/qgraphicssystem_gl.cpp
+++ b/src/opengl/qgraphicssystem_gl.cpp
@@ -40,6 +40,7 @@
****************************************************************************/
#include "qgraphicssystem_gl_p.h"
+#include <QGraphicsView>
#include "private/qpixmap_raster_p.h"
#include "private/qpixmapdata_gl_p.h"
@@ -47,7 +48,7 @@
#include "private/qgl_p.h"
#include <private/qwindowsurface_raster_p.h>
-#if defined(Q_WS_X11) && defined(QT_OPENGL_ES)
+#if defined(Q_WS_X11) && !defined(QT_NO_EGL)
#include "private/qpixmapdata_x11gl_p.h"
#include "private/qwindowsurface_x11gl_p.h"
#endif
@@ -58,11 +59,6 @@ extern QGLWidget *qt_gl_getShareWidget();
QPixmapData *QGLGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const
{
-#if defined(Q_WS_X11) && defined(QT_OPENGL_ES)
- if (type == QPixmapData::PixmapType && QX11GLPixmapData::hasX11GLPixmaps())
- return new QX11GLPixmapData();
-#endif
-
return new QGLPixmapData(type);
}
@@ -76,9 +72,18 @@ QWindowSurface *QGLGraphicsSystem::createWindowSurface(QWidget *widget) const
return new QRasterWindowSurface(widget);
#endif
-#if defined(Q_WS_X11) && defined(QT_OPENGL_ES)
- if (QX11GLPixmapData::hasX11GLPixmaps())
- return new QX11GLWindowSurface(widget);
+#if defined(Q_WS_X11) && !defined(QT_NO_EGL)
+ if (m_useX11GL && QX11GLPixmapData::hasX11GLPixmaps()) {
+ // If the widget is a QGraphicsView which will be re-drawing the entire
+ // scene each frame anyway, we should use QGLWindowSurface as this may
+ // provide proper buffer flipping, which should be faster than QX11GL's
+ // blitting approach:
+ QGraphicsView* qgv = qobject_cast<QGraphicsView*>(widget);
+ if (qgv && qgv->viewportUpdateMode() == QGraphicsView::FullViewportUpdate)
+ return new QGLWindowSurface(widget);
+ else
+ return new QX11GLWindowSurface(widget);
+ }
#endif
return new QGLWindowSurface(widget);
diff --git a/src/opengl/qgraphicssystem_gl_p.h b/src/opengl/qgraphicssystem_gl_p.h
index ff47854..9d2d506 100644
--- a/src/opengl/qgraphicssystem_gl_p.h
+++ b/src/opengl/qgraphicssystem_gl_p.h
@@ -62,10 +62,12 @@ QT_BEGIN_NAMESPACE
class Q_OPENGL_EXPORT QGLGraphicsSystem : public QGraphicsSystem
{
public:
- QGLGraphicsSystem();
+ QGLGraphicsSystem(bool useX11GL);
QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
QWindowSurface *createWindowSurface(QWidget *widget) const;
+private:
+ bool m_useX11GL;
};
QT_END_NAMESPACE
diff --git a/src/opengl/qpaintengine_opengl.cpp b/src/opengl/qpaintengine_opengl.cpp
index fc31548..12c487d 100644
--- a/src/opengl/qpaintengine_opengl.cpp
+++ b/src/opengl/qpaintengine_opengl.cpp
@@ -60,6 +60,7 @@
#include <private/qglpixelbuffer_p.h>
#include <private/qbezier_p.h>
#include <qglframebufferobject.h>
+#include <private/qstatictext_p.h>
#include "private/qtessellator_p.h"
@@ -71,10 +72,6 @@
#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 *>(device->context());
#include <stdlib.h>
@@ -671,6 +668,7 @@ public:
, last_created_state(0)
, shader_ctx(0)
, grad_palette(0)
+ , tess_points(0)
, drawable_texture(0)
, ref_cleaner(this)
{}
@@ -780,7 +778,7 @@ public:
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());
+ void composite(GLuint primitive, const GLfloat *vertexArray, int vertexCount, const QPoint &maskOffset = QPoint());
bool createFragmentPrograms();
void deleteFragmentPrograms();
@@ -1792,7 +1790,7 @@ class QOpenGLTrapezoidToArrayTessellator : public QOpenGLTessellator
public:
QOpenGLTrapezoidToArrayTessellator() : vertices(0), allocated(0), size(0) {}
~QOpenGLTrapezoidToArrayTessellator() { free(vertices); }
- q_vertexType *vertices;
+ GLfloat *vertices;
int allocated;
int size;
QRectF bounds;
@@ -1813,36 +1811,36 @@ void QOpenGLTrapezoidToArrayTessellator::addTrap(const Trapezoid &trap)
if (size > allocated - 12) {
#endif
allocated = qMax(2*allocated, 512);
- vertices = (q_vertexType *)realloc(vertices, allocated * sizeof(q_vertexType));
+ vertices = (GLfloat *)realloc(vertices, allocated * sizeof(GLfloat));
}
QGLTrapezoid t = toGLTrapezoid(trap);
#ifndef QT_OPENGL_ES
- vertices[size++] = f2vt(t.topLeftX);
- vertices[size++] = f2vt(t.top);
- vertices[size++] = f2vt(t.topRightX);
- vertices[size++] = f2vt(t.top);
- vertices[size++] = f2vt(t.bottomRightX);
- vertices[size++] = f2vt(t.bottom);
- vertices[size++] = f2vt(t.bottomLeftX);
- vertices[size++] = f2vt(t.bottom);
+ 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++] = f2vt(t.topLeftX);
- vertices[size++] = f2vt(t.top);
- vertices[size++] = f2vt(t.topRightX);
- vertices[size++] = f2vt(t.top);
- vertices[size++] = f2vt(t.bottomRightX);
- vertices[size++] = f2vt(t.bottom);
+ 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++] = f2vt(t.bottomLeftX);
- vertices[size++] = f2vt(t.bottom);
- vertices[size++] = f2vt(t.topLeftX);
- vertices[size++] = f2vt(t.top);
- vertices[size++] = f2vt(t.bottomRightX);
- vertices[size++] = f2vt(t.bottom);
+ 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
}
@@ -1869,7 +1867,7 @@ void QOpenGLPaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, in
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);
+ glVertexPointer(2, GL_FLOAT, 0, tessellator.vertices);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(geometry_mode, 0, tessellator.size/2);
glDisableClientState(GL_VERTEX_ARRAY);
@@ -1958,6 +1956,8 @@ void QOpenGLPaintEnginePrivate::pathToVertexArrays(const QPainterPath &path)
void QOpenGLPaintEnginePrivate::drawVertexArrays()
{
+ if (tess_points_stops.count() == 0)
+ return;
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_DOUBLE, 0, tess_points.data());
int previous_stop = 0;
@@ -2270,7 +2270,7 @@ void QOpenGLPaintEnginePrivate::updateDepthClip()
return;
}
-#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_ES_1_CL)
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
glClearDepthf(0.0f);
#else
glClearDepth(0.0f);
@@ -2286,12 +2286,12 @@ void QOpenGLPaintEnginePrivate::updateDepthClip()
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);
+ QDataBuffer<GLfloat> 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());
+ GLfloat x = GLfloat(rects.at(i).left());
+ GLfloat w = GLfloat(rects.at(i).width());
+ GLfloat h = GLfloat(rects.at(i).height());
+ GLfloat y = GLfloat(rects.at(i).top());
// First triangle
clipVertex.add(x);
@@ -2319,7 +2319,7 @@ void QOpenGLPaintEnginePrivate::updateDepthClip()
glLoadIdentity();
glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(2, q_vertexTypeEnum, 0, clipVertex.data());
+ glVertexPointer(2, GL_FLOAT, 0, clipVertex.data());
glDrawArrays(GL_TRIANGLES, 0, rects.size()*2*3);
glDisableClientState(GL_VERTEX_ARRAY);
@@ -3111,8 +3111,8 @@ QGLTrapezoidMaskGenerator::QGLTrapezoidMaskGenerator(const QPainterPath &path, c
{
}
-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);
+extern void qt_add_rect_to_array(const QRectF &r, GLfloat *array);
+extern void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, GLfloat *array);
void QGLTrapezoidMaskGenerator::drawMask(const QRect &rect)
{
@@ -3143,7 +3143,7 @@ void QGLTrapezoidMaskGenerator::drawMask(const QRect &rect)
// clear mask
glBlendFunc(GL_ZERO, GL_ZERO); // clear
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
@@ -3350,15 +3350,15 @@ void QGLEllipseMaskGenerator::drawMask(const QRect &rect)
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() } };
+ float m[3][4] = { { float(inv_matrix.m11()), float(inv_matrix.m12()), float(inv_matrix.m13()) },
+ { float(inv_matrix.m21()), float(inv_matrix.m22()), float(inv_matrix.m23()) },
+ { float(inv_matrix.m31()), float(inv_matrix.m32()), float(inv_matrix.m33()) } };
QPoint offs(screen_rect.left() - rect.left(), (offscreen->drawableSize().height() - screen_rect.top())
- (offscreen->offscreenSize().height() - rect.top()));
// last component needs to be 1.0f to avoid Nvidia bug on linux
- float ellipse_offset[4] = { offs.x(), offs.y(), 0.0f, 1.0f };
+ float ellipse_offset[4] = { float(offs.x()), float(offs.y()), 0.0f, 1.0f };
GLfloat vertexArray[4 * 2];
qt_add_rect_to_array(rect, vertexArray);
@@ -3374,7 +3374,7 @@ void QGLEllipseMaskGenerator::drawMask(const QRect &rect)
glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_ELLIPSE_OFFSET], ellipse_offset);
glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_FRAGMENT_PROGRAM_ARB);
@@ -3404,7 +3404,7 @@ void QOpenGLPaintEnginePrivate::drawFastRect(const QRectF &r)
Q_Q(QOpenGLPaintEngine);
DEBUG_ONCE_STR("QOpenGLPaintEngine::drawRects(): drawing fast rect");
- q_vertexType vertexArray[10];
+ GLfloat vertexArray[10];
qt_add_rect_to_array(r, vertexArray);
if (has_pen)
@@ -3425,7 +3425,7 @@ void QOpenGLPaintEnginePrivate::drawFastRect(const QRectF &r)
if (fast_style && has_fast_composition_mode) {
glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
} else {
@@ -3444,7 +3444,7 @@ void QOpenGLPaintEnginePrivate::drawFastRect(const QRectF &r)
vertexArray[8] = vertexArray[0];
vertexArray[9] = vertexArray[1];
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_LINE_STRIP, 0, 5);
glDisableClientState(GL_VERTEX_ARRAY);
@@ -3551,7 +3551,7 @@ void QOpenGLPaintEngine::drawRects(const QRectF *rects, int rectCount)
}
}
-static void addQuadAsTriangle(q_vertexType *quad, q_vertexType *triangle)
+static void addQuadAsTriangle(GLfloat *quad, GLfloat *triangle)
{
triangle[0] = quad[0];
triangle[1] = quad[1];
@@ -3612,7 +3612,7 @@ void QOpenGLPaintEngine::drawPoints(const QPointF *points, int pointCount)
d->flushDrawQueue();
if (d->has_fast_pen) {
- QVarLengthArray<q_vertexType> vertexArray(6 * pointCount);
+ QVarLengthArray<GLfloat> vertexArray(6 * pointCount);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
@@ -3622,25 +3622,22 @@ void QOpenGLPaintEngine::drawPoints(const QPointF *points, int pointCount)
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);
+ GLfloat x = GLfloat(qRound(mapped.x()));
+ GLfloat y = GLfloat(qRound(mapped.y()));
vertexArray[j++] = x;
- vertexArray[j++] = y - f2vt(0.5);
+ vertexArray[j++] = y - 0.5f;
- vertexArray[j++] = x + f2vt(1.5);
- vertexArray[j++] = y + f2vt(1.0);
+ vertexArray[j++] = x + 1.5f;
+ vertexArray[j++] = y + 1.0f;
vertexArray[j++] = x;
- vertexArray[j++] = y + f2vt(1.0);
+ vertexArray[j++] = y + 1.0f;
}
glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray.constData());
glDrawArrays(GL_TRIANGLES, 0, pointCount*3);
glDisableClientState(GL_VERTEX_ARRAY);
@@ -3657,7 +3654,7 @@ void QOpenGLPaintEngine::drawPoints(const QPointF *points, int pointCount)
}
else {
Q_ASSERT(sizeof(QPointF) == 8);
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
}
glEnableClientState(GL_VERTEX_ARRAY);
@@ -3725,47 +3722,47 @@ void QOpenGLPaintEngine::drawLines(const QLineF *lines, int lineCount)
}
}
- q_vertexType endCap = f2vt(d->cpen.capStyle() == Qt::FlatCap ? 0 : 0.5);
+ GLfloat endCap = d->cpen.capStyle() == Qt::FlatCap ? 0.0f : 0.5f;
if (useRects) {
- QVarLengthArray<q_vertexType> vertexArray(12 * lineCount);
+ QVarLengthArray<GLfloat> vertexArray(12 * lineCount);
- q_vertexType quad[8];
+ GLfloat 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());
+ GLfloat x1 = lines[i].x1();
+ GLfloat x2 = lines[i].x2();
+ GLfloat y1 = lines[i].y1();
+ GLfloat y2 = lines[i].y2();
if (x1 == x2) {
if (y1 > y2)
qSwap(y1, y2);
- quad[0] = x1 - f2vt(0.5);
+ quad[0] = x1 - 0.5f;
quad[1] = y1 - endCap;
- quad[2] = x1 + f2vt(0.5);
+ quad[2] = x1 + 0.5f;
quad[3] = y1 - endCap;
- quad[4] = x1 + f2vt(0.5);
+ quad[4] = x1 + 0.5f;
quad[5] = y2 + endCap;
- quad[6] = x1 - f2vt(0.5);
+ quad[6] = x1 - 0.5f;
quad[7] = y2 + endCap;
} else {
if (x1 > x2)
qSwap(x1, x2);
quad[0] = x1 - endCap;
- quad[1] = y1 + f2vt(0.5);
+ quad[1] = y1 + 0.5f;
quad[2] = x1 - endCap;
- quad[3] = y1 - f2vt(0.5);
+ quad[3] = y1 - 0.5f;
quad[4] = x2 + endCap;
- quad[5] = y1 - f2vt(0.5);
+ quad[5] = y1 - 0.5f;
quad[6] = x2 + endCap;
- quad[7] = y1 + f2vt(0.5);
+ quad[7] = y1 + 0.5f;
}
addQuadAsTriangle(quad, &vertexArray[12*i]);
@@ -3773,26 +3770,26 @@ void QOpenGLPaintEngine::drawLines(const QLineF *lines, int lineCount)
glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray.constData());
glDrawArrays(GL_TRIANGLES, 0, lineCount*6);
glDisableClientState(GL_VERTEX_ARRAY);
} else {
- QVarLengthArray<q_vertexType> vertexArray(4 * lineCount);
+ QVarLengthArray<GLfloat> 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());
+ vertexArray[4*i] = lines[i].x1();
+ vertexArray[4*i+1] = lines[i].y1();
+ vertexArray[4*i+2] = lines[i].x2();
+ vertexArray[4*i+3] = lines[i].y2();
}
glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray.constData());
glDrawArrays(GL_LINES, 0, lineCount*2);
- glVertexPointer(2, q_vertexTypeEnum, 4*sizeof(q_vertexType), vertexArray.constData() + 2);
+ glVertexPointer(2, GL_FLOAT, 4*sizeof(GLfloat), vertexArray.constData() + 2);
glDrawArrays(GL_POINTS, 0, lineCount);
glDisableClientState(GL_VERTEX_ARRAY);
@@ -3879,7 +3876,7 @@ void QOpenGLPaintEngine::drawPolygon(const QPointF *points, int pointCount, Poly
}
else {
Q_ASSERT(sizeof(QPointF) == 8);
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
}
glEnableClientState(GL_VERTEX_ARRAY);
@@ -3898,12 +3895,12 @@ void QOpenGLPaintEngine::drawPolygon(const QPointF *points, int pointCount, Poly
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());
+ QVarLengthArray<GLfloat> vertexArray(pointCount*2 + 2);
+ glVertexPointer(2, GL_FLOAT, 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());
+ vertexArray[i*2] = points[i].x();
+ vertexArray[i*2+1] = points[i].y();
}
glEnableClientState(GL_VERTEX_ARRAY);
@@ -3958,7 +3955,7 @@ void QOpenGLPaintEnginePrivate::strokeLines(const QPainterPath &path)
enableClipping();
}
-extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
+Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
void QOpenGLPaintEnginePrivate::strokePath(const QPainterPath &path, bool use_cache)
{
@@ -4079,7 +4076,7 @@ void QOpenGLPaintEnginePrivate::strokePathFastPen(const QPainterPath &path, bool
switch (e.type) {
case QPainterPath::MoveToElement:
if (i != 0) {
- glVertexPointer(2, q_vertexTypeEnum, 0, tess_points.data());
+ glVertexPointer(2, GL_FLOAT, 0, tess_points.data());
glDrawArrays(GL_LINE_STRIP, 0, tess_points.size());
tess_points.reset();
}
@@ -4129,7 +4126,7 @@ void QOpenGLPaintEnginePrivate::strokePathFastPen(const QPainterPath &path, bool
break;
} // end of switch
}
- glVertexPointer(2, q_vertexTypeEnum, 0, tess_points.data());
+ glVertexPointer(2, GL_FLOAT, 0, tess_points.data());
glDrawArrays(GL_LINE_STRIP, 0, tess_points.size());
glDisableClientState(GL_VERTEX_ARRAY);
#endif
@@ -4396,8 +4393,8 @@ void QOpenGLPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, con
glRotatef(180.0, 0.0, 0.0, 1.0);
}
- q_vertexType vertexArray[4*2];
- q_vertexType texCoordArray[4*2];
+ GLfloat vertexArray[4*2];
+ GLfloat texCoordArray[4*2];
double offset_x = offset.x() / pm.width();
double offset_y = offset.y() / pm.height();
@@ -4406,8 +4403,8 @@ void QOpenGLPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, con
qt_add_texcoords_to_array(offset_x, offset_y,
tc_w + offset_x, tc_h + offset_y, texCoordArray);
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
- glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -4488,14 +4485,14 @@ void QOpenGLPaintEngine::drawTextureRect(int tx_width, int tx_height, const QRec
y2 = sr.y();
}
- q_vertexType vertexArray[4*2];
- q_vertexType texCoordArray[4*2];
+ GLfloat vertexArray[4*2];
+ GLfloat texCoordArray[4*2];
qt_add_rect_to_array(r, vertexArray);
qt_add_texcoords_to_array(x1, y2, x2, y1, texCoordArray);
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
- glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -4560,7 +4557,7 @@ public:
QGLGlyphCache() : QObject(0) { current_cache = 0; }
~QGLGlyphCache();
QGLGlyphCoord *lookup(QFontEngine *, glyph_t);
- void cacheGlyphs(QGLContext *, const QTextItemInt &, const QVarLengthArray<glyph_t> &);
+ void cacheGlyphs(QGLContext *, QFontEngine *, glyph_t *glyphs, int numGlyphs);
void cleanCache();
void allocTexture(int width, int height, GLuint texture);
@@ -4712,8 +4709,8 @@ static QImage getCurrentTexture(const QColor &color, QGLFontTexture *font_tex)
}
#endif
-void QGLGlyphCache::cacheGlyphs(QGLContext *context, const QTextItemInt &ti,
- const QVarLengthArray<glyph_t> &glyphs)
+void QGLGlyphCache::cacheGlyphs(QGLContext *context, QFontEngine *fontEngine,
+ glyph_t *glyphs, int numGlyphs)
{
QGLContextHash::const_iterator dev_it = qt_context_cache.constFind(context);
QGLFontGlyphHash *font_cache = 0;
@@ -4749,25 +4746,25 @@ void QGLGlyphCache::cacheGlyphs(QGLContext *context, const QTextItemInt &ti,
}
Q_ASSERT(font_cache != 0);
- QGLFontGlyphHash::const_iterator cache_it = font_cache->constFind(ti.fontEngine);
+ QGLFontGlyphHash::const_iterator cache_it = font_cache->constFind(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*)));
+ font_cache->insert(fontEngine, cache);
+ connect(fontEngine, SIGNAL(destroyed(QObject*)), SLOT(fontEngineDestroyed(QObject*)));
} else {
cache = cache_it.value();
}
current_cache = cache;
quint64 font_key = (reinterpret_cast<quint64>(context_key ? context_key : context) << 32)
- | reinterpret_cast<quint64>(ti.fontEngine);
+ | reinterpret_cast<quint64>(fontEngine);
QGLFontTexHash::const_iterator it = qt_font_textures.constFind(font_key);
QGLFontTexture *font_tex;
if (it == qt_font_textures.constEnd()) {
GLuint font_texture;
glGenTextures(1, &font_texture);
- GLint tex_height = qt_next_power_of_two(qRound(ti.ascent.toReal() + ti.descent.toReal())+2);
+ GLint tex_height = qt_next_power_of_two(qRound(fontEngine->ascent().toReal() + fontEngine->descent().toReal())+2);
GLint tex_width = qt_next_power_of_two(tex_height*30); // ###
GLint max_tex_size;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
@@ -4789,16 +4786,16 @@ void QGLGlyphCache::cacheGlyphs(QGLContext *context, const QTextItemInt &ti,
glBindTexture(GL_TEXTURE_2D, font_tex->texture);
}
- for (int i=0; i< glyphs.size(); ++i) {
+ for (int i=0; i< numGlyphs; ++i) {
QGLGlyphHash::const_iterator it = cache->constFind(glyphs[i]);
if (it == cache->constEnd()) {
// render new glyph and put it in the cache
- glyph_metrics_t metrics = ti.fontEngine->boundingBox(glyphs[i]);
+ glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
int glyph_width = qRound(metrics.width.toReal())+2;
- int glyph_height = qRound(ti.ascent.toReal() + ti.descent.toReal())+2;
+ int glyph_height = qRound(fontEngine->ascent().toReal() + fontEngine->descent().toReal())+2;
if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
- int strip_height = qt_next_power_of_two(qRound(ti.ascent.toReal() + ti.descent.toReal())+2);
+ int strip_height = qt_next_power_of_two(qRound(fontEngine->ascent().toReal() + fontEngine->descent().toReal())+2);
font_tex->x_offset = x_margin;
font_tex->y_offset += strip_height;
if (font_tex->y_offset >= font_tex->height) {
@@ -4831,7 +4828,7 @@ void QGLGlyphCache::cacheGlyphs(QGLContext *context, const QTextItemInt &ti,
}
}
- QImage glyph_im(ti.fontEngine->alphaMapForGlyph(glyphs[i]));
+ QImage glyph_im(fontEngine->alphaMapForGlyph(glyphs[i]));
glyph_im = glyph_im.convertToFormat(QImage::Format_Indexed8);
glyph_width = glyph_im.width();
Q_ASSERT(glyph_width >= 0);
@@ -4911,30 +4908,15 @@ void qgl_cleanup_glyph_cache(QGLContext *ctx)
qt_glyph_cache()->cleanupContext(ctx);
}
-void QOpenGLPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+void QOpenGLPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
{
Q_D(QOpenGLPaintEngine);
- const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
-
- // fall back to drawing a polygon if the scale factor is large, or
- // we use a gradient pen
- if ((d->matrix.det() > 1) || (d->pen_brush_style >= Qt::LinearGradientPattern
- && d->pen_brush_style <= Qt::ConicalGradientPattern)) {
- QPaintEngine::drawTextItem(p, textItem);
- return;
- }
-
d->flushDrawQueue();
- // add the glyphs used to the glyph texture cache
- QVarLengthArray<QFixedPoint> positions;
- QVarLengthArray<glyph_t> glyphs;
- QTransform matrix = QTransform::fromTranslate(qRound(p.x()), qRound(p.y()));
- ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
-
// make sure the glyphs we want to draw are in the cache
- qt_glyph_cache()->cacheGlyphs(d->device->context(), ti, glyphs);
+ qt_glyph_cache()->cacheGlyphs(d->device->context(), textItem->fontEngine, textItem->glyphs,
+ textItem->numGlyphs);
d->setGradientOps(Qt::SolidPattern, QRectF()); // turns off gradient ops
qt_glColor4ubv(d->pen_color);
@@ -4948,21 +4930,21 @@ void QOpenGLPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte
#endif
// do the actual drawing
- q_vertexType vertexArray[4*2];
- q_vertexType texCoordArray[4*2];
+ GLfloat vertexArray[4*2];
+ GLfloat texCoordArray[4*2];
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
- glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- bool antialias = !(ti.fontEngine->fontDef.styleStrategy & QFont::NoAntialias)
- && (d->matrix.type() > QTransform::TxTranslate);
+ bool antialias = !(textItem->fontEngine->fontDef.styleStrategy & QFont::NoAntialias)
+ && (d->matrix.type() > QTransform::TxTranslate);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, antialias ? GL_LINEAR : GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, antialias ? GL_LINEAR : GL_NEAREST);
- for (int i=0; i< glyphs.size(); ++i) {
- QGLGlyphCoord *g = qt_glyph_cache()->lookup(ti.fontEngine, glyphs[i]);
+ for (int i=0; i< textItem->numGlyphs; ++i) {
+ QGLGlyphCoord *g = qt_glyph_cache()->lookup(textItem->fontEngine, textItem->glyphs[i]);
// we don't cache glyphs with no width/height
if (!g)
@@ -4974,8 +4956,8 @@ void QOpenGLPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte
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());
+ QPointF logical_pos((textItem->glyphPositions[i].x - g->x_offset).toReal(),
+ (textItem->glyphPositions[i].y + g->y_offset).toReal());
qt_add_rect_to_array(QRectF(logical_pos, QSizeF(g->log_width, g->log_height)), vertexArray);
qt_add_texcoords_to_array(x1, y1, x2, y2, texCoordArray);
@@ -4992,6 +4974,40 @@ void QOpenGLPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte
// XXX: This may not be needed as this behavior does seem to be caused by driver bug
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
#endif
+
+}
+
+void QOpenGLPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QOpenGLPaintEngine);
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+
+ // fall back to drawing a polygon if the scale factor is large, or
+ // we use a gradient pen
+ if ((d->matrix.det() > 1) || (d->pen_brush_style >= Qt::LinearGradientPattern
+ && d->pen_brush_style <= Qt::ConicalGradientPattern)) {
+ QPaintEngine::drawTextItem(p, textItem);
+ return;
+ }
+
+ // add the glyphs used to the glyph texture cache
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix = QTransform::fromTranslate(qRound(p.x()), qRound(p.y()));
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+
+ {
+ QStaticTextItem staticTextItem;
+ staticTextItem.chars = const_cast<QChar *>(ti.chars);
+ staticTextItem.fontEngine = ti.fontEngine;
+ staticTextItem.glyphs = glyphs.data();
+ staticTextItem.numChars = ti.num_chars;
+ staticTextItem.numGlyphs = glyphs.size();
+ staticTextItem.glyphPositions = positions.data();
+ drawStaticTextItem(&staticTextItem);
+ }
+
}
@@ -5165,7 +5181,7 @@ void QOpenGLPaintEnginePrivate::composite(const QRectF &rect, const QPoint &mask
Q_UNUSED(rect);
Q_UNUSED(maskOffset);
#else
- q_vertexType vertexArray[8];
+ GLfloat vertexArray[8];
qt_add_rect_to_array(rect, vertexArray);
composite(GL_TRIANGLE_FAN, vertexArray, 4, maskOffset);
@@ -5173,7 +5189,7 @@ void QOpenGLPaintEnginePrivate::composite(const QRectF &rect, const QPoint &mask
}
-void QOpenGLPaintEnginePrivate::composite(GLuint primitive, const q_vertexType *vertexArray, int vertexCount, const QPoint &maskOffset)
+void QOpenGLPaintEnginePrivate::composite(GLuint primitive, const GLfloat *vertexArray, int vertexCount, const QPoint &maskOffset)
{
#ifdef QT_OPENGL_ES
Q_UNUSED(primitive);
@@ -5196,8 +5212,8 @@ void QOpenGLPaintEnginePrivate::composite(GLuint primitive, const q_vertexType *
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 x = vertexArray[2 * i];
+ qreal y = vertexArray[2 * i + 1];
qreal tx, ty;
matrix.map(x, y, &tx, &ty);
@@ -5256,7 +5272,7 @@ void QOpenGLPaintEnginePrivate::composite(GLuint primitive, const q_vertexType *
}
glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
GLuint program = qt_gl_program_cache()->getProgram(device->context(),
fragment_brush,
diff --git a/src/opengl/qpaintengine_opengl_p.h b/src/opengl/qpaintengine_opengl_p.h
index de0086a..55f7792 100644
--- a/src/opengl/qpaintengine_opengl_p.h
+++ b/src/opengl/qpaintengine_opengl_p.h
@@ -133,6 +133,7 @@ public:
void drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
Qt::ImageConversionFlags conversionFlags);
void drawTextItem(const QPointF &p, const QTextItem &ti);
+ void drawStaticTextItem(QStaticTextItem *staticTextItem);
void drawEllipse(const QRectF &rect);
diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h
index c239bcb..736a28e 100644
--- a/src/opengl/qpixmapdata_gl_p.h
+++ b/src/opengl/qpixmapdata_gl_p.h
@@ -96,7 +96,7 @@ private:
};
-class QGLPixmapData : public QPixmapData
+class Q_OPENGL_EXPORT QGLPixmapData : public QPixmapData
{
public:
QGLPixmapData(PixelType type);
diff --git a/src/opengl/qpixmapdata_x11gl_egl.cpp b/src/opengl/qpixmapdata_x11gl_egl.cpp
index 229f75a..2c11a0b 100644
--- a/src/opengl/qpixmapdata_x11gl_egl.cpp
+++ b/src/opengl/qpixmapdata_x11gl_egl.cpp
@@ -41,149 +41,194 @@
#include <QDebug>
-#include <private/qgl_p.h>
-#include <private/qegl_p.h>
-#include <private/qeglproperties_p.h>
+#include <QtGui/private/qt_x11_p.h>
+#include <QtGui/private/qegl_p.h>
+#include <QtGui/private/qeglproperties_p.h>
+#include <QtGui/private/qeglcontext_p.h>
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
-#include <private/qpaintengineex_opengl2_p.h>
+#if !defined(QT_OPENGL_ES_1)
+#include <QtOpenGL/private/qpaintengineex_opengl2_p.h>
#endif
#ifndef QT_OPENGL_ES_2
-#include <private/qpaintengine_opengl_p.h>
+#include <QtOpenGL/private/qpaintengine_opengl_p.h>
#endif
+#include <QtOpenGL/private/qgl_p.h>
+#include <QtOpenGL/private/qgl_egl_p.h>
+
#include "qpixmapdata_x11gl_p.h"
QT_BEGIN_NAMESPACE
-extern EGLConfig qt_chooseEGLConfigForPixmap(bool hasAlpha, bool readOnly); // in qgl_x11egl.cpp
-extern bool qt_createEGLSurfaceForPixmap(QPixmapData* pmd, bool readOnly); // in qgl_x11egl.cpp
-
-// On 16bpp systems, RGB & ARGB pixmaps are different bit-depths and therefore need
-// different contexts:
-static EGLContext qPixmapARGBSharedEglContext = EGL_NO_CONTEXT;
-static EGLContext qPixmapRGBSharedEglContext = EGL_NO_CONTEXT;
-bool QX11GLPixmapData::hasX11GLPixmaps()
+class QX11GLSharedContexts
{
- static bool checkedForX11Pixmaps = false;
- static bool haveX11Pixmaps = false;
+public:
+ QX11GLSharedContexts()
+ : rgbContext(0)
+ , argbContext(0)
+ , sharedQGLContext(0)
+ , sharePixmap(0)
+ {
+ EGLint rgbConfigId;
+ EGLint argbConfigId;
+
+ do {
+ EGLConfig rgbConfig = QEgl::defaultConfig(QInternal::Pixmap, QEgl::OpenGL, QEgl::Renderable);
+ EGLConfig argbConfig = QEgl::defaultConfig(QInternal::Pixmap, QEgl::OpenGL,
+ QEgl::Renderable | QEgl::Translucent);
+
+ eglGetConfigAttrib(QEgl::display(), rgbConfig, EGL_CONFIG_ID, &rgbConfigId);
+ eglGetConfigAttrib(QEgl::display(), argbConfig, EGL_CONFIG_ID, &argbConfigId);
+
+ rgbContext = new QEglContext;
+ rgbContext->setConfig(rgbConfig);
+ rgbContext->createContext();
+
+ if (!rgbContext->isValid())
+ break;
- if (checkedForX11Pixmaps)
- return haveX11Pixmaps;
+ // If the RGB & ARGB configs are the same, use the same egl context for both:
+ if (rgbConfig == argbConfig)
+ argbContext = rgbContext;
+
+ // Otherwise, create a seperate context to be used for ARGB pixmaps:
+ if (!argbContext) {
+ argbContext = new QEglContext;
+ argbContext->setConfig(argbConfig);
+ bool success = argbContext->createContext(rgbContext);
+ if (!success) {
+ qWarning("QX11GLPixmapData - RGB & ARGB contexts aren't shared");
+ success = argbContext->createContext();
+ if (!success)
+ argbContext = rgbContext; // Might work, worth a shot at least.
+ }
+ }
- checkedForX11Pixmaps = true;
+ if (!argbContext->isValid())
+ break;
- QX11PixmapData *argbPixmapData = 0;
- QX11PixmapData *rgbPixmapData = 0;
- do {
- if (qgetenv("QT_USE_X11GL_PIXMAPS").isEmpty())
- break;
+ // Create the pixmap which will be used to create the egl surface for the share QGLContext
+ QX11PixmapData *rgbPixmapData = new QX11PixmapData(QPixmapData::PixmapType);
+ rgbPixmapData->resize(8, 8);
+ rgbPixmapData->fill(Qt::red);
+ sharePixmap = new QPixmap(rgbPixmapData);
+ EGLSurface sharePixmapSurface = QEgl::createSurface(sharePixmap, rgbConfig);
+ rgbPixmapData->gl_surface = (void*)sharePixmapSurface;
+
+ // Create the actual QGLContext which will be used for sharing
+ sharedQGLContext = new QGLContext(QX11GLPixmapData::glFormat());
+ sharedQGLContext->d_func()->eglContext = rgbContext;
+ sharedQGLContext->d_func()->eglSurface = sharePixmapSurface;
+ sharedQGLContext->d_func()->valid = true;
+ qt_glformat_from_eglconfig(sharedQGLContext->d_func()->glFormat, rgbConfig);
+
+
+ valid = rgbContext->makeCurrent(sharePixmapSurface);
+
+ // If the ARGB & RGB configs are different, check ARGB works too:
+ if (argbConfig != rgbConfig) {
+ QX11PixmapData *argbPixmapData = new QX11PixmapData(QPixmapData::PixmapType);
+ argbPixmapData->resize(8, 8);
+ argbPixmapData->fill(Qt::transparent); // Force ARGB
+ QPixmap argbPixmap(argbPixmapData); // destroys pixmap data when goes out of scope
+ EGLSurface argbPixmapSurface = QEgl::createSurface(&argbPixmap, argbConfig);
+ valid = argbContext->makeCurrent(argbPixmapSurface);
+ argbContext->doneCurrent();
+ eglDestroySurface(QEgl::display(), argbPixmapSurface);
+ argbPixmapData->gl_surface = 0;
+ }
- // Check we actually have EGL configs which support pixmaps
- EGLConfig argbConfig = qt_chooseEGLConfigForPixmap(true, false);
- EGLConfig rgbConfig = qt_chooseEGLConfigForPixmap(false, false);
+ if (!valid) {
+ qWarning() << "Unable to make pixmap surface current:" << QEgl::errorString();
+ break;
+ }
- if (argbConfig == 0 || rgbConfig == 0)
- break;
+ // The pixmap surface destruction hooks are installed by QGLTextureCache, so we
+ // must make sure this is instanciated:
+ QGLTextureCache::instance();
+ } while(0);
- // Create the shared contexts:
- eglBindAPI(EGL_OPENGL_ES_API);
- EGLint contextAttribs[] = {
-#if defined(QT_OPENGL_ES_2)
- EGL_CONTEXT_CLIENT_VERSION, 2,
-#endif
- EGL_NONE
- };
- qPixmapARGBSharedEglContext = eglCreateContext(QEglContext::display(),
- argbConfig, 0, contextAttribs);
-
- if (argbConfig == rgbConfig) {
- // If the configs are the same, we can re-use the same context.
- qPixmapRGBSharedEglContext = qPixmapARGBSharedEglContext;
- } else {
- qPixmapRGBSharedEglContext = eglCreateContext(QEglContext::display(),
- rgbConfig, 0, contextAttribs);
- }
+ if (!valid)
+ cleanup();
+ else
+ qDebug("Using QX11GLPixmapData with EGL config %d for ARGB and config %d for RGB", argbConfigId, rgbConfigId);
- argbPixmapData = new QX11PixmapData(QPixmapData::PixmapType);
- argbPixmapData->resize(100, 100);
- argbPixmapData->fill(Qt::transparent); // Force ARGB
-
- if (!qt_createEGLSurfaceForPixmap(argbPixmapData, false))
- break;
-
- haveX11Pixmaps = eglMakeCurrent(QEglContext::display(),
- (EGLSurface)argbPixmapData->gl_surface,
- (EGLSurface)argbPixmapData->gl_surface,
- qPixmapARGBSharedEglContext);
- if (!haveX11Pixmaps) {
- EGLint err = eglGetError();
- qWarning() << "Unable to make pixmap config current:" << err << QEglContext::errorString(err);
- break;
- }
+ }
- // If the ARGB & RGB configs are the same, we don't need to check RGB too
- if (haveX11Pixmaps && (argbConfig != rgbConfig)) {
- rgbPixmapData = new QX11PixmapData(QPixmapData::PixmapType);
- rgbPixmapData->resize(100, 100);
- rgbPixmapData->fill(Qt::red);
+ ~QX11GLSharedContexts() {
+ cleanup();
+ }
- // Try to actually create an EGL pixmap surface
- if (!qt_createEGLSurfaceForPixmap(rgbPixmapData, false))
- break;
+ void cleanup() {
+ if (sharedQGLContext) {
+ delete sharedQGLContext;
+ sharedQGLContext = 0;
+ }
+ if (argbContext && argbContext != rgbContext)
+ delete argbContext;
+ argbContext = 0;
- haveX11Pixmaps = eglMakeCurrent(QEglContext::display(),
- (EGLSurface)rgbPixmapData->gl_surface,
- (EGLSurface)rgbPixmapData->gl_surface,
- qPixmapRGBSharedEglContext);
- if (!haveX11Pixmaps) {
- EGLint err = eglGetError();
- qWarning() << "Unable to make pixmap config current:" << err << QEglContext::errorString(err);
- break;
- }
+ if (rgbContext) {
+ delete rgbContext;
+ rgbContext = 0;
}
- } while (0);
- if (qPixmapARGBSharedEglContext || qPixmapRGBSharedEglContext) {
- eglMakeCurrent(QEglContext::display(),
- EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ // Deleting the QPixmap will fire the pixmap destruction cleanup hooks which in turn
+ // will destroy the egl surface:
+ if (sharePixmap) {
+ delete sharePixmap;
+ sharePixmap = 0;
+ }
}
- if (argbPixmapData) {
- if (argbPixmapData->gl_surface)
- QGLContextPrivate::destroyGlSurfaceForPixmap(argbPixmapData);
- delete argbPixmapData;
- argbPixmapData = 0;
- }
- if (rgbPixmapData) {
- if (rgbPixmapData->gl_surface)
- QGLContextPrivate::destroyGlSurfaceForPixmap(rgbPixmapData);
- delete rgbPixmapData;
- rgbPixmapData = 0;
- }
+ bool isValid() { return valid;}
- if (!haveX11Pixmaps) {
- // Clean up the context(s) if we can't use X11GL pixmaps
- if (qPixmapARGBSharedEglContext != EGL_NO_CONTEXT)
- eglDestroyContext(QEglContext::display(), qPixmapARGBSharedEglContext);
+ // On 16bpp systems, RGB & ARGB pixmaps are different bit-depths and therefore need
+ // different contexts:
+ QEglContext *rgbContext;
+ QEglContext *argbContext;
- if (qPixmapRGBSharedEglContext != qPixmapARGBSharedEglContext &&
- qPixmapRGBSharedEglContext != EGL_NO_CONTEXT)
- {
- eglDestroyContext(QEglContext::display(), qPixmapRGBSharedEglContext);
- }
- qPixmapRGBSharedEglContext = EGL_NO_CONTEXT;
- qPixmapARGBSharedEglContext = EGL_NO_CONTEXT;
- }
+ // The share context wraps the rgbContext and is used as the master of the context share
+ // group. As all other contexts will have the same egl context (or a shared one if rgb != argb)
+ // all QGLContexts will actually be sharing and can be in the same context group.
+ QGLContext *sharedQGLContext;
+private:
+ QPixmap *sharePixmap;
+ bool valid;
+};
+
+static void qt_cleanup_x11gl_share_contexts();
+
+Q_GLOBAL_STATIC_WITH_INITIALIZER(QX11GLSharedContexts, qt_x11gl_share_contexts,
+ {
+ qAddPostRoutine(qt_cleanup_x11gl_share_contexts);
+ })
+
+static void qt_cleanup_x11gl_share_contexts()
+{
+ qt_x11gl_share_contexts()->cleanup();
+}
- if (haveX11Pixmaps)
- qDebug("QX11GLPixmapData is supported");
- else
- qDebug("QX11GLPixmapData is *NOT* being used");
- return haveX11Pixmaps;
+QX11GLSharedContexts* QX11GLPixmapData::sharedContexts()
+{
+ return qt_x11gl_share_contexts();
+}
+
+bool QX11GLPixmapData::hasX11GLPixmaps()
+{
+ static bool checkedForX11GLPixmaps = false;
+ static bool haveX11GLPixmaps = false;
+
+ if (checkedForX11GLPixmaps)
+ return haveX11GLPixmaps;
+
+ haveX11GLPixmaps = qt_x11gl_share_contexts()->isValid();
+ checkedForX11GLPixmaps = true;
+
+ return haveX11GLPixmaps;
}
QX11GLPixmapData::QX11GLPixmapData()
@@ -194,9 +239,65 @@ QX11GLPixmapData::QX11GLPixmapData()
QX11GLPixmapData::~QX11GLPixmapData()
{
+ if (ctx)
+ delete ctx;
}
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+
+void QX11GLPixmapData::fill(const QColor &color)
+{
+ if (ctx) {
+ ctx->makeCurrent();
+ glFinish();
+ eglWaitClient();
+ }
+
+ QX11PixmapData::fill(color);
+ XSync(X11->display, False);
+
+ if (ctx) {
+ ctx->makeCurrent();
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+ }
+}
+
+void QX11GLPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (ctx) {
+ ctx->makeCurrent();
+ glFinish();
+ eglWaitClient();
+ }
+
+ QX11PixmapData::copy(data, rect);
+ XSync(X11->display, False);
+
+ if (ctx) {
+ ctx->makeCurrent();
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+ }
+}
+
+bool QX11GLPixmapData::scroll(int dx, int dy, const QRect &rect)
+{
+ if (ctx) {
+ ctx->makeCurrent();
+ glFinish();
+ eglWaitClient();
+ }
+
+ bool success = QX11PixmapData::scroll(dx, dy, rect);
+ XSync(X11->display, False);
+
+ if (ctx) {
+ ctx->makeCurrent();
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+ }
+
+ return success;
+}
+
+#if !defined(QT_OPENGL_ES_1)
Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_pixmap_2_engine)
#endif
@@ -210,16 +311,23 @@ QPaintEngine* QX11GLPixmapData::paintEngine() const
// We need to create the context before beginPaint - do it here:
if (!ctx) {
ctx = new QGLContext(glFormat());
- if (ctx->d_func()->eglContext == 0)
- ctx->d_func()->eglContext = new QEglContext();
- ctx->d_func()->eglContext->setApi(QEgl::OpenGL);
- ctx->d_func()->eglContext->setContext(hasAlphaChannel() ? qPixmapARGBSharedEglContext
- : qPixmapRGBSharedEglContext);
+ Q_ASSERT(ctx->d_func()->eglContext == 0);
+ ctx->d_func()->eglContext = hasAlphaChannel() ? sharedContexts()->argbContext : sharedContexts()->rgbContext;
+
+ // While we use a seperate QGLContext for each pixmap, the underlying QEglContext is
+ // the same. So we must use a "fake" QGLContext and fool the texture cache into thinking
+ // each pixmap's QGLContext is sharing with this central one. The only place this is
+ // going to fail is where we the underlying EGL RGB and ARGB contexts aren't sharing.
+ ctx->d_func()->sharing = true;
+ QGLContextGroup::addShare(ctx, sharedContexts()->sharedQGLContext);
+
+ // Update the glFormat for the QGLContext:
+ qt_glformat_from_eglconfig(ctx->d_func()->glFormat, ctx->d_func()->eglContext->config());
}
QPaintEngine* engine;
-#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL)
+#if defined(QT_OPENGL_ES_1)
engine = qt_gl_pixmap_engine();
#elif defined(QT_OPENGL_ES_2)
engine = qt_gl_pixmap_2_engine();
@@ -236,7 +344,7 @@ QPaintEngine* QX11GLPixmapData::paintEngine() const
if (engine->isActive()) {
qWarning("Pixmap paint engine already active");
-#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL)
+#if defined(QT_OPENGL_ES_1)
engine = new QOpenGLPaintEngine;
#elif defined(QT_OPENGL_ES_2)
engine = new QGL2PaintEngineEx;
@@ -257,20 +365,25 @@ QPaintEngine* QX11GLPixmapData::paintEngine() const
void QX11GLPixmapData::beginPaint()
{
// qDebug("QX11GLPixmapData::beginPaint()");
+ // TODO: Check to see if the surface is renderable
if ((EGLSurface)gl_surface == EGL_NO_SURFACE) {
- qt_createEGLSurfaceForPixmap(this, false);
- ctx->d_func()->eglSurface = (EGLSurface)gl_surface;
- ctx->d_func()->valid = true; // ;-)
+ QPixmap tmpPixmap(this);
+ EGLConfig cfg = ctx->d_func()->eglContext->config();
+ Q_ASSERT(cfg != QEGL_NO_CONFIG);
+
+// qDebug("QX11GLPixmapData - using EGL Config ID %d", ctx->d_func()->eglContext->configAttrib(EGL_CONFIG_ID));
+ EGLSurface surface = QEgl::createSurface(&tmpPixmap, cfg);
+ if (surface == EGL_NO_SURFACE) {
+ qWarning() << "Error creating EGL surface for pixmap:" << QEgl::errorString();
+ return;
+ }
+ gl_surface = (void*)surface;
+ ctx->d_func()->eglSurface = surface;
+ ctx->d_func()->valid = true;
}
QGLPaintDevice::beginPaint();
}
-void QX11GLPixmapData::endPaint()
-{
- glFinish();
- QGLPaintDevice::endPaint();
-}
-
QGLContext* QX11GLPixmapData::context() const
{
return ctx;
diff --git a/src/opengl/qpixmapdata_x11gl_p.h b/src/opengl/qpixmapdata_x11gl_p.h
index c9f4f56..2d1336b 100644
--- a/src/opengl/qpixmapdata_x11gl_p.h
+++ b/src/opengl/qpixmapdata_x11gl_p.h
@@ -59,23 +59,35 @@
#include <qgl.h>
+#ifndef QT_NO_EGL
+#include <QtGui/private/qeglcontext_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
+class QX11GLSharedContexts;
+
class QX11GLPixmapData : public QX11PixmapData, public QGLPaintDevice
{
public:
QX11GLPixmapData();
virtual ~QX11GLPixmapData();
+ // Re-implemented from QX11PixmapData:
+ void fill(const QColor &color);
+ void copy(const QPixmapData *data, const QRect &rect);
+ bool scroll(int dx, int dy, const QRect &rect);
+
// Re-implemented from QGLPaintDevice
QPaintEngine* paintEngine() const; // Also re-implements QX11PixmapData::paintEngine
void beginPaint();
- void endPaint();
QGLContext* context() const;
QSize size() const;
static bool hasX11GLPixmaps();
static QGLFormat glFormat();
+ static QX11GLSharedContexts* sharedContexts();
+
private:
mutable QGLContext* ctx;
};
diff --git a/src/opengl/qwindowsurface_gl.cpp b/src/opengl/qwindowsurface_gl.cpp
index 7a565e6..e9da452 100644
--- a/src/opengl/qwindowsurface_gl.cpp
+++ b/src/opengl/qwindowsurface_gl.cpp
@@ -82,12 +82,8 @@
#define GLX_SAMPLES_ARB 100001
#endif
-#ifdef QT_OPENGL_ES_1_CL
-#include "qgl_cl_p.h"
-#endif
-
-#ifdef QT_OPENGL_ES
-#include <private/qegl_p.h>
+#ifndef QT_NO_EGL
+#include <private/qeglcontext_p.h>
#endif
QT_BEGIN_NAMESPACE
@@ -98,8 +94,8 @@ QT_BEGIN_NAMESPACE
#ifdef Q_WS_WIN
extern Q_GUI_EXPORT bool qt_win_owndc_required;
#endif
-QGLGraphicsSystem::QGLGraphicsSystem()
- : QGraphicsSystem()
+QGLGraphicsSystem::QGLGraphicsSystem(bool useX11GL)
+ : QGraphicsSystem(), m_useX11GL(useX11GL)
{
#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
// only override the system defaults if the user hasn't already
@@ -357,16 +353,23 @@ void QGLWindowSurface::hijackWindow(QWidget *widget)
QGLContext *ctx = new QGLContext(surfaceFormat, widget);
ctx->create(qt_gl_share_widget()->context());
-#if defined(Q_WS_X11) && defined(QT_OPENGL_ES)
- // Create the EGL surface to draw into. QGLContext::chooseContext()
- // does not do this for X11/EGL, but does do it for other platforms.
- // This probably belongs in qgl_x11egl.cpp.
- QGLContextPrivate *ctxpriv = ctx->d_func();
- ctxpriv->eglSurface = ctxpriv->eglContext->createSurface(widget);
- if (ctxpriv->eglSurface == EGL_NO_SURFACE) {
- qWarning() << "hijackWindow() could not create EGL surface";
+#ifndef QT_NO_EGL
+ static bool checkedForNOKSwapRegion = false;
+ static bool haveNOKSwapRegion = false;
+
+ if (!checkedForNOKSwapRegion) {
+ haveNOKSwapRegion = QEgl::hasExtension("EGL_NOK_swap_region2");
+ checkedForNOKSwapRegion = true;
+
+ if (haveNOKSwapRegion)
+ qDebug() << "Found EGL_NOK_swap_region2 extension. Using partial updates.";
}
- qDebug("QGLWindowSurface - using EGLConfig %d", reinterpret_cast<int>(ctxpriv->eglContext->config()));
+
+ if (ctx->d_func()->eglContext->configAttrib(EGL_SWAP_BEHAVIOR) != EGL_BUFFER_PRESERVED &&
+ ! haveNOKSwapRegion)
+ setPartialUpdateSupport(false); // Force full-screen updates
+ else
+ setPartialUpdateSupport(true);
#endif
widgetPrivate->extraData()->glContext = ctx;
@@ -491,8 +494,14 @@ void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &
}
}
#endif
+ if (d_ptr->paintedRegion.boundingRect() != geometry()) {
+ // Emits warning if not supported. Should never happen unless
+ // setPartialUpdateSupport(true) has been called.
+ context()->d_func()->swapRegion(&d_ptr->paintedRegion);
+ } else
+ context()->swapBuffers();
+
d_ptr->paintedRegion = QRegion();
- context()->swapBuffers();
} else {
glFlush();
}
@@ -838,22 +847,22 @@ static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize,
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());
+ const GLfloat tx1 = src.left();
+ const GLfloat tx2 = src.right();
+ const GLfloat ty1 = src.top();
+ const GLfloat ty2 = src.bottom();
- q_vertexType texCoordArray[4*2] = {
+ GLfloat 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
+ GLfloat vertexArray[4*2];
+ extern void qt_add_rect_to_array(const QRectF &r, GLfloat *array); // qpaintengine_opengl.cpp
qt_add_rect_to_array(rect, vertexArray);
#if !defined(QT_OPENGL_ES_2)
- glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
- glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
+ glVertexPointer(2, GL_FLOAT, 0, vertexArray);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
glBindTexture(target, tex_id);
glEnable(target);
diff --git a/src/opengl/qwindowsurface_gl_p.h b/src/opengl/qwindowsurface_gl_p.h
index 8ea714c..5e670fe 100644
--- a/src/opengl/qwindowsurface_gl_p.h
+++ b/src/opengl/qwindowsurface_gl_p.h
@@ -66,6 +66,8 @@ class QRegion;
class QWidget;
struct QGLWindowSurfacePrivate;
+Q_OPENGL_EXPORT QGLWidget* qt_gl_share_widget();
+
class QGLWindowSurfaceGLPaintDevice : public QGLPaintDevice
{
public:
@@ -77,7 +79,7 @@ public:
QGLWindowSurfacePrivate* d;
};
-class QGLWindowSurface : public QObject, public QWindowSurface // , public QPaintDevice
+class Q_OPENGL_EXPORT QGLWindowSurface : public QObject, public QWindowSurface // , public QPaintDevice
{
Q_OBJECT
public:
diff --git a/src/opengl/qwindowsurface_x11gl.cpp b/src/opengl/qwindowsurface_x11gl.cpp
index 27b91ba..3de6cae 100644
--- a/src/opengl/qwindowsurface_x11gl.cpp
+++ b/src/opengl/qwindowsurface_x11gl.cpp
@@ -51,14 +51,16 @@
QT_BEGIN_NAMESPACE
QX11GLWindowSurface::QX11GLWindowSurface(QWidget* window)
- : QWindowSurface(window), m_GC(0), m_window(window)
+ : QWindowSurface(window), m_windowGC(0), m_pixmapGC(0), m_window(window)
{
}
QX11GLWindowSurface::~QX11GLWindowSurface()
{
- if (m_GC)
- XFree(m_GC);
+ if (m_windowGC)
+ XFree(m_windowGC);
+ if (m_pixmapGC)
+ XFree(m_pixmapGC);
}
QPaintDevice *QX11GLWindowSurface::paintDevice()
@@ -70,78 +72,142 @@ extern void *qt_getClipRects(const QRegion &r, int &num); // in qpaintengine_x11
void QX11GLWindowSurface::flush(QWidget *widget, const QRegion &widgetRegion, const QPoint &offset)
{
-// qDebug("QX11GLWindowSurface::flush()");
- QTime startTime = QTime::currentTime();
+ // We don't need to know the widget which initiated the flush. Instead we just use the offset
+ // to translate the widgetRegion:
+ Q_UNUSED(widget);
+
if (m_backBuffer.isNull()) {
- qDebug("QHarmattanWindowSurface::flush() - backBuffer is null, not flushing anything");
+ qDebug("QX11GLWindowSurface::flush() - backBuffer is null, not flushing anything");
return;
}
- QPoint widgetOffset = qt_qwidget_data(widget)->wrect.topLeft();
- QRegion windowRegion(widgetRegion);
- QRect boundingRect = widgetRegion.boundingRect();
- if (!widgetOffset.isNull())
- windowRegion.translate(-widgetOffset);
- QRect windowBoundingRect = windowRegion.boundingRect();
+ Q_ASSERT(window()->size() != m_backBuffer.size());
+
+ // Wait for all GL rendering to the back buffer pixmap to complete before trying to
+ // copy it to the window. We do this by making sure the pixmap's context is current
+ // and then call eglWaitClient. The EGL 1.4 spec says eglWaitClient doesn't have to
+ // block, just that "All rendering calls...are guaranteed to be executed before native
+ // rendering calls". This makes it potentially less expensive than glFinish.
+ QGLContext* ctx = static_cast<QX11GLPixmapData*>(m_backBuffer.data_ptr().data())->context();
+ if (QGLContext::currentContext() != ctx && ctx && ctx->isValid())
+ ctx->makeCurrent();
+ eglWaitClient();
+
+ if (m_windowGC == 0) {
+ XGCValues attribs;
+ attribs.graphics_exposures = False;
+ m_windowGC = XCreateGC(X11->display, m_window->handle(), GCGraphicsExposures, &attribs);
+ }
int rectCount;
- XRectangle *rects = (XRectangle *)qt_getClipRects(windowRegion, rectCount);
+ XRectangle *rects = (XRectangle *)qt_getClipRects(widgetRegion, rectCount);
if (rectCount <= 0)
return;
-// qDebug() << "XSetClipRectangles";
-// for (int i = 0; i < num; ++i)
-// qDebug() << ' ' << i << rects[i].x << rects[i].x << rects[i].y << rects[i].width << rects[i].height;
- if (m_GC == 0) {
- m_GC = XCreateGC(X11->display, m_window->handle(), 0, 0);
- XSetGraphicsExposures(X11->display, m_GC, False);
- }
+ XSetClipRectangles(X11->display, m_windowGC, 0, 0, rects, rectCount, YXBanded);
+
+ QRect dirtyRect = widgetRegion.boundingRect().translated(-offset);
+ XCopyArea(X11->display, m_backBuffer.handle(), m_window->handle(), m_windowGC,
+ dirtyRect.x(), dirtyRect.y(), dirtyRect.width(), dirtyRect.height(),
+ dirtyRect.x(), dirtyRect.y());
- XSetClipRectangles(X11->display, m_GC, 0, 0, rects, rectCount, YXBanded);
- XCopyArea(X11->display, m_backBuffer.handle(), m_window->handle(), m_GC,
- boundingRect.x() + offset.x(), boundingRect.y() + offset.y(),
- boundingRect.width(), boundingRect.height(),
- windowBoundingRect.x(), windowBoundingRect.y());
+ // Make sure the blit of the update from the back buffer to the window completes
+ // before allowing rendering to start again to the back buffer. Otherwise the GPU
+ // might start rendering to the back buffer again while the blit takes place.
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
}
void QX11GLWindowSurface::setGeometry(const QRect &rect)
{
if (rect.width() > m_backBuffer.size().width() || rect.height() > m_backBuffer.size().height()) {
- QSize newSize = rect.size();
-// QSize newSize(1024,512);
- qDebug() << "QX11GLWindowSurface::setGeometry() - creating a pixmap of size" << newSize;
QX11GLPixmapData *pd = new QX11GLPixmapData;
+ QSize newSize = rect.size();
pd->resize(newSize.width(), newSize.height());
m_backBuffer = QPixmap(pd);
+ if (window()->testAttribute(Qt::WA_TranslucentBackground))
+ m_backBuffer.fill(Qt::transparent);
+ if (m_pixmapGC) {
+ XFreeGC(X11->display, m_pixmapGC);
+ m_pixmapGC = 0;
+ }
}
-// if (gc)
-// XFreeGC(X11->display, gc);
-// gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
-// XSetGraphicsExposures(X11->display, gc, False);
QWindowSurface::setGeometry(rect);
}
bool QX11GLWindowSurface::scroll(const QRegion &area, int dx, int dy)
{
- Q_UNUSED(area);
- Q_UNUSED(dx);
- Q_UNUSED(dy);
- return false;
-}
+ if (m_backBuffer.isNull())
+ return false;
-/*
-void QX11GLWindowSurface::beginPaint(const QRegion &region)
-{
-}
+ Q_ASSERT(m_backBuffer.data_ptr()->classId() == QPixmapData::X11Class);
-void QX11GLWindowSurface::endPaint(const QRegion &region)
-{
+ // Make sure all GL rendering is complete before starting the scroll operation:
+ QGLContext* ctx = static_cast<QX11GLPixmapData*>(m_backBuffer.data_ptr().data())->context();
+ if (QGLContext::currentContext() != ctx && ctx && ctx->isValid())
+ ctx->makeCurrent();
+ eglWaitClient();
+
+ if (!m_pixmapGC)
+ m_pixmapGC = XCreateGC(X11->display, m_backBuffer.handle(), 0, 0);
+
+ foreach (const QRect& rect, area.rects()) {
+ XCopyArea(X11->display, m_backBuffer.handle(), m_backBuffer.handle(), m_pixmapGC,
+ rect.x(), rect.y(), rect.width(), rect.height(),
+ rect.x()+dx, rect.y()+dy);
+ }
+
+ // Make sure the scroll operation is complete before allowing GL rendering to resume
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+
+ return true;
}
-QImage *QX11GLWindowSurface::buffer(const QWidget *widget)
+
+QPixmap QX11GLWindowSurface::grabWidget(const QWidget *widget, const QRect& rect) const
{
+ if (!widget || m_backBuffer.isNull())
+ return QPixmap();
+
+ QRect srcRect;
+
+ // make sure the rect is inside the widget & clip to widget's rect
+ if (!rect.isEmpty())
+ srcRect = rect & widget->rect();
+ else
+ srcRect = widget->rect();
+
+ if (srcRect.isEmpty())
+ return QPixmap();
+
+ // If it's a child widget we have to translate the coordinates
+ if (widget != window())
+ srcRect.translate(widget->mapTo(window(), QPoint(0, 0)));
+
+ QPixmap::x11SetDefaultScreen(widget->x11Info().screen());
+
+ QX11PixmapData *pmd = new QX11PixmapData(QPixmapData::PixmapType);
+ pmd->resize(srcRect.width(), srcRect.height());
+ QPixmap px(pmd);
+
+ GC tmpGc = XCreateGC(X11->display, m_backBuffer.handle(), 0, 0);
+
+ // Make sure all GL rendering is complete before copying the window
+ QGLContext* ctx = static_cast<QX11GLPixmapData*>(m_backBuffer.pixmapData())->context();
+ if (QGLContext::currentContext() != ctx && ctx && ctx->isValid())
+ ctx->makeCurrent();
+ eglWaitClient();
+
+ // Copy srcRect from the backing store to the new pixmap
+ XSetGraphicsExposures(X11->display, tmpGc, False);
+ XCopyArea(X11->display, m_backBuffer.handle(), px.handle(), tmpGc,
+ srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(), 0, 0);
+ XFreeGC(X11->display, tmpGc);
+
+ // Wait until the copy has finised before allowing more rendering into the back buffer
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+
+ return px;
}
-*/
QT_END_NAMESPACE
diff --git a/src/opengl/qwindowsurface_x11gl_p.h b/src/opengl/qwindowsurface_x11gl_p.h
index 90f3ad5..4d493d0 100644
--- a/src/opengl/qwindowsurface_x11gl_p.h
+++ b/src/opengl/qwindowsurface_x11gl_p.h
@@ -68,9 +68,11 @@ public:
void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
void setGeometry(const QRect &rect);
bool scroll(const QRegion &area, int dx, int dy);
+ QPixmap grabWidget(const QWidget *widget, const QRect& rectangle = QRect()) const;
private:
- GC m_GC;
+ GC m_windowGC;
+ GC m_pixmapGC;
QPixmap m_backBuffer;
QWidget *m_window;
};