summaryrefslogtreecommitdiffstats
path: root/src/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'src/opengl')
-rw-r--r--src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp37
-rw-r--r--src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h5
-rw-r--r--src/opengl/gl2paintengineex/qglcustomshaderstage_p.h2
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager.cpp73
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager_p.h16
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadersource_p.h20
-rw-r--r--src/opengl/gl2paintengineex/qglgradientcache.cpp6
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp657
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h62
-rw-r--r--src/opengl/qgl.cpp300
-rw-r--r--src/opengl/qgl.h2
-rw-r--r--src/opengl/qgl_egl.cpp17
-rw-r--r--src/opengl/qgl_p.h198
-rw-r--r--src/opengl/qgl_x11egl.cpp345
-rw-r--r--src/opengl/qglframebufferobject.cpp64
-rw-r--r--src/opengl/qglframebufferobject_p.h7
-rw-r--r--src/opengl/qglpixelbuffer_egl.cpp15
-rw-r--r--src/opengl/qglpixmapfilter.cpp185
-rw-r--r--src/opengl/qglshaderprogram.cpp8
-rw-r--r--src/opengl/qpaintengine_opengl.cpp12
-rw-r--r--src/opengl/qpixmapdata_gl.cpp23
-rw-r--r--src/opengl/qwindowsurface_gl.cpp6
-rw-r--r--src/opengl/util/fragmentprograms_p.h8
23 files changed, 1290 insertions, 778 deletions
diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp
index 866d1a2..1fe3999 100644
--- a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp
+++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp
@@ -67,7 +67,30 @@ void QGL2PEXVertexArray::addRect(const QRectF &rect)
<< rect.bottomRight() << rect.bottomLeft() << rect.topLeft();
}
-void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseScale)
+void QGL2PEXVertexArray::addClosingLine(int index)
+{
+ if (QPointF(vertexArray.at(index)) != QPointF(vertexArray.last()))
+ vertexArray.add(vertexArray.at(index));
+}
+
+void QGL2PEXVertexArray::addCentroid(const QVectorPath &path, int subPathIndex)
+{
+ const QPointF *const points = reinterpret_cast<const QPointF *>(path.points());
+ const QPainterPath::ElementType *const elements = path.elements();
+
+ QPointF sum = points[subPathIndex];
+ int count = 1;
+
+ for (int i = subPathIndex + 1; i < path.elementCount() && (!elements || elements[i] != QPainterPath::MoveToElement); ++i) {
+ sum += points[i];
+ ++count;
+ }
+
+ const QPointF centroid = sum / qreal(count);
+ vertexArray.add(centroid);
+}
+
+void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline)
{
const QPointF* const points = reinterpret_cast<const QPointF*>(path.points());
const QPainterPath::ElementType* const elements = path.elements();
@@ -78,6 +101,10 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc
boundingRectDirty = false;
}
+ if (!outline)
+ addCentroid(path, 0);
+
+ int lastMoveTo = vertexArray.size();
vertexArray.add(points[0]); // The first element is always a moveTo
do {
@@ -96,8 +123,14 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc
const QPainterPath::ElementType elementType = elements[i];
switch (elementType) {
case QPainterPath::MoveToElement:
+ if (!outline)
+ addClosingLine(lastMoveTo);
// qDebug("element[%d] is a MoveToElement", i);
vertexArrayStops.append(vertexArray.size());
+ if (!outline) {
+ addCentroid(path, i);
+ lastMoveTo = vertexArray.size();
+ }
lineToArray(points[i].x(), points[i].y()); // Add the moveTo as a new vertex
break;
case QPainterPath::LineToElement:
@@ -115,6 +148,8 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc
}
} while (0);
+ if (!outline)
+ addClosingLine(lastMoveTo);
vertexArrayStops.append(vertexArray.size());
}
diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
index 08ce234..719904f 100644
--- a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
+++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
@@ -104,7 +104,7 @@ public:
boundingRectDirty(true) {}
void addRect(const QRectF &rect);
- void addPath(const QVectorPath &path, GLfloat curveInverseScale);
+ void addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline = true);
void clear();
QGLPoint* data() {return vertexArray.data();}
@@ -124,6 +124,9 @@ private:
bool boundingRectDirty;
inline void curveToArray(const QGLPoint &cp1, const QGLPoint &cp2, const QGLPoint &ep, GLfloat inverseScale);
+
+ void addClosingLine(int index);
+ void addCentroid(const QVectorPath &path, int subPathIndex);
};
QT_END_NAMESPACE
diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h
index 25f5c2f..f8c13c5 100644
--- a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h
+++ b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h
@@ -64,7 +64,7 @@ QT_MODULE(OpenGL)
class QGLCustomShaderStagePrivate;
class Q_OPENGL_EXPORT QGLCustomShaderStage
{
- Q_DECLARE_PRIVATE(QGLCustomShaderStage);
+ Q_DECLARE_PRIVATE(QGLCustomShaderStage)
public:
QGLCustomShaderStage();
virtual ~QGLCustomShaderStage();
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
index eceed06..e22303d 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
@@ -60,12 +60,8 @@ QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLConte
{
QGLEngineSharedShaders *p = reinterpret_cast<QGLEngineSharedShaders *>(qt_shared_shaders()->value(context));
if (!p) {
- QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
- if (oldContext != context)
- const_cast<QGLContext *>(context)->makeCurrent();
+ QGLShareContextScope scope(context);
qt_shared_shaders()->insert(context, p = new QGLEngineSharedShaders(context));
- if (oldContext && oldContext != context)
- oldContext->makeCurrent();
}
return p;
}
@@ -78,7 +74,7 @@ const char* QGLEngineSharedShaders::qglEngineShaderSourceCode[] = {
};
QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
- : ctx(QGLContextPrivate::contextGroup(context))
+ : ctxGuard(context)
, blitShaderProg(0)
, simpleShaderProg(0)
{
@@ -97,6 +93,7 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
code[MainVertexShader] = qglslMainVertexShader;
code[MainWithTexCoordsVertexShader] = qglslMainWithTexCoordsVertexShader;
+ code[MainWithTexCoordsAndOpacityVertexShader] = qglslMainWithTexCoordsAndOpacityVertexShader;
code[UntransformedPositionVertexShader] = qglslUntransformedPositionVertexShader;
code[PositionOnlyVertexShader] = qglslPositionOnlyVertexShader;
@@ -119,6 +116,7 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
code[MainFragmentShader_C] = qglslMainFragmentShader_C;
code[MainFragmentShader_O] = qglslMainFragmentShader_O;
code[MainFragmentShader] = qglslMainFragmentShader;
+ code[MainFragmentShader_ImageArrays] = qglslMainFragmentShader_ImageArrays;
code[ImageSrcFragmentShader] = qglslImageSrcFragmentShader;
code[ImageSrcWithPatternFragmentShader] = qglslImageSrcWithPatternFragmentShader;
@@ -223,7 +221,7 @@ QGLShader *QGLEngineSharedShaders::compileNamedShader(ShaderName name, QGLShader
return compiledShaders[name];
QByteArray source = qglEngineShaderSourceCode[name];
- QGLShader *newShader = new QGLShader(type, ctx->context(), this);
+ QGLShader *newShader = new QGLShader(type, ctxGuard.context(), this);
newShader->compile(source);
#if defined(QT_DEBUG)
@@ -245,7 +243,7 @@ QGLShader *QGLEngineSharedShaders::compileCustomShader(QGLCustomShaderStage *sta
if (newShader)
return newShader;
- newShader = new QGLShader(type, ctx->context(), this);
+ newShader = new QGLShader(type, ctxGuard.context(), this);
newShader->compile(source);
customShaderCache.insert(source, newShader);
@@ -273,7 +271,7 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS
QGLEngineShaderProg &cached = cachedPrograms.last();
// If the shader program's not found in the cache, create it now.
- cached.program = new QGLShaderProgram(ctx->context(), this);
+ cached.program = new QGLShaderProgram(ctxGuard.context(), this);
cached.program->addShader(cached.mainVertexShader);
cached.program->addShader(cached.positionVertexShader);
cached.program->addShader(cached.mainFragShader);
@@ -285,6 +283,8 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS
cached.program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
if (cached.useTextureCoords)
cached.program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
+ if (cached.useOpacityAttribute)
+ cached.program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR);
cached.program->link();
if (!cached.program->isLinked()) {
@@ -331,7 +331,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context)
: ctx(context),
shaderProgNeedsChanging(true),
srcPixelType(Qt::NoBrush),
- useGlobalOpacity(false),
+ opacityMode(NoOpacity),
maskType(NoMask),
compositionMode(QPainter::CompositionMode_SourceOver),
customSrcStage(0),
@@ -407,12 +407,12 @@ void QGLEngineShaderManager::setSrcPixelType(PixelSrcType type)
shaderProgNeedsChanging = true; //###
}
-void QGLEngineShaderManager::setUseGlobalOpacity(bool useOpacity)
+void QGLEngineShaderManager::setOpacityMode(OpacityMode mode)
{
- if (useGlobalOpacity == useOpacity)
+ if (opacityMode == mode)
return;
- useGlobalOpacity = useOpacity;
+ opacityMode = mode;
shaderProgNeedsChanging = true; //###
}
@@ -564,22 +564,28 @@ bool QGLEngineShaderManager::useCorrectShaderProg()
// Choose fragment shader main function:
QGLEngineSharedShaders::ShaderName mainFragShaderName;
- if (hasCompose && hasMask && useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CMO;
- if (hasCompose && hasMask && !useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CM;
- if (!hasCompose && hasMask && useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_MO;
- if (!hasCompose && hasMask && !useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_M;
- if (hasCompose && !hasMask && useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CO;
- if (hasCompose && !hasMask && !useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_C;
- if (!hasCompose && !hasMask && useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_O;
- if (!hasCompose && !hasMask && !useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader;
+ if (opacityMode == AttributeOpacity) {
+ Q_ASSERT(!hasCompose && !hasMask);
+ mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_ImageArrays;
+ } else {
+ bool useGlobalOpacity = (opacityMode == UniformOpacity);
+ if (hasCompose && hasMask && useGlobalOpacity)
+ mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CMO;
+ if (hasCompose && hasMask && !useGlobalOpacity)
+ mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CM;
+ if (!hasCompose && hasMask && useGlobalOpacity)
+ mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_MO;
+ if (!hasCompose && hasMask && !useGlobalOpacity)
+ mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_M;
+ if (hasCompose && !hasMask && useGlobalOpacity)
+ mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CO;
+ if (hasCompose && !hasMask && !useGlobalOpacity)
+ mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_C;
+ if (!hasCompose && !hasMask && useGlobalOpacity)
+ mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_O;
+ if (!hasCompose && !hasMask && !useGlobalOpacity)
+ mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader;
+ }
requiredProgram.mainFragShader = sharedShaders->compileNamedShader(mainFragShaderName, QGLShader::PartialFragmentShader);
@@ -652,12 +658,17 @@ bool QGLEngineShaderManager::useCorrectShaderProg()
// Choose vertex shader main function
QGLEngineSharedShaders::ShaderName mainVertexShaderName = QGLEngineSharedShaders::InvalidShaderName;
- if (texCoords)
+ if (opacityMode == AttributeOpacity) {
+ Q_ASSERT(texCoords);
+ mainVertexShaderName = QGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader;
+ } else if (texCoords) {
mainVertexShaderName = QGLEngineSharedShaders::MainWithTexCoordsVertexShader;
- else
+ } else {
mainVertexShaderName = QGLEngineSharedShaders::MainVertexShader;
+ }
requiredProgram.mainVertexShader = sharedShaders->compileNamedShader(mainVertexShaderName, QGLShader::PartialVertexShader);
requiredProgram.useTextureCoords = texCoords;
+ requiredProgram.useOpacityAttribute = (opacityMode == AttributeOpacity);
// At this point, requiredProgram is fully populated so try to find the program in the cache
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
index 47d9a2a..291d24c 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
@@ -247,6 +247,7 @@ struct QGLEngineShaderProg
QVector<uint> uniformLocations;
bool useTextureCoords;
+ bool useOpacityAttribute;
bool operator==(const QGLEngineShaderProg& other) {
// We don't care about the program
@@ -277,6 +278,7 @@ struct QGLEngineCachedShaderProg
static const GLuint QT_VERTEX_COORDS_ATTR = 0;
static const GLuint QT_TEXTURE_COORDS_ATTR = 1;
+static const GLuint QT_OPACITY_ATTR = 2;
class QGLEngineSharedShaders : public QObject
{
@@ -285,6 +287,7 @@ public:
enum ShaderName {
MainVertexShader,
MainWithTexCoordsVertexShader,
+ MainWithTexCoordsAndOpacityVertexShader,
UntransformedPositionVertexShader,
PositionOnlyVertexShader,
@@ -307,6 +310,7 @@ public:
MainFragmentShader_C,
MainFragmentShader_O,
MainFragmentShader,
+ MainFragmentShader_ImageArrays,
ImageSrcFragmentShader,
ImageSrcWithPatternFragmentShader,
@@ -361,7 +365,7 @@ private slots:
void shaderDestroyed(QObject *shader);
private:
- QGLContextGroup *ctx;
+ QGLSharedResourceGuard ctxGuard;
QGLShaderProgram *blitShaderProg;
QGLShaderProgram *simpleShaderProg;
QList<QGLEngineShaderProg> cachedPrograms;
@@ -406,13 +410,19 @@ public:
NumUniforms
};
+ enum OpacityMode {
+ NoOpacity,
+ UniformOpacity,
+ AttributeOpacity
+ };
+
// There are optimisations we can do, depending on the brush transform:
// 1) May not have to apply perspective-correction
// 2) Can use lower precision for matrix
void optimiseForBrushTransform(const QTransform &transform);
void setSrcPixelType(Qt::BrushStyle);
void setSrcPixelType(PixelSrcType); // For non-brush sources, like pixmaps & images
- void setUseGlobalOpacity(bool);
+ void setOpacityMode(OpacityMode);
void setMaskType(MaskType);
void setCompositionMode(QPainter::CompositionMode);
void setCustomStage(QGLCustomShaderStage* stage);
@@ -451,7 +461,7 @@ private:
// Current state variables which influence the choice of shader:
QTransform brushTransform;
int srcPixelType;
- bool useGlobalOpacity;
+ OpacityMode opacityMode;
MaskType maskType;
QPainter::CompositionMode compositionMode;
QGLCustomShaderStage* customSrcStage;
diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
index 8ae86d3..6712bf6 100644
--- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
@@ -84,6 +84,20 @@ static const char* const qglslMainWithTexCoordsVertexShader = "\
textureCoords = textureCoordArray; \
}";
+static const char* const qglslMainWithTexCoordsAndOpacityVertexShader = "\
+ attribute highp vec2 textureCoordArray; \
+ attribute lowp float opacityArray; \
+ varying highp vec2 textureCoords; \
+ varying lowp float opacity; \
+ uniform highp float depth; \
+ void setPosition(); \
+ void main(void) \
+ { \
+ setPosition(); \
+ gl_Position.z = depth * gl_Position.w; \
+ textureCoords = textureCoordArray; \
+ opacity = opacityArray; \
+ }";
static const char* const qglslPositionOnlyVertexShader = "\
attribute highp vec4 vertexCoordsArray;\
@@ -331,6 +345,12 @@ static const char* const qglslShockingPinkSrcFragmentShader = "\
return vec4(0.98, 0.06, 0.75, 1.0); \
}";
+static const char* const qglslMainFragmentShader_ImageArrays = "\
+ varying lowp float opacity; \
+ lowp vec4 srcPixel(); \
+ void main() { \
+ gl_FragColor = srcPixel() * opacity; \
+ }";
static const char* const qglslMainFragmentShader_CMO = "\
uniform lowp float globalOpacity; \
diff --git a/src/opengl/gl2paintengineex/qglgradientcache.cpp b/src/opengl/gl2paintengineex/qglgradientcache.cpp
index 4b2b2a0..e06f15d 100644
--- a/src/opengl/gl2paintengineex/qglgradientcache.cpp
+++ b/src/opengl/gl2paintengineex/qglgradientcache.cpp
@@ -57,13 +57,9 @@ QGL2GradientCache *QGL2GradientCache::cacheForContext(const QGLContext *context)
{
QGL2GradientCache *p = reinterpret_cast<QGL2GradientCache *>(qt_gradient_caches()->value(context));
if (!p) {
- QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
- if (oldContext != context)
- const_cast<QGLContext *>(context)->makeCurrent();
+ QGLShareContextScope scope(context);
p = new QGL2GradientCache;
qt_gradient_caches()->insert(context, p);
- if (oldContext && oldContext != context)
- oldContext->makeCurrent();
}
return p;
}
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
index 7e45fd9..8130151 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -87,6 +87,7 @@ QT_BEGIN_NAMESPACE
//#define QT_GL_NO_SCISSOR_TEST
+static const GLuint GL_STENCIL_HIGH_BIT = 0x80;
static const GLuint QT_BRUSH_TEXTURE_UNIT = 0;
static const GLuint QT_IMAGE_TEXTURE_UNIT = 0; //Can be the same as brush texture unit
static const GLuint QT_MASK_TEXTURE_UNIT = 1;
@@ -162,15 +163,11 @@ QGLTextureGlyphCache::QGLTextureGlyphCache(QGLContext *context, QFontEngineGlyph
QGLTextureGlyphCache::~QGLTextureGlyphCache()
{
if (ctx) {
- QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
- if (oldContext != ctx)
- ctx->makeCurrent();
+ QGLShareContextScope scope(ctx);
glDeleteFramebuffers(1, &m_fbo);
if (m_width || m_height)
glDeleteTextures(1, &m_texture);
- if (oldContext && oldContext != ctx)
- oldContext->makeCurrent();
}
}
@@ -231,6 +228,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height)
glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT | GL_SCISSOR_BIT);
#endif
+ glDisable(GL_STENCIL_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
@@ -280,7 +278,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height)
glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
glViewport(0, 0, pex->width, pex->height);
- pex->updateDepthScissorTest();
+ pex->updateClipScissorTest();
#ifndef QT_OPENGL_ES_2
if (pex->inRenderText)
@@ -366,7 +364,7 @@ void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMod
}
-QColor QGL2PaintEngineExPrivate::premultiplyColor(QColor c, GLfloat opacity)
+inline QColor qt_premultiplyColor(QColor c, GLfloat opacity)
{
qreal alpha = c.alphaF() * opacity;
c.setAlphaF(alpha);
@@ -393,7 +391,6 @@ void QGL2PaintEngineExPrivate::setBrush(const QBrush* brush)
}
-// Unless this gets used elsewhere, it's probably best to merge it into fillStencilWithVertexArray
void QGL2PaintEngineExPrivate::useSimpleShader()
{
shaderManager->simpleProgram()->enable();
@@ -406,11 +403,6 @@ void QGL2PaintEngineExPrivate::useSimpleShader()
shaderManager->simpleProgram()->setUniformValue("pmvMatrix", pmvMatrix);
simpleShaderMatrixUniformDirty = false;
}
-
- if (simpleShaderDepthUniformDirty) {
- shaderManager->simpleProgram()->setUniformValue("depth", normalizedDeviceDepth(q->state()->currentDepth));
- simpleShaderDepthUniformDirty = false;
- }
}
void QGL2PaintEngineExPrivate::updateBrushTexture()
@@ -469,7 +461,7 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms()
QTransform brushQTransform = currentBrush->transform();
if (style == Qt::SolidPattern) {
- QColor col = premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity);
+ QColor col = qt_premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity);
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::FragmentColor), col);
}
else {
@@ -479,7 +471,7 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms()
if (style <= Qt::DiagCrossPattern) {
translationPoint = q->state()->brushOrigin;
- QColor col = premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity);
+ QColor col = qt_premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity);
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
@@ -541,7 +533,7 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms()
const QPixmap& texPixmap = currentBrush->texture();
if (qHasPixmapTexture(*currentBrush) && currentBrush->texture().isQBitmap()) {
- QColor col = premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity);
+ QColor col = qt_premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity);
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
}
@@ -712,7 +704,7 @@ void QGL2PaintEngineExPrivate::drawTexture(const QGLRect& dest, const QGLRect& s
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
if (pattern) {
- QColor col = premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity);
+ QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity);
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
}
@@ -766,6 +758,8 @@ void QGL2PaintEngineEx::beginNativePainting()
d->dirtyStencilRegion = QRect(0, 0, d->width, d->height);
d->resetGLState();
+ d->shaderManager->setDirty();
+
d->needsSync = true;
}
@@ -773,9 +767,11 @@ void QGL2PaintEngineExPrivate::resetGLState()
{
glDisable(GL_BLEND);
glActiveTexture(GL_TEXTURE0);
+ glDisable(GL_STENCIL_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_SCISSOR_TEST);
glDepthMask(true);
+ glDepthFunc(GL_LESS);
glClearDepth(1);
}
@@ -796,9 +792,10 @@ void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode)
if (newMode == mode)
return;
- if (mode == TextDrawingMode || mode == ImageDrawingMode) {
+ if (mode == TextDrawingMode || mode == ImageDrawingMode || mode == ImageArrayDrawingMode) {
glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+ glDisableVertexAttribArray(QT_OPACITY_ATTR);
lastTexture = GLuint(-1);
}
@@ -824,6 +821,16 @@ void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode)
glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, staticTextureCoordinateArray);
}
+ if (newMode == ImageArrayDrawingMode) {
+ glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+ glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
+ glEnableVertexAttribArray(QT_OPACITY_ATTR);
+
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray.data());
+ glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray.data());
+ glVertexAttribPointer(QT_OPACITY_ATTR, 1, GL_FLOAT, GL_FALSE, 0, opacityArray.data());
+ }
+
// This needs to change when we implement high-quality anti-aliasing...
if (newMode != TextDrawingMode)
shaderManager->setMaskType(QGLEngineShaderManager::NoMask);
@@ -868,37 +875,49 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
if (path.shape() == QVectorPath::RectangleHint) {
QGLRect rect(points[0].x(), points[0].y(), points[2].x(), points[2].y());
prepareForDraw(currentBrush->isOpaque());
-
composite(rect);
- }
- else if (path.shape() == QVectorPath::EllipseHint) {
+ } else if (path.shape() == QVectorPath::EllipseHint
+ || path.shape() == QVectorPath::ConvexPolygonHint)
+ {
vertexCoordinateArray.clear();
- vertexCoordinateArray.addPath(path, inverseScale);
+ vertexCoordinateArray.addPath(path, inverseScale, false);
prepareForDraw(currentBrush->isOpaque());
drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
- }
- else {
+ } else {
// The path is too complicated & needs the stencil technique
vertexCoordinateArray.clear();
- vertexCoordinateArray.addPath(path, inverseScale);
+ vertexCoordinateArray.addPath(path, inverseScale, false);
fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
- // Stencil the brush onto the dest buffer
- glStencilFunc(GL_NOTEQUAL, 0, 0xFFFF); // Pass if stencil buff value != 0
- glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ 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);
+ }
- glEnable(GL_STENCIL_TEST);
prepareForDraw(currentBrush->isOpaque());
-#ifndef QT_OPENGL_ES_2
if (inRenderText)
- shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Depth), zValueForRenderText());
-#endif
+ prepareDepthRangeForRenderText();
+
+ // Stencil the brush onto the dest buffer
composite(vertexCoordinateArray.boundingRect());
- glDisable(GL_STENCIL_TEST);
+
+ if (inRenderText)
+ restoreDepthRangeForRenderText();
glStencilMask(0);
+
+ updateClipScissorTest();
}
}
@@ -906,31 +925,28 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill)
{
// qDebug("QGL2PaintEngineExPrivate::fillStencilWithVertexArray()");
- glStencilMask(0xFFFF); // Enable stencil writes
+ glStencilMask(0xff); // Enable stencil writes
if (dirtyStencilRegion.intersects(currentScissorBounds)) {
- // Clear the stencil buffer to zeros
- glDisable(GL_STENCIL_TEST);
+ QVector<QRect> clearRegion = dirtyStencilRegion.intersected(currentScissorBounds).rects();
glClearStencil(0); // Clear to zero
- glClear(GL_STENCIL_BUFFER_BIT);
+ for (int i = 0; i < clearRegion.size(); ++i) {
+#ifndef QT_GL_NO_SCISSOR_TEST
+ setScissor(clearRegion.at(i));
+#endif
+ glClear(GL_STENCIL_BUFFER_BIT);
+ }
+
dirtyStencilRegion -= currentScissorBounds;
+
+#ifndef QT_GL_NO_SCISSOR_TEST
+ updateClipScissorTest();
+#endif
}
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes
- glStencilFunc(GL_ALWAYS, 0, 0xFFFF); // Always pass the stencil test
-
- // Setup the stencil op:
- if (useWindingFill) {
- glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); // Inc. for front-facing triangle
- glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); //Dec. for back-facing "holes"
- } else
- glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
-
- // No point in using a fancy gradient shader for writing into the stencil buffer!
useSimpleShader();
-
glEnable(GL_STENCIL_TEST); // For some reason, this has to happen _after_ the simple shader is use()'d
- glDisable(GL_BLEND);
#ifndef QT_OPENGL_ES_2
if (inRenderText) {
@@ -939,21 +955,94 @@ void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& ve
}
#endif
- // Draw the vertecies into the stencil buffer:
- drawVertexArrays(vertexArray, GL_TRIANGLE_FAN);
+ if (useWindingFill) {
+ if (q->state()->clipTestEnabled) {
+ // Flatten clip values higher than current clip, and set high bit to match current clip
+ glStencilFunc(GL_LEQUAL, GL_STENCIL_HIGH_BIT | q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
+ composite(vertexArray.boundingRect());
+
+ glStencilFunc(GL_EQUAL, GL_STENCIL_HIGH_BIT, GL_STENCIL_HIGH_BIT);
+ } else if (!stencilClean) {
+ // Clear stencil buffer within bounding rect
+ glStencilFunc(GL_ALWAYS, 0, 0xff);
+ glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
+ composite(vertexArray.boundingRect());
+ }
+
+ // Inc. for front-facing triangle
+ glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP);
+ // Dec. for back-facing "holes"
+ glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_DECR_WRAP);
+ glStencilMask(~GL_STENCIL_HIGH_BIT);
+ drawVertexArrays(vertexArray, GL_TRIANGLE_FAN);
+
+ if (q->state()->clipTestEnabled) {
+ // Clear high bit of stencil outside of path
+ glStencilFunc(GL_EQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
+ glStencilMask(GL_STENCIL_HIGH_BIT);
+ composite(vertexArray.boundingRect());
+ }
+ } else {
+ glStencilMask(GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
+ drawVertexArrays(vertexArray, GL_TRIANGLE_FAN);
+ }
+
+ // Enable color writes & disable stencil writes
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
#ifndef QT_OPENGL_ES_2
if (inRenderText)
glPopAttrib();
#endif
- // Enable color writes & disable stencil writes
+}
+
+/*
+ If the maximum value in the stencil buffer is GL_STENCIL_HIGH_BIT - 1,
+ restore the stencil buffer to a pristine state. The current clip region
+ is set to 1, and the rest to 0.
+*/
+void QGL2PaintEngineExPrivate::resetClipIfNeeded()
+{
+ if (maxClip != (GL_STENCIL_HIGH_BIT - 1))
+ return;
+
+ Q_Q(QGL2PaintEngineEx);
+
+ useSimpleShader();
+ glEnable(GL_STENCIL_TEST);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+
+ QRectF bounds = q->state()->matrix.inverted().mapRect(QRectF(0, 0, width, height));
+ QGLRect rect(bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
+
+ // Set high bit on clip region
+ glStencilFunc(GL_LEQUAL, q->state()->currentClip, 0xff);
+ glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT);
+ glStencilMask(GL_STENCIL_HIGH_BIT);
+ composite(rect);
+
+ // Reset clipping to 1 and everything else to zero
+ glStencilFunc(GL_NOTEQUAL, 0x01, GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_ZERO, GL_REPLACE, GL_REPLACE);
+ glStencilMask(0xff);
+ composite(rect);
+
+ q->state()->currentClip = 1;
+ q->state()->canRestoreClip = false;
+
+ maxClip = 1;
+
+ glStencilMask(0x0);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque)
{
- if (brushTextureDirty && mode != ImageDrawingMode)
+ if (brushTextureDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode)
updateBrushTexture();
if (compositionModeDirty)
@@ -972,16 +1061,22 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque)
glEnable(GL_BLEND);
}
- bool useGlobalOpacityUniform = stateHasOpacity;
- if (stateHasOpacity && (mode != ImageDrawingMode)) {
- // Using a brush
- bool brushIsPattern = (currentBrush->style() >= Qt::Dense1Pattern) &&
- (currentBrush->style() <= Qt::DiagCrossPattern);
-
- if ((currentBrush->style() == Qt::SolidPattern) || brushIsPattern)
- useGlobalOpacityUniform = false; // Global opacity handled by srcPixel shader
+ QGLEngineShaderManager::OpacityMode opacityMode;
+ if (mode == ImageArrayDrawingMode) {
+ opacityMode = QGLEngineShaderManager::AttributeOpacity;
+ } else {
+ opacityMode = stateHasOpacity ? QGLEngineShaderManager::UniformOpacity
+ : QGLEngineShaderManager::NoOpacity;
+ if (stateHasOpacity && (mode != ImageDrawingMode)) {
+ // Using a brush
+ bool brushIsPattern = (currentBrush->style() >= Qt::Dense1Pattern) &&
+ (currentBrush->style() <= Qt::DiagCrossPattern);
+
+ if ((currentBrush->style() == Qt::SolidPattern) || brushIsPattern)
+ opacityMode = QGLEngineShaderManager::NoOpacity; // Global opacity handled by srcPixel shader
+ }
}
- shaderManager->setUseGlobalOpacity(useGlobalOpacityUniform);
+ shaderManager->setOpacityMode(opacityMode);
bool changed = shaderManager->useCorrectShaderProg();
// If the shader program needs changing, we change it and mark all uniforms as dirty
@@ -989,11 +1084,10 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque)
// The shader program has changed so mark all uniforms as dirty:
brushUniformsDirty = true;
shaderMatrixUniformDirty = true;
- depthUniformDirty = true;
opacityUniformDirty = true;
}
- if (brushUniformsDirty && mode != ImageDrawingMode)
+ if (brushUniformsDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode)
updateBrushUniforms();
if (shaderMatrixUniformDirty) {
@@ -1001,12 +1095,7 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque)
shaderMatrixUniformDirty = false;
}
- if (depthUniformDirty) {
- shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Depth), normalizedDeviceDepth(q->state()->currentDepth));
- depthUniformDirty = false;
- }
-
- if (useGlobalOpacityUniform && opacityUniformDirty) {
+ if (opacityMode == QGLEngineShaderManager::UniformOpacity && opacityUniformDirty) {
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::GlobalOpacity), (GLfloat)q->state()->opacity);
opacityUniformDirty = false;
}
@@ -1052,7 +1141,7 @@ void QGL2PaintEngineExPrivate::drawVertexArrays(QGL2PEXVertexArray& vertexArray,
glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
}
-float QGL2PaintEngineExPrivate::zValueForRenderText() const
+void QGL2PaintEngineExPrivate::prepareDepthRangeForRenderText()
{
#ifndef QT_OPENGL_ES_2
// Get the z translation value from the model view matrix and
@@ -1060,9 +1149,19 @@ float QGL2PaintEngineExPrivate::zValueForRenderText() const
// and z-far = 1, which is used in QGLWidget::renderText()
GLdouble model[4][4];
glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]);
- return -2 * model[3][2] - 1;
-#else
- return 0;
+ float deviceZ = -2 * model[3][2] - 1;
+
+ glGetFloatv(GL_DEPTH_RANGE, depthRange);
+ float windowZ = depthRange[0] + (deviceZ + 1) * 0.5 * (depthRange[1] - depthRange[0]);
+
+ glDepthRange(windowZ, windowZ);
+#endif
+}
+
+void QGL2PaintEngineExPrivate::restoreDepthRangeForRenderText()
+{
+#ifndef QT_OPENGL_ES_2
+ glDepthRange(depthRange[0], depthRange[1]);
#endif
}
@@ -1125,6 +1224,7 @@ void QGL2PaintEngineEx::opacityChanged()
{
// qDebug("QGL2PaintEngineEx::opacityChanged()");
Q_D(QGL2PaintEngineEx);
+ state()->opacityChanged = true;
Q_ASSERT(d->shaderManager);
d->brushUniformsDirty = true;
@@ -1135,11 +1235,14 @@ void QGL2PaintEngineEx::compositionModeChanged()
{
// qDebug("QGL2PaintEngineEx::compositionModeChanged()");
Q_D(QGL2PaintEngineEx);
+ state()->compositionModeChanged = true;
d->compositionModeDirty = true;
}
void QGL2PaintEngineEx::renderHintsChanged()
{
+ state()->renderHintsChanged = true;
+
#if !defined(QT_OPENGL_ES_2)
if ((state()->renderHints & QPainter::Antialiasing)
|| (state()->renderHints & QPainter::HighQualityAntialiasing))
@@ -1158,6 +1261,7 @@ void QGL2PaintEngineEx::transformChanged()
{
Q_D(QGL2PaintEngineEx);
d->matrixDirty = true;
+ state()->matrixChanged = true;
}
@@ -1245,6 +1349,9 @@ void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem
? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat)
: d->glyphCacheType;
+ if (d->inRenderText)
+ glyphType = QFontEngineGlyphCache::Raster_A8;
+
if (glyphType == QFontEngineGlyphCache::Raster_RGBMask
&& state()->composition_mode != QPainter::CompositionMode_Source
&& state()->composition_mode != QPainter::CompositionMode_SourceOver)
@@ -1317,6 +1424,9 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly
QBrush pensBrush = q->state()->pen.brush();
setBrush(&pensBrush);
+ if (inRenderText)
+ prepareDepthRangeForRenderText();
+
if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) {
// Subpixel antialiasing without gamma correction
@@ -1332,7 +1442,7 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly
QColor c = pensBrush.color();
qreal oldOpacity = q->state()->opacity;
if (compMode == QPainter::CompositionMode_Source) {
- c = premultiplyColor(c, q->state()->opacity);
+ c = qt_premultiplyColor(c, q->state()->opacity);
q->state()->opacity = 1;
opacityUniformDirty = true;
}
@@ -1369,10 +1479,6 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly
glBindTexture(GL_TEXTURE_2D, cache->texture());
updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
-#ifndef QT_OPENGL_ES_2
- if (inRenderText)
- shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Depth), zValueForRenderText());
-#endif
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::MaskTexture), QT_MASK_TEXTURE_UNIT);
glDrawArrays(GL_TRIANGLES, 0, 6 * glyphs.size());
@@ -1403,12 +1509,101 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly
glBindTexture(GL_TEXTURE_2D, cache->texture());
updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
-#ifndef QT_OPENGL_ES_2
- if (inRenderText)
- shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Depth), zValueForRenderText());
-#endif
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::MaskTexture), QT_MASK_TEXTURE_UNIT);
glDrawArrays(GL_TRIANGLES, 0, 6 * glyphs.size());
+
+ if (inRenderText)
+ restoreDepthRangeForRenderText();
+}
+
+void QGL2PaintEngineEx::drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints)
+{
+ // Use fallback for extended composition modes.
+ if (state()->composition_mode > QPainter::CompositionMode_Plus) {
+ QPaintEngineEx::drawPixmaps(drawingData, dataCount, pixmap, hints);
+ return;
+ }
+
+ Q_D(QGL2PaintEngineEx);
+
+ GLfloat dx = 1.0f / pixmap.size().width();
+ GLfloat dy = 1.0f / pixmap.size().height();
+
+ d->vertexCoordinateArray.clear();
+ d->textureCoordinateArray.clear();
+ d->opacityArray.reset();
+
+ bool allOpaque = true;
+
+ for (int i = 0; i < dataCount; ++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);
+ }
+
+ qreal right = 0.5 * drawingData[i].scaleX * drawingData[i].source.width();
+ qreal bottom = 0.5 * drawingData[i].scaleY * drawingData[i].source.height();
+ QGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c);
+ QGLPoint bottomLeft(-right * c - bottom * s, -right * s + bottom * c);
+
+ d->vertexCoordinateArray.lineToArray(bottomRight.x + drawingData[i].point.x(), bottomRight.y + drawingData[i].point.y());
+ d->vertexCoordinateArray.lineToArray(-bottomLeft.x + drawingData[i].point.x(), -bottomLeft.y + drawingData[i].point.y());
+ d->vertexCoordinateArray.lineToArray(-bottomRight.x + drawingData[i].point.x(), -bottomRight.y + drawingData[i].point.y());
+ d->vertexCoordinateArray.lineToArray(-bottomRight.x + drawingData[i].point.x(), -bottomRight.y + drawingData[i].point.y());
+ d->vertexCoordinateArray.lineToArray(bottomLeft.x + drawingData[i].point.x(), bottomLeft.y + drawingData[i].point.y());
+ d->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);
+
+ d->textureCoordinateArray.lineToArray(src.right, src.bottom);
+ d->textureCoordinateArray.lineToArray(src.right, src.top);
+ d->textureCoordinateArray.lineToArray(src.left, src.top);
+ d->textureCoordinateArray.lineToArray(src.left, src.top);
+ d->textureCoordinateArray.lineToArray(src.left, src.bottom);
+ d->textureCoordinateArray.lineToArray(src.right, src.bottom);
+
+ qreal opacity = drawingData[i].opacity * state()->opacity;
+ d->opacityArray << opacity << opacity << opacity << opacity << opacity << opacity;
+ allOpaque &= (opacity >= 0.99f);
+ }
+
+ ensureActive();
+
+ QGLContext *ctx = d->ctx;
+ glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
+ QGLTexture *texture = ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA,
+ QGLContext::InternalBindOption
+ | QGLContext::CanFlipNativePixmapBindOption);
+
+ if (texture->options & QGLContext::InvertedYBindOption) {
+ // Flip texture y-coordinate.
+ QGLPoint *data = d->textureCoordinateArray.data();
+ for (int i = 0; i < 6 * dataCount; ++i)
+ data[i].y = 1 - data[i].y;
+ }
+
+ d->transferMode(ImageArrayDrawingMode);
+
+ bool isBitmap = pixmap.isQBitmap();
+ bool isOpaque = !isBitmap && (!pixmap.hasAlphaChannel() || (hints & QDrawPixmaps::OpaqueHint)) && allOpaque;
+
+ d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
+ state()->renderHints & QPainter::SmoothPixmapTransform, texture->id);
+
+ // Setup for texture drawing
+ d->shaderManager->setSrcPixelType(isBitmap ? QGLEngineShaderManager::PatternSrc : QGLEngineShaderManager::ImageSrc);
+ if (d->prepareForDraw(isOpaque))
+ d->shaderManager->currentProgram()->setUniformValue(d->location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
+
+ if (isBitmap) {
+ QColor col = qt_premultiplyColor(state()->pen.color(), (GLfloat)state()->opacity);
+ d->shaderManager->currentProgram()->setUniformValue(d->location(QGLEngineShaderManager::PatternColor), col);
+ }
+
+ glDrawArrays(GL_TRIANGLES, 0, 6 * dataCount);
}
bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
@@ -1430,19 +1625,17 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
const QSize sz = d->device->size();
d->width = sz.width();
d->height = sz.height();
- d->last_created_state = 0;
d->mode = BrushDrawingMode;
d->brushTextureDirty = true;
d->brushUniformsDirty = true;
d->matrixDirty = true;
d->compositionModeDirty = true;
- d->simpleShaderDepthUniformDirty = true;
- d->depthUniformDirty = true;
d->opacityUniformDirty = true;
d->needsSync = true;
d->use_system_clip = !systemClip().isEmpty();
d->dirtyStencilRegion = QRect(0, 0, d->width, d->height);
+ d->stencilClean = true;
// Calling begin paint should make the correct context current. So, any
// code which calls into GL or otherwise needs a current context *must*
@@ -1452,15 +1645,15 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
#if !defined(QT_OPENGL_ES_2)
bool success = qt_resolve_version_2_0_functions(d->ctx);
Q_ASSERT(success);
+ Q_UNUSED(success);
#endif
d->shaderManager = new QGLEngineShaderManager(d->ctx);
if (!d->inRenderText) {
+ glDisable(GL_STENCIL_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_SCISSOR_TEST);
- glDepthFunc(GL_LESS);
- glDepthMask(false);
}
#if !defined(QT_OPENGL_ES_2)
@@ -1524,22 +1717,26 @@ void QGL2PaintEngineEx::ensureActive()
if (d->needsSync) {
d->transferMode(BrushDrawingMode);
glViewport(0, 0, d->width, d->height);
- glDepthMask(false);
- glDepthFunc(GL_LESS);
d->needsSync = false;
+ d->shaderManager->setDirty();
setState(state());
}
}
-void QGL2PaintEngineExPrivate::updateDepthScissorTest()
+void QGL2PaintEngineExPrivate::updateClipScissorTest()
{
Q_Q(QGL2PaintEngineEx);
- if (q->state()->depthTestEnabled)
- glEnable(GL_DEPTH_TEST);
- else
- glDisable(GL_DEPTH_TEST);
+ if (q->state()->clipTestEnabled) {
+ glEnable(GL_STENCIL_TEST);
+ glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
+ } else {
+ glDisable(GL_STENCIL_TEST);
+ glStencilFunc(GL_ALWAYS, 0, 0xff);
+ }
-#ifndef QT_GL_NO_SCISSOR_TEST
+#ifdef QT_GL_NO_SCISSOR_TEST
+ currentScissorBounds = QRect(0, 0, width, height);
+#else
QRect bounds = q->state()->rectangleClip;
if (!q->state()->clipEnabled) {
if (use_system_clip)
@@ -1578,69 +1775,93 @@ void QGL2PaintEngineEx::clipEnabledChanged()
{
Q_D(QGL2PaintEngineEx);
- d->simpleShaderDepthUniformDirty = true;
- d->depthUniformDirty = true;
+ state()->clipChanged = true;
- if (painter()->hasClipping()) {
- d->regenerateDepthClip();
- } else {
- if (d->use_system_clip) {
- state()->currentDepth = 0;
- } else {
- state()->depthTestEnabled = false;
- }
+ if (painter()->hasClipping())
+ d->regenerateClip();
+ else
+ d->systemStateChanged();
+}
- d->updateDepthScissorTest();
- }
+void QGL2PaintEngineExPrivate::clearClip(uint value)
+{
+ dirtyStencilRegion -= currentScissorBounds;
+
+ glStencilMask(0xff);
+ glClearStencil(value);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ glStencilMask(0x0);
+
+ q->state()->needsClipBufferClear = false;
}
-void QGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint depth)
+void QGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint value)
{
transferMode(BrushDrawingMode);
if (matrixDirty)
updateMatrix();
- if (q->state()->needsDepthBufferClear) {
- glDepthMask(true);
- glClearDepth(rawDepth(2));
- glClear(GL_DEPTH_BUFFER_BIT);
- q->state()->needsDepthBufferClear = false;
- glDepthMask(false);
- }
- if (path.isEmpty())
+ stencilClean = false;
+
+ const bool singlePass = !path.hasWindingFill()
+ && (((q->state()->currentClip == maxClip - 1) && q->state()->clipTestEnabled)
+ || q->state()->needsClipBufferClear);
+ const uint referenceClipValue = q->state()->needsClipBufferClear ? 1 : q->state()->currentClip;
+
+ if (q->state()->needsClipBufferClear)
+ clearClip(1);
+
+ if (path.isEmpty()) {
+ glEnable(GL_STENCIL_TEST);
+ glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT);
return;
+ }
- glDisable(GL_BLEND);
- glDepthMask(false);
+ if (q->state()->clipTestEnabled)
+ glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
+ else
+ glStencilFunc(GL_ALWAYS, 0, 0xff);
vertexCoordinateArray.clear();
- vertexCoordinateArray.addPath(path, inverseScale);
+ vertexCoordinateArray.addPath(path, inverseScale, false);
- glDepthMask(GL_FALSE);
- fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
+ if (!singlePass)
+ fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
- // Stencil the clip onto the clip buffer
glColorMask(false, false, false, false);
- glDepthMask(true);
+ glEnable(GL_STENCIL_TEST);
+ useSimpleShader();
- shaderManager->simpleProgram()->setUniformValue("depth", normalizedDeviceDepth(depth));
- simpleShaderDepthUniformDirty = true;
+ if (singlePass) {
+ // Under these conditions we can set the new stencil value in a single
+ // pass, by using the current value and the "new value" as the toggles
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_ALWAYS);
+ glStencilFunc(GL_LEQUAL, referenceClipValue, ~GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT);
+ glStencilMask(value ^ referenceClipValue);
- glStencilFunc(GL_NOTEQUAL, 0, 0xFFFF); // Pass if stencil buff value != 0
- glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
+ } else {
+ glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
+ glStencilMask(0xff);
- glEnable(GL_STENCIL_TEST);
- composite(vertexCoordinateArray.boundingRect());
- glDisable(GL_STENCIL_TEST);
+ if (!q->state()->clipTestEnabled && path.hasWindingFill()) {
+ // Pass when any clip bit is set, set high bit
+ glStencilFunc(GL_NOTEQUAL, GL_STENCIL_HIGH_BIT, ~GL_STENCIL_HIGH_BIT);
+ composite(vertexCoordinateArray.boundingRect());
+ }
+
+ // Pass when high bit is set, replace stencil value with new clip value
+ glStencilFunc(GL_NOTEQUAL, value, GL_STENCIL_HIGH_BIT);
+
+ composite(vertexCoordinateArray.boundingRect());
+ }
+ glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT);
glStencilMask(0);
glColorMask(true, true, true, true);
- glDepthMask(false);
}
void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
@@ -1648,6 +1869,8 @@ void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
// qDebug("QGL2PaintEngineEx::clip()");
Q_D(QGL2PaintEngineEx);
+ state()->clipChanged = true;
+
ensureActive();
if (op == Qt::ReplaceClip) {
@@ -1665,7 +1888,7 @@ void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
if (state()->matrix.type() <= QTransform::TxScale) {
state()->rectangleClip = state()->rectangleClip.intersected(state()->matrix.mapRect(rect).toRect());
- d->updateDepthScissorTest();
+ d->updateClipScissorTest();
return;
}
}
@@ -1676,43 +1899,44 @@ void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
switch (op) {
case Qt::NoClip:
if (d->use_system_clip) {
- state()->depthTestEnabled = true;
- state()->currentDepth = 0;
+ state()->clipTestEnabled = true;
+ state()->currentClip = 1;
} else {
- state()->depthTestEnabled = false;
+ state()->clipTestEnabled = false;
}
state()->rectangleClip = QRect(0, 0, d->width, d->height);
state()->canRestoreClip = false;
- d->updateDepthScissorTest();
+ d->updateClipScissorTest();
break;
case Qt::IntersectClip:
state()->rectangleClip = state()->rectangleClip.intersected(pathRect);
- d->updateDepthScissorTest();
- ++state()->maxDepth;
- d->writeClip(path, state()->maxDepth);
- state()->currentDepth = state()->maxDepth - 1;
- state()->depthTestEnabled = true;
+ d->updateClipScissorTest();
+ d->resetClipIfNeeded();
+ ++d->maxClip;
+ d->writeClip(path, d->maxClip);
+ state()->currentClip = d->maxClip;
+ state()->clipTestEnabled = true;
break;
case Qt::UniteClip: {
-#ifndef QT_GL_NO_SCISSOR_TEST
+ d->resetClipIfNeeded();
+ ++d->maxClip;
if (state()->rectangleClip.isValid()) {
- ++state()->maxDepth;
-
QPainterPath path;
path.addRect(state()->rectangleClip);
// flush the existing clip rectangle to the depth buffer
- d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(path)), state()->maxDepth);
+ d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(path)), d->maxClip);
}
+ state()->clipTestEnabled = false;
+#ifndef QT_GL_NO_SCISSOR_TEST
QRect oldRectangleClip = state()->rectangleClip;
state()->rectangleClip = state()->rectangleClip.united(pathRect);
- d->updateDepthScissorTest();
+ d->updateClipScissorTest();
QRegion extendRegion = QRegion(state()->rectangleClip) - oldRectangleClip;
- glDepthFunc(GL_ALWAYS);
if (!extendRegion.isEmpty()) {
QPainterPath extendPath;
extendPath.addRegion(extendRegion);
@@ -1721,27 +1945,19 @@ void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(extendPath)), 0);
}
#endif
- glDepthFunc(GL_ALWAYS);
// now write the clip path
- d->writeClip(path, state()->maxDepth);
+ d->writeClip(path, d->maxClip);
state()->canRestoreClip = false;
- state()->currentDepth = state()->maxDepth - 1;
- state()->depthTestEnabled = true;
+ state()->currentClip = d->maxClip;
+ state()->clipTestEnabled = true;
break;
}
default:
break;
}
-
- glDepthFunc(GL_LESS);
- if (state()->depthTestEnabled) {
- glEnable(GL_DEPTH_TEST);
- d->simpleShaderDepthUniformDirty = true;
- d->depthUniformDirty = true;
- }
}
-void QGL2PaintEngineExPrivate::regenerateDepthClip()
+void QGL2PaintEngineExPrivate::regenerateClip()
{
systemStateChanged();
replayClipOperations();
@@ -1751,6 +1967,8 @@ void QGL2PaintEngineExPrivate::systemStateChanged()
{
Q_Q(QGL2PaintEngineEx);
+ q->state()->clipChanged = true;
+
if (systemClip.isEmpty()) {
use_system_clip = false;
} else {
@@ -1762,46 +1980,34 @@ void QGL2PaintEngineExPrivate::systemStateChanged()
}
}
- q->state()->depthTestEnabled = false;
- q->state()->needsDepthBufferClear = true;
+ q->state()->clipTestEnabled = false;
+ q->state()->needsClipBufferClear = true;
- q->state()->currentDepth = 1;
- q->state()->maxDepth = 4;
+ q->state()->currentClip = 1;
+ maxClip = 1;
q->state()->rectangleClip = use_system_clip ? systemClip.boundingRect() : QRect(0, 0, width, height);
- updateDepthScissorTest();
+ updateClipScissorTest();
- if (use_system_clip) {
+ if (systemClip.numRects() == 1) {
+ if (systemClip.boundingRect() == QRect(0, 0, width, height))
+ use_system_clip = false;
#ifndef QT_GL_NO_SCISSOR_TEST
- if (systemClip.numRects() == 1) {
- if (q->state()->rectangleClip == QRect(0, 0, width, height)) {
- use_system_clip = false;
- } else {
- simpleShaderDepthUniformDirty = true;
- depthUniformDirty = true;
- }
- return;
- }
+ // scissoring takes care of the system clip
+ return;
#endif
- q->state()->needsDepthBufferClear = false;
-
- glDepthMask(true);
+ }
- glClearDepth(0);
- glClear(GL_DEPTH_BUFFER_BIT);
+ if (use_system_clip) {
+ clearClip(0);
QPainterPath path;
path.addRegion(systemClip);
- glDepthFunc(GL_ALWAYS);
- writeClip(qtVectorPathForPath(q->state()->matrix.inverted().map(path)), 2);
- glDepthFunc(GL_LESS);
-
- glEnable(GL_DEPTH_TEST);
- q->state()->depthTestEnabled = true;
-
- simpleShaderDepthUniformDirty = true;
- depthUniformDirty = true;
+ q->state()->currentClip = 0;
+ writeClip(qtVectorPathForPath(q->state()->matrix.inverted().map(path)), 1);
+ q->state()->currentClip = 1;
+ q->state()->clipTestEnabled = true;
}
}
@@ -1816,37 +2022,43 @@ void QGL2PaintEngineEx::setState(QPainterState *new_state)
QPaintEngineEx::setState(s);
- if (s == d->last_created_state) {
- d->last_created_state = 0;
+ if (s->isNew) {
+ // Newly created state object. The call to setState()
+ // will either be followed by a call to begin(), or we are
+ // setting the state as part of a save().
+ s->isNew = false;
return;
}
- renderHintsChanged();
+ // Setting the state as part of a restore().
- d->matrixDirty = true;
- d->compositionModeDirty = true;
- d->simpleShaderDepthUniformDirty = true;
- d->depthUniformDirty = true;
- d->simpleShaderMatrixUniformDirty = true;
- d->shaderMatrixUniformDirty = true;
- d->opacityUniformDirty = true;
+ if (old_state == s || old_state->renderHintsChanged)
+ renderHintsChanged();
- d->shaderManager->setDirty();
+ if (old_state == s || old_state->matrixChanged) {
+ d->matrixDirty = true;
+ d->simpleShaderMatrixUniformDirty = true;
+ d->shaderMatrixUniformDirty = true;
+ }
- if (old_state && old_state != s && old_state->canRestoreClip) {
- d->updateDepthScissorTest();
- glDepthMask(false);
- glDepthFunc(GL_LESS);
- s->maxDepth = old_state->maxDepth;
- } else {
- d->regenerateDepthClip();
+ if (old_state == s || old_state->compositionModeChanged)
+ d->compositionModeDirty = true;
+
+ if (old_state == s || old_state->opacityChanged)
+ d->opacityUniformDirty = true;
+
+ if (old_state == s || old_state->clipChanged) {
+ if (old_state && old_state != s && old_state->canRestoreClip) {
+ d->updateClipScissorTest();
+ glDepthFunc(GL_LEQUAL);
+ } else {
+ d->regenerateClip();
+ }
}
}
QPainterState *QGL2PaintEngineEx::createState(QPainterState *orig) const
{
- Q_D(const QGL2PaintEngineEx);
-
if (orig)
const_cast<QGL2PaintEngineEx *>(this)->ensureActive();
@@ -1856,7 +2068,12 @@ QPainterState *QGL2PaintEngineEx::createState(QPainterState *orig) const
else
s = new QOpenGL2PaintEngineState(*static_cast<QOpenGL2PaintEngineState *>(orig));
- d->last_created_state = s;
+ s->matrixChanged = false;
+ s->compositionModeChanged = false;
+ s->opacityChanged = false;
+ s->renderHintsChanged = false;
+ s->clipChanged = false;
+
return s;
}
@@ -1869,21 +2086,19 @@ void QGL2PaintEngineEx::setRenderTextActive(bool active)
QOpenGL2PaintEngineState::QOpenGL2PaintEngineState(QOpenGL2PaintEngineState &other)
: QPainterState(other)
{
- needsDepthBufferClear = other.needsDepthBufferClear;
- depthTestEnabled = other.depthTestEnabled;
- scissorTestEnabled = other.scissorTestEnabled;
- currentDepth = other.currentDepth;
- maxDepth = other.maxDepth;
+ isNew = true;
+ needsClipBufferClear = other.needsClipBufferClear;
+ clipTestEnabled = other.clipTestEnabled;
+ currentClip = other.currentClip;
canRestoreClip = other.canRestoreClip;
rectangleClip = other.rectangleClip;
}
QOpenGL2PaintEngineState::QOpenGL2PaintEngineState()
{
- needsDepthBufferClear = true;
- depthTestEnabled = false;
- currentDepth = 1;
- maxDepth = 4;
+ isNew = true;
+ needsClipBufferClear = true;
+ clipTestEnabled = false;
canRestoreClip = true;
}
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
index f245945..5704a04 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
@@ -61,11 +61,13 @@
#include <private/qglpaintdevice_p.h>
#include <private/qglpixmapfilter_p.h>
#include <private/qfontengine_p.h>
+#include <private/qdatabuffer_p.h>
enum EngineMode {
ImageDrawingMode,
TextDrawingMode,
- BrushDrawingMode
+ BrushDrawingMode,
+ ImageArrayDrawingMode
};
QT_BEGIN_NAMESPACE
@@ -80,17 +82,18 @@ public:
QOpenGL2PaintEngineState();
~QOpenGL2PaintEngineState();
- bool needsDepthBufferClear;
- qreal depthBufferClearValue;
+ uint isNew : 1;
+ uint needsClipBufferClear : 1;
+ uint clipTestEnabled : 1;
+ uint canRestoreClip : 1;
+ uint matrixChanged : 1;
+ uint compositionModeChanged : 1;
+ uint opacityChanged : 1;
+ uint renderHintsChanged : 1;
+ uint clipChanged : 1;
+ uint currentClip : 8;
- bool depthTestEnabled;
- bool scissorTestEnabled;
- uint maxDepth;
- uint currentDepth;
-
- bool canRestoreClip;
QRect rectangleClip;
- bool hasRectangleClip;
};
class Q_OPENGL_EXPORT QGL2PaintEngineEx : public QPaintEngineEx
@@ -126,6 +129,8 @@ public:
virtual void drawTextItem(const QPointF &p, const QTextItem &textItem);
+ virtual void drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints);
+
Type type() const { return OpenGL2; }
void setState(QPainterState *s);
@@ -195,9 +200,9 @@ public:
// ^ returns whether the current program changed or not
inline void useSimpleShader();
- inline QColor premultiplyColor(QColor c, GLfloat opacity);
- float zValueForRenderText() const;
+ void prepareDepthRangeForRenderText();
+ void restoreDepthRangeForRenderText();
static QGLEngineShaderManager* shaderManagerForEngine(QGL2PaintEngineEx *engine) { return engine->d_func()->shaderManager; }
@@ -208,8 +213,6 @@ public:
EngineMode mode;
QFontEngineGlyphCache::Type glyphCacheType;
- mutable QOpenGL2PaintEngineState *last_created_state;
-
// Dirty flags
bool matrixDirty; // Implies matrix uniforms are also dirty
bool compositionModeDirty;
@@ -217,12 +220,12 @@ public:
bool brushUniformsDirty;
bool simpleShaderMatrixUniformDirty;
bool shaderMatrixUniformDirty;
- bool depthUniformDirty;
- bool simpleShaderDepthUniformDirty;
bool opacityUniformDirty;
+ bool stencilClean; // Has the stencil not been used for clipping so far?
QRegion dirtyStencilRegion;
QRect currentScissorBounds;
+ uint maxClip;
const QBrush* currentBrush; // May not be the state's brush!
@@ -230,6 +233,7 @@ public:
QGL2PEXVertexArray vertexCoordinateArray;
QGL2PEXVertexArray textureCoordinateArray;
+ QDataBuffer<GLfloat> opacityArray;
GLfloat staticVertexCoordinateArray[8];
GLfloat staticTextureCoordinateArray[8];
@@ -238,26 +242,16 @@ public:
QGLEngineShaderManager* shaderManager;
- void writeClip(const QVectorPath &path, uint depth);
- void updateDepthScissorTest();
+ void clearClip(uint value);
+ void writeClip(const QVectorPath &path, uint value);
+ void resetClipIfNeeded();
+
+ void updateClipScissorTest();
void setScissor(const QRect &rect);
- void regenerateDepthClip();
+ void regenerateClip();
void systemStateChanged();
uint use_system_clip : 1;
- static inline GLfloat rawDepth(uint depth)
- {
- // assume at least 16 bits in the depth buffer, and
- // use 2^15 depth levels to be safe with regard to
- // rounding issues etc
- return depth * (1.0f / GLfloat((1 << 15) - 1));
- }
-
- static inline GLfloat normalizedDeviceDepth(uint depth)
- {
- return 2.0f * rawDepth(depth) - 1.0f;
- }
-
uint location(QGLEngineShaderManager::Uniform uniform)
{
return shaderManager->getUniformLocation(uniform);
@@ -268,12 +262,16 @@ public:
bool needsSync;
bool inRenderText;
+ GLfloat depthRange[2];
+
float textureInvertedY;
QScopedPointer<QPixmapFilter> convolutionFilter;
QScopedPointer<QPixmapFilter> colorizeFilter;
QScopedPointer<QPixmapFilter> blurFilter;
QScopedPointer<QPixmapFilter> fastBlurFilter;
+ QScopedPointer<QPixmapFilter> dropShadowFilter;
+ QScopedPointer<QPixmapFilter> fastDropShadowFilter;
};
QT_END_NAMESPACE
diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp
index 1276443..8aef8b4 100644
--- a/src/opengl/qgl.cpp
+++ b/src/opengl/qgl.cpp
@@ -59,6 +59,8 @@
# include <private/qt_mac_p.h>
#endif
+#include <qdatetime.h>
+
#include <stdlib.h> // malloc
#include "qpixmap.h"
@@ -148,7 +150,25 @@ QGLSignalProxy *QGLSignalProxy::instance()
class QGLEngineSelector
{
public:
- QGLEngineSelector() : engineType(QPaintEngine::MaxUser) { }
+ QGLEngineSelector() : engineType(QPaintEngine::MaxUser)
+ {
+#ifdef Q_WS_MAC
+ // The ATI X1600 driver for Mac OS X does not support return
+ // values from functions in GLSL. Since working around this in
+ // the GL2 engine would require a big, ugly rewrite, we're
+ // falling back to the GL 1 engine..
+ QGLWidget *tmp = 0;
+ if (!QGLContext::currentContext()) {
+ tmp = new QGLWidget();
+ tmp->makeCurrent();
+ }
+ if (strstr((char *) glGetString(GL_RENDERER), "X1600"))
+ setPreferredPaintEngine(QPaintEngine::OpenGL);
+ if (tmp)
+ delete tmp;
+#endif
+
+ }
void setPreferredPaintEngine(QPaintEngine::Type type) {
if (type == QPaintEngine::OpenGL || type == QPaintEngine::OpenGL2)
@@ -1391,11 +1411,39 @@ bool operator!=(const QGLFormat& a, const QGLFormat& b)
/*****************************************************************************
QGLContext implementation
*****************************************************************************/
-QGLContextPrivate::~QGLContextPrivate()
+
+QGLContextGroup::~QGLContextGroup()
+{
+ // Clear any remaining QGLSharedResourceGuard objects on the group.
+ QGLSharedResourceGuard *guard = m_guards;
+ while (guard != 0) {
+ guard->m_group = 0;
+ guard->m_id = 0;
+ guard = guard->m_next;
+ }
+}
+
+void QGLContextGroup::addGuard(QGLSharedResourceGuard *guard)
+{
+ if (m_guards)
+ m_guards->m_prev = guard;
+ guard->m_next = m_guards;
+ guard->m_prev = 0;
+ m_guards = guard;
+}
+
+void QGLContextGroup::removeGuard(QGLSharedResourceGuard *guard)
{
- if (!reference->deref())
- delete reference;
+ if (guard->m_next)
+ guard->m_next->m_prev = guard->m_prev;
+ if (guard->m_prev)
+ guard->m_prev->m_next = guard->m_next;
+ else
+ m_guards = guard->m_next;
+}
+QGLContextPrivate::~QGLContextPrivate()
+{
if (!group->m_refs.deref()) {
Q_ASSERT(group->context() == q_ptr);
delete group;
@@ -1624,8 +1672,10 @@ void QGLTextureCache::pixmapCleanupHook(QPixmap* pixmap)
}
#if defined(Q_WS_X11)
QPixmapData *pd = pixmap->data_ptr().data();
- Q_ASSERT(pd->ref == 1); // Make sure reference counting isn't broken
- QGLContextPrivate::destroyGlSurfaceForPixmap(pd);
+ if (pd->classId() == QPixmapData::X11Class) {
+ Q_ASSERT(pd->ref == 1); // Make sure reference counting isn't broken
+ QGLContextPrivate::destroyGlSurfaceForPixmap(pd);
+ }
#endif
}
@@ -1718,7 +1768,7 @@ Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg()
the top left corner. Inverting the texture implies a deep copy
prior to upload.
- \value MipmapBindOption Specifies that bindTexture should try
+ \value MipmapBindOption Specifies that bindTexture() should try
to generate mipmaps. If the GL implementation supports the \c
GL_SGIS_generate_mipmap extension, mipmaps will be automatically
generated for the texture. Mipmap generation is only supported for
@@ -1804,6 +1854,8 @@ QGLContext::~QGLContext()
QGLTextureCache::instance()->removeContextTextures(this);
QGLTextureCache::deleteIfEmpty(); // ### thread safety
+ d_ptr->group->cleanupResources(this);
+
QGLSignalProxy::instance()->emitAboutToDestroyContext(this);
reset();
}
@@ -2077,6 +2129,8 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
#ifdef QGL_BIND_TEXTURE_DEBUG
printf("QGLContextPrivate::bindTexture(), imageSize=(%d,%d), internalFormat =0x%x, options=%x\n",
image.width(), image.height(), internalFormat, int(options));
+ QTime time;
+ time.start();
#endif
// Scale the pixmap if needed. GL textures needs to have the
@@ -2092,7 +2146,8 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
{
img = img.scaled(tx_w, tx_h);
#ifdef QGL_BIND_TEXTURE_DEBUG
- printf(" - upscaled to %dx%d\n", tx_w, tx_h);
+ printf(" - upscaled to %dx%d (%d ms)\n", tx_w, tx_h, time.elapsed());
+
#endif
}
@@ -2112,7 +2167,7 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
&& options & QGLContext::MipmapBindOption)
{
#ifdef QGL_BIND_TEXTURE_DEBUG
- printf(" - generating mipmaps\n");
+ printf(" - generating mipmaps (%d ms)\n", time.elapsed());
#endif
#if !defined(QT_OPENGL_ES_2)
glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
@@ -2148,7 +2203,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 \n");
+ printf(" - converting ARGB32 -> ARGB32_Premultiplied (%d ms) \n", time.elapsed());
#endif
}
break;
@@ -2156,7 +2211,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\n");
+ printf(" - converting ARGB32_Premultiplied -> ARGB32 (%d ms)\n", time.elapsed());
#endif
}
break;
@@ -2173,19 +2228,19 @@ 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\n");
+ printf(" - converting 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\n");
+ printf(" - converting to 32-bit (%d ms)\n", time.elapsed());
#endif
}
}
if (options & QGLContext::InvertedYBindOption) {
#ifdef QGL_BIND_TEXTURE_DEBUG
- printf(" - flipping bits over y\n");
+ printf(" - flipping bits over y (%d ms)\n", time.elapsed());
#endif
int ipl = img.bytesPerLine() / 4;
int h = img.height();
@@ -2199,7 +2254,7 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
if (externalFormat == GL_RGBA) {
#ifdef QGL_BIND_TEXTURE_DEBUG
- printf(" - doing byte swapping\n");
+ 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
@@ -2242,6 +2297,13 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
}
#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);
+#endif
+
+
// this assumes the size of a texture is always smaller than the max cache size
int cost = img.width()*img.height()*4/1024;
QGLTexture *texture = new QGLTexture(q, tx_id, target, options);
@@ -2254,7 +2316,7 @@ QGLTexture *QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum targe
Q_Q(QGLContext);
QGLTexture *texture = QGLTextureCache::instance()->getTexture(key);
if (texture && texture->target == target
- && (texture->context == q || qgl_share_reg()->checkSharing(q, texture->context)))
+ && (texture->context == q || QGLContext::areSharing(q, texture->context)))
{
return texture;
}
@@ -2361,6 +2423,8 @@ GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format)
}
/*!
+ \since 4.6
+
Generates and binds a 2D GL texture to the current context, based
on \a image. The generated texture id is returned and can be used
in later \c glBindTexture() calls.
@@ -2422,6 +2486,7 @@ GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint forma
/*!
\overload
+ \since 4.6
Generates and binds a 2D GL texture to the current context, based
on \a pixmap.
@@ -2755,6 +2820,20 @@ void QGLContext::setDevice(QPaintDevice *pDev)
*/
/*!
+ Returns true if \a context1 and \a context2 are sharing their
+ GL resources such as textures, shader programs, etc;
+ otherwise returns false.
+
+ \since 4.6
+*/
+bool QGLContext::areSharing(const QGLContext *context1, const QGLContext *context2)
+{
+ if (!context1 || !context2)
+ return false;
+ return context1->d_ptr->group == context2->d_ptr->group;
+}
+
+/*!
\fn bool QGLContext::deviceIsPixmap() const
Returns true if the paint device of this context is a pixmap;
@@ -3722,6 +3801,11 @@ bool QGLWidget::event(QEvent *e)
glFinish();
doneCurrent();
} else if (e->type() == QEvent::ParentChange) {
+ // if we've reparented a window that has the current context
+ // bound, we need to rebind that context to the new window id
+ if (d->glcx == QGLContext::currentContext())
+ makeCurrent();
+
if (d->glcx->d_func()->screen != d->xinfo.screen() || testAttribute(Qt::WA_TranslucentBackground)) {
setContext(new QGLContext(d->glcx->requestedFormat(), this));
// ### recreating the overlay isn't supported atm
@@ -4489,6 +4573,7 @@ GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format)
/*!
\overload
+ \since 4.6
The binding \a options are a set of options used to decide how to
bind the texture to the context.
@@ -4530,6 +4615,7 @@ GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format
/*!
\overload
+ \since 4.6
Generates and binds a 2D GL texture to the current context, based
on \a pixmap. The generated texture id is returned and can be used in
@@ -4835,67 +4921,57 @@ Q_OPENGL_EXPORT const QString qt_gl_library_name()
}
#endif
-bool QGLShareRegister::checkSharing(const QGLContext *context1, const QGLContext *context2) {
- bool sharing = (context1 && context2 && context1->d_ptr->group == context2->d_ptr->group);
- return sharing;
-}
-
void QGLShareRegister::addShare(const QGLContext *context, const QGLContext *share) {
Q_ASSERT(context && share);
if (context->d_ptr->group == share->d_ptr->group)
return;
// Make sure 'context' is not already shared with another group of contexts.
- Q_ASSERT(reg.find(context->d_ptr->group) == reg.end());
Q_ASSERT(context->d_ptr->group->m_refs == 1);
// Free 'context' group resources and make it use the same resources as 'share'.
+ QGLContextGroup *group = share->d_ptr->group;
delete context->d_ptr->group;
- context->d_ptr->group = share->d_ptr->group;
- context->d_ptr->group->m_refs.ref();
+ context->d_ptr->group = group;
+ group->m_refs.ref();
// Maintain a list of all the contexts in each group of sharing contexts.
- SharingHash::iterator it = reg.find(share->d_ptr->group);
- if (it == reg.end())
- it = reg.insert(share->d_ptr->group, ContextList() << share);
- it.value() << context;
+ // The list is empty if the "share" context wasn't sharing already.
+ if (group->m_shares.isEmpty())
+ group->m_shares.append(share);
+ group->m_shares.append(context);
}
QList<const QGLContext *> QGLShareRegister::shares(const QGLContext *context) {
- SharingHash::const_iterator it = reg.find(context->d_ptr->group);
- if (it == reg.end())
- return ContextList();
- return it.value();
+ return context->d_ptr->group->m_shares;
}
void QGLShareRegister::removeShare(const QGLContext *context) {
- SharingHash::iterator it = reg.find(context->d_ptr->group);
- if (it == reg.end())
+ // Remove the context from the group.
+ QGLContextGroup *group = context->d_ptr->group;
+ if (group->m_shares.isEmpty())
return;
-
- int count = it.value().removeAll(context);
- Q_ASSERT(count == 1);
- Q_UNUSED(count);
+ group->m_shares.removeAll(context);
// Update context group representative.
- if (context->d_ptr->group->m_context == context)
- context->d_ptr->group->m_context = it.value().first();
+ Q_ASSERT(group->m_shares.size() != 0);
+ if (group->m_context == context)
+ group->m_context = group->m_shares[0];
- Q_ASSERT(it.value().size() != 0);
- if (it.value().size() == 1)
- reg.erase(it);
+ // If there is only one context left, then make the list empty.
+ if (group->m_shares.size() == 1)
+ group->m_shares.clear();
}
-QGLContextResource::QGLContextResource(FreeFunc f, QObject *parent)
- : QObject(parent), free(f)
+QGLContextResource::QGLContextResource(FreeFunc f)
+ : free(f), active(0)
{
- connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext *)), this, SLOT(removeOne(const QGLContext *)));
}
QGLContextResource::~QGLContextResource()
{
#ifndef QT_NO_DEBUG
- if (m_resources.size()) {
+ if (active != 0) {
qWarning("QtOpenGL: Resources are still available at program shutdown.\n"
" This is possibly caused by a leaked QGLWidget, \n"
" QGLFramebufferObject or QGLPixelBuffer.");
@@ -4905,121 +4981,53 @@ QGLContextResource::~QGLContextResource()
void QGLContextResource::insert(const QGLContext *key, void *value)
{
- QList<const QGLContext *> shares = qgl_share_reg()->shares(key);
- if (shares.size() == 0)
- shares.append(key);
- void *oldValue = 0;
- for (int i = 0; i < shares.size(); ++i) {
- ResourceHash::iterator it = m_resources.find(shares.at(i));
- if (it != m_resources.end()) {
- Q_ASSERT(oldValue == 0 || oldValue == it.value());
- oldValue = it.value();
- it.value() = value;
- } else {
- m_resources.insert(shares.at(i), value);
- }
- }
- if (oldValue != 0 && oldValue != value) {
- QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
- if (oldContext != key)
- const_cast<QGLContext *>(key)->makeCurrent();
- free(oldValue);
- if (oldContext && oldContext != key)
- oldContext->makeCurrent();
- }
+ QGLContextGroup *group = QGLContextPrivate::contextGroup(key);
+ Q_ASSERT(!group->m_resources.contains(this));
+ group->m_resources.insert(this, value);
+ active.ref();
}
void *QGLContextResource::value(const QGLContext *key)
{
- ResourceHash::const_iterator it = m_resources.find(key);
- // Check if there is a value associated with 'key'.
- if (it != m_resources.end())
- return it.value();
- // Check if there is a value associated with sharing contexts.
- QList<const QGLContext *> shares = qgl_share_reg()->shares(key);
- for (int i = 0; i < shares.size() && it == m_resources.end(); ++i)
- it = m_resources.find(shares.at(i));
- if (it == m_resources.end())
- return 0; // Didn't find anything.
-
- // Found something! Share this info with all the buddies.
- for (int i = 0; i < shares.size(); ++i)
- m_resources.insert(shares.at(i), it.value());
- return it.value();
-}
-
-void QGLContextResource::removeGroup(const QGLContext *key)
-{
- QList<const QGLContext *> shares = qgl_share_reg()->shares(key);
- if (shares.size() == 0)
- shares.append(key);
- void *oldValue = 0;
- for (int i = 0; i < shares.size(); ++i) {
- ResourceHash::iterator it = m_resources.find(shares.at(i));
- if (it != m_resources.end()) {
- Q_ASSERT(oldValue == 0 || oldValue == it.value());
- oldValue = it.value();
- m_resources.erase(it);
- }
- }
- if (oldValue != 0) {
- QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
- if (oldContext != key)
- const_cast<QGLContext *>(key)->makeCurrent();
- free(oldValue);
- if (oldContext && oldContext != key)
- oldContext->makeCurrent();
- }
+ QGLContextGroup *group = QGLContextPrivate::contextGroup(key);
+ return group->m_resources.value(this, 0);
+}
+
+void QGLContextResource::cleanup(const QGLContext *ctx, void *value)
+{
+ QGLShareContextScope scope(ctx);
+ free(value);
+ active.deref();
}
-void QGLContextResource::removeOne(const QGLContext *key)
+void QGLContextGroup::cleanupResources(const QGLContext *ctx)
{
- ResourceHash::iterator it = m_resources.find(key);
- if (it == m_resources.end())
+ // If there are still shares, then no cleanup to be done yet.
+ if (m_shares.size() > 1)
return;
- QList<const QGLContext *> shares = qgl_share_reg()->shares(key);
- if (shares.size() > 1) {
- Q_ASSERT(key->isSharing());
- // At least one of the shared contexts must stay in the cache.
- // Otherwise, the value pointer is lost.
- for (int i = 0; i < 2/*shares.size()*/; ++i)
- m_resources.insert(shares.at(i), it.value());
- } else {
- QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
- if (oldContext != key)
- const_cast<QGLContext *>(key)->makeCurrent();
- free(it.value());
- if (oldContext && oldContext != key)
- oldContext->makeCurrent();
- }
- m_resources.erase(it);
+ // Iterate over all resources and free each in turn.
+ QHash<QGLContextResource *, void *>::ConstIterator it;
+ for (it = m_resources.begin(); it != m_resources.end(); ++it)
+ it.key()->cleanup(ctx, it.value());
}
-QGLContextReference::QGLContextReference(const QGLContext *ctx)
- : m_ref(1), m_ctx(ctx)
+QGLSharedResourceGuard::~QGLSharedResourceGuard()
{
- connect(QGLSignalProxy::instance(),
- SIGNAL(aboutToDestroyContext(const QGLContext *)),
- this, SLOT(aboutToDestroyContext(const QGLContext *)));
+ if (m_group)
+ m_group->removeGuard(this);
}
-void QGLContextReference::aboutToDestroyContext(const QGLContext *ctx)
+void QGLSharedResourceGuard::setContext(const QGLContext *context)
{
- // Bail out if our context is not being destroyed.
- if (ctx != m_ctx || !m_ctx)
- return;
-
- // Find some other context that this one is shared with to take over.
- QList<const QGLContext *> shares = qgl_share_reg()->shares(m_ctx);
- shares.removeAll(m_ctx);
- if (!shares.isEmpty()) {
- m_ctx = shares[0];
- return;
+ if (m_group)
+ m_group->removeGuard(this);
+ if (context) {
+ m_group = QGLContextPrivate::contextGroup(context);
+ m_group->addGuard(this);
+ } else {
+ m_group = 0;
}
-
- // No more contexts sharing with this one, so the reference is now invalid.
- m_ctx = 0;
}
QT_END_NAMESPACE
diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h
index 1776004..b1c1317 100644
--- a/src/opengl/qgl.h
+++ b/src/opengl/qgl.h
@@ -282,6 +282,8 @@ public:
bool isSharing() const;
void reset();
+ static bool areSharing(const QGLContext *context1, const QGLContext *context2);
+
QGLFormat format() const;
QGLFormat requestedFormat() const;
void setFormat(const QGLFormat& format);
diff --git a/src/opengl/qgl_egl.cpp b/src/opengl/qgl_egl.cpp
index 6c93bea..fbf0349 100644
--- a/src/opengl/qgl_egl.cpp
+++ b/src/opengl/qgl_egl.cpp
@@ -75,7 +75,7 @@ void qt_egl_set_format(QEglProperties& props, int deviceType, const QGLFormat& f
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());
+ props.setValue(EGL_SAMPLES, f.samples() == -1 ? 1 : f.samples());
} else {
props.setValue(EGL_SAMPLE_BUFFERS, 0);
}
@@ -142,8 +142,19 @@ void QGLContext::reset()
d->cleanup();
doneCurrent();
if (d->eglContext) {
- if (d->eglSurface != EGL_NO_SURFACE)
- eglDestroySurface(d->eglContext->display(), d->eglSurface);
+ if (d->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 (d->paintDevice->devType() == QInternal::Widget) {
+ QGLWidget* w = static_cast<QGLWidget*>(d->paintDevice);
+
+ if (w->d_func()->eglSurfaceWindowId == w->winId())
+ eglDestroySurface(d->eglContext->display(), d->eglSurface);
+ } else
+#endif
+ eglDestroySurface(d->eglContext->display(), d->eglSurface);
+ }
delete d->eglContext;
}
d->eglContext = 0;
diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h
index 2e8ac88..8d4f673 100644
--- a/src/opengl/qgl_p.h
+++ b/src/opengl/qgl_p.h
@@ -219,6 +219,9 @@ public:
#endif
};
+class QGLContextResource;
+class QGLSharedResourceGuard;
+
// QGLContextPrivate has the responsibility of creating context groups.
// QGLContextPrivate and QGLShareRegister will both maintain the reference counter and destroy
// context groups when needed.
@@ -226,41 +229,29 @@ public:
class QGLContextGroup
{
public:
+ ~QGLContextGroup();
+
QGLExtensionFuncs &extensionFuncs() {return m_extensionFuncs;}
const QGLContext *context() const {return m_context;}
+
+ void addGuard(QGLSharedResourceGuard *guard);
+ void removeGuard(QGLSharedResourceGuard *guard);
private:
- QGLContextGroup(const QGLContext *context) : m_context(context), m_refs(1) { }
+ QGLContextGroup(const QGLContext *context) : m_context(context), m_guards(0), m_refs(1) { }
QGLExtensionFuncs m_extensionFuncs;
const QGLContext *m_context; // context group's representative
+ QList<const QGLContext *> m_shares;
+ QHash<QGLContextResource *, void *> m_resources;
+ QGLSharedResourceGuard *m_guards; // double-linked list of active guards.
QAtomicInt m_refs;
+ void cleanupResources(const QGLContext *ctx);
+
friend class QGLShareRegister;
+ friend class QGLContext;
friend class QGLContextPrivate;
-};
-
-// Reference to a QGLContext which automatically switches to another
-// shared context when the main one is destroyed. If there is no
-// shared context to switch to, the context pointer is set to null.
-// Note: should be merged into QGLContextGroup at some point.
-class QGLContextReference : public QObject
-{
- Q_OBJECT
-public:
- QGLContextReference(const QGLContext *ctx);
- ~QGLContextReference() {}
-
- const QGLContext *context() const { return m_ctx; }
-
- void ref() { m_ref.ref(); }
- bool deref() { return m_ref.deref(); }
-
-private slots:
- void aboutToDestroyContext(const QGLContext *ctx);
-
-private:
- QAtomicInt m_ref;
- const QGLContext *m_ctx;
+ friend class QGLContextResource;
};
class QGLTexture;
@@ -269,7 +260,7 @@ class QGLContextPrivate
{
Q_DECLARE_PUBLIC(QGLContext)
public:
- explicit QGLContextPrivate(QGLContext *context) : internal_context(false), q_ptr(context) {reference = new QGLContextReference(context); group = new QGLContextGroup(context);}
+ explicit QGLContextPrivate(QGLContext *context) : internal_context(false), q_ptr(context) {group = new QGLContextGroup(context);}
~QGLContextPrivate();
QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format,
QGLContext::BindOptions options);
@@ -332,7 +323,6 @@ public:
QGLContext *q_ptr;
QGLFormat::OpenGLVersionFlags version_flags;
- QGLContextReference *reference;
QGLContextGroup *group;
GLint max_texture_size;
@@ -343,13 +333,11 @@ public:
#ifdef Q_WS_WIN
static inline QGLExtensionFuncs& extensionFuncs(const QGLContext *ctx) { return ctx->d_ptr->group->extensionFuncs(); }
- static inline QGLExtensionFuncs& extensionFuncs(QGLContextGroup *ctx) { return ctx->extensionFuncs(); }
#endif
#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 inline QGLExtensionFuncs& extensionFuncs(QGLContextGroup *) { return qt_extensionFuncs; }
#endif
static void setCurrentContext(QGLContext *context);
@@ -399,25 +387,59 @@ public:
Q_DECLARE_OPERATORS_FOR_FLAGS(QGLExtensions::Extensions)
-class QGLShareRegister
+class Q_AUTOTEST_EXPORT QGLShareRegister
{
public:
QGLShareRegister() {}
- ~QGLShareRegister() { reg.clear(); }
+ ~QGLShareRegister() {}
- bool checkSharing(const QGLContext *context1, const QGLContext *context2);
void addShare(const QGLContext *context, const QGLContext *share);
QList<const QGLContext *> shares(const QGLContext *context);
void removeShare(const QGLContext *context);
-private:
- // Use a context's 'group' pointer to uniquely identify a group.
- typedef QList<const QGLContext *> ContextList;
- typedef QHash<const QGLContextGroup *, ContextList> SharingHash;
- SharingHash reg;
};
extern Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg();
+// Temporarily make a context current if not already current or
+// shared with the current contex. The previous context is made
+// current when the object goes out of scope.
+class Q_OPENGL_EXPORT QGLShareContextScope
+{
+public:
+ QGLShareContextScope(const QGLContext *ctx)
+ : m_oldContext(0)
+ {
+ QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext());
+ if (currentContext != ctx && !QGLContext::areSharing(ctx, currentContext)) {
+ m_oldContext = currentContext;
+ m_ctx = const_cast<QGLContext *>(ctx);
+ m_ctx->makeCurrent();
+ } else {
+ m_ctx = currentContext;
+ }
+ }
+
+ operator QGLContext *()
+ {
+ return m_ctx;
+ }
+
+ QGLContext *operator->()
+ {
+ return m_ctx;
+ }
+
+ ~QGLShareContextScope()
+ {
+ if (m_oldContext)
+ m_oldContext->makeCurrent();
+ }
+
+private:
+ QGLContext *m_oldContext;
+ QGLContext *m_ctx;
+};
+
class QGLTexture {
public:
QGLTexture(QGLContext *ctx = 0, GLuint tx_id = 0, GLenum tx_target = GL_TEXTURE_2D,
@@ -433,12 +455,8 @@ public:
~QGLTexture() {
if (options & QGLContext::MemoryManagedBindOption) {
- QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext());
- QGLContext *ctx = const_cast<QGLContext *>(context);
- Q_ASSERT(ctx);
- bool switch_context = current != ctx && !qgl_share_reg()->checkSharing(current, ctx);
- if (switch_context)
- ctx->makeCurrent();
+ Q_ASSERT(context);
+ QGLShareContextScope scope(context);
#if defined(Q_WS_X11)
// Although glXReleaseTexImage is a glX call, it must be called while there
// is a current context - the context the pixmap was bound to a texture in.
@@ -448,8 +466,6 @@ public:
QGLContextPrivate::unbindPixmapFromTexture(boundPixmap);
#endif
glDeleteTextures(1, &id);
- if (switch_context && current)
- current->makeCurrent();
}
}
@@ -491,8 +507,6 @@ private:
#ifdef Q_WS_QWS
extern QPaintEngine* qt_qgl_paint_engine();
-
-extern EGLDisplay qt_qgl_egl_display();
#endif
bool qt_gl_preferGL2Engine();
@@ -515,66 +529,21 @@ inline GLenum qt_gl_preferredTextureTarget()
}
// One resource per group of shared contexts.
-class QGLContextResource : public QObject
+class Q_AUTOTEST_EXPORT QGLContextResource
{
- Q_OBJECT
public:
typedef void (*FreeFunc)(void *);
- QGLContextResource(FreeFunc f, QObject *parent = 0);
+ QGLContextResource(FreeFunc f);
~QGLContextResource();
// Set resource 'value' for 'key' and all its shared contexts.
void insert(const QGLContext *key, void *value);
// Return resource for 'key' or a shared context.
void *value(const QGLContext *key);
- // Free resource for 'key' and all its shared contexts.
- void removeGroup(const QGLContext *key);
-private slots:
- // Remove entry 'key' from cache and delete resource if there are no shared contexts.
- void removeOne(const QGLContext *key);
+ // Cleanup 'value' in response to a context group being destroyed.
+ void cleanup(const QGLContext *ctx, void *value);
private:
- typedef QHash<const QGLContext *, void *> ResourceHash;
- ResourceHash m_resources;
FreeFunc free;
-};
-
-// Temporarily make a context current if not already current or
-// shared with the current contex. The previous context is made
-// current when the object goes out of scope.
-class Q_OPENGL_EXPORT QGLShareContextScope
-{
-public:
- QGLShareContextScope(const QGLContext *ctx)
- : m_oldContext(0)
- {
- QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext());
- if (currentContext != ctx && !qgl_share_reg()->checkSharing(ctx, currentContext)) {
- m_oldContext = currentContext;
- m_ctx = const_cast<QGLContext *>(ctx);
- m_ctx->makeCurrent();
- } else {
- m_ctx = currentContext;
- }
- }
-
- operator QGLContext *()
- {
- return m_ctx;
- }
-
- QGLContext *operator->()
- {
- return m_ctx;
- }
-
- ~QGLShareContextScope()
- {
- if (m_oldContext)
- m_oldContext->makeCurrent();
- }
-
-private:
- QGLContext *m_oldContext;
- QGLContext *m_ctx;
+ QAtomicInt active;
};
// Put a guard around a GL object identifier and its context.
@@ -586,44 +555,27 @@ class Q_OPENGL_EXPORT QGLSharedResourceGuard
{
public:
QGLSharedResourceGuard(const QGLContext *context)
- : m_ctxref(0), m_id(0)
+ : m_group(0), m_id(0), m_next(0), m_prev(0)
{
setContext(context);
}
QGLSharedResourceGuard(const QGLContext *context, GLuint id)
- : m_ctxref(0), m_id(id)
+ : m_group(0), m_id(id), m_next(0), m_prev(0)
{
setContext(context);
}
- ~QGLSharedResourceGuard()
- {
- if (m_ctxref && !m_ctxref->deref())
- delete m_ctxref;
- }
+ ~QGLSharedResourceGuard();
const QGLContext *context() const
{
- return m_ctxref ? m_ctxref->context() : 0;
+ return m_group ? m_group->context() : 0;
}
- void setContext(const QGLContext *context)
- {
- if (m_ctxref && !m_ctxref->deref())
- delete m_ctxref;
- if (context) {
- m_ctxref = context->d_ptr->reference;
- m_ctxref->ref();
- } else {
- m_ctxref = 0;
- }
- }
+ void setContext(const QGLContext *context);
GLuint id() const
{
- if (m_ctxref && m_ctxref->context())
- return m_id;
- else
- return 0;
+ return m_id;
}
void setId(GLuint id)
@@ -632,8 +584,12 @@ public:
}
private:
- QGLContextReference *m_ctxref;
+ QGLContextGroup *m_group;
GLuint m_id;
+ QGLSharedResourceGuard *m_next;
+ QGLSharedResourceGuard *m_prev;
+
+ friend class QGLContextGroup;
};
QT_END_NAMESPACE
diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp
index 67f391b..971a660 100644
--- a/src/opengl/qgl_x11egl.cpp
+++ b/src/opengl/qgl_x11egl.cpp
@@ -86,9 +86,19 @@ bool QGLContext::chooseContext(const QGLContext* shareContext)
qt_egl_add_platform_config(configProps, device());
configProps.setRenderableType(QEgl::OpenGL);
+ QEgl::PixelFormatMatch matchType = QEgl::BestPixelFormat;
+ if (device()->depth() == 16) {
+ 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;
+ }
+ configProps.setRenderableType(QEgl::OpenGL);
+
// Search for a matching configuration, reducing the complexity
// each time until we get something that matches.
- if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) {
+ if (!d->eglContext->chooseConfig(configProps, matchType)) {
delete d->eglContext;
d->eglContext = 0;
return false;
@@ -140,85 +150,36 @@ void QGLWidget::updateOverlayGL()
//handle overlay
}
-void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
+bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig config, const QX11Info &x11Info, bool useArgbVisual)
{
- Q_D(QGLWidget);
- if (context == 0) {
- qWarning("QGLWidget::setContext: Cannot set null context");
- return;
- }
- if (!context->deviceIsPixmap() && context->device() != this) {
- qWarning("QGLWidget::setContext: Context must refer to this widget");
- return;
- }
-
- if (d->glcx)
- d->glcx->doneCurrent();
- QGLContext* oldcx = d->glcx;
- d->glcx = context;
-
- if (parentWidget()) {
- // force creation of delay-created widgets
- parentWidget()->winId();
- if (parentWidget()->x11Info().screen() != x11Info().screen())
- d_func()->xinfo = parentWidget()->d_func()->xinfo;
- }
-
- // If the application has set WA_TranslucentBackground and not explicitly set
- // the alpha buffer size to zero, modify the format so it have an alpha channel
- QGLFormat& fmt = d->glcx->d_func()->glFormat;
- const bool useArgbVisual = testAttribute(Qt::WA_TranslucentBackground);
- if (useArgbVisual && fmt.alphaBufferSize() == -1)
- fmt.setAlphaBufferSize(1);
-
- bool createFailed = false;
- if (!d->glcx->isValid()) {
- if (!d->glcx->create(shareContext ? shareContext : oldcx))
- createFailed = true;
- }
- if (createFailed) {
- if (deleteOldContext)
- delete oldcx;
- return;
- }
-
- if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) {
- if (deleteOldContext)
- delete oldcx;
- return;
- }
-
- bool visible = isVisible();
- if (visible)
- hide();
+ bool foundVisualIsArgb = useArgbVisual;
- XVisualInfo vi;
memset(&vi, 0, sizeof(XVisualInfo));
// Check to see if EGL is suggesting an appropriate visual id:
EGLint nativeVisualId;
- QEglContext* qeglCtx = d->glcx->d_func()->eglContext;
- qeglCtx->configAttrib(EGL_NATIVE_VISUAL_ID, &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);
+ 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);
+ format = XRenderFindVisualFormat(x11Info.display(), chosenVisualInfo->visual);
if (format->type == PictTypeDirect && format->direct.alphaMask) {
-// qDebug("Using opaque X Visual ID (%d) provided by EGL", (int)vi.visualid);
+// qDebug("Using ARGB X Visual ID (%d) provided by EGL", (int)vi.visualid);
+ foundVisualIsArgb = true;
vi = *chosenVisualInfo;
}
else {
qWarning("Warning: EGL suggested using X visual ID %d for config %d, but this is not ARGB",
- nativeVisualId, (int)qeglCtx->config());
+ nativeVisualId, (int)config);
vi.visualid = 0;
}
} else
@@ -231,32 +192,32 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext,
}
else {
qWarning("Warning: EGL suggested using X visual ID %d for config %d, but this seems to be invalid!",
- nativeVisualId, (int)qeglCtx->config());
+ 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
-
- bool useArgb = context->format().alpha() && !context->deviceIsPixmap();
+
#if !defined(QT_NO_XRENDER)
- if (vi.visualid == 0 && useArgb) {
+ if (vi.visualid == 0 && useArgbVisual) {
// Try to use XRender to find an ARGB visual we can use
- vi.screen = x11Info().screen();
- vi.depth = 32;
+ 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(),
+ 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);
+ format = XRenderFindVisualFormat(x11Info.display(), matchingVisuals[i].visual);
if (format->type == PictTypeDirect && format->direct.alphaMask) {
vi = matchingVisuals[i];
+ foundVisualIsArgb = true;
// qDebug("Using X Visual ID (%d) for ARGB visual as provided by XRender", (int)vi.visualid);
break;
}
@@ -267,17 +228,17 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext,
if (vi.visualid == 0) {
EGLint depth;
- qeglCtx->configAttrib(EGL_BUFFER_SIZE, &depth);
+ eglGetConfigAttrib(display, config, EGL_BUFFER_SIZE, &depth);
int err;
- err = XMatchVisualInfo(x11Info().display(), x11Info().screen(), depth, TrueColor, &vi);
+ 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)qeglCtx->config(), depth);
- depth = x11Info().depth();
- err = XMatchVisualInfo(x11Info().display(), x11Info().screen(), depth, TrueColor, &vi);
+ (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;
+ return false;
} else
qWarning(" - Falling back to X11 suggested depth (%d)", depth);
}
@@ -285,8 +246,8 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext,
// qDebug("Using X Visual ID (%d) for EGL provided depth (%d)", (int)vi.visualid, depth);
// Don't try to use ARGB now unless the visual is 32-bit - even then it might stil fail :-(
- if (useArgb)
- useArgb = vi.depth == 32;
+ if (useArgbVisual)
+ foundVisualIsArgb = vi.depth == 32; //### We might at some point (soon) get ARGB4444
}
// qDebug("Visual Info:");
@@ -299,6 +260,65 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext,
// qDebug(" depth=%d", vi.depth);
// qDebug(" screen=%d", vi.screen);
// qDebug(" visualid=%d", vi.visualid);
+ return foundVisualIsArgb;
+}
+
+void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+ if (!context->deviceIsPixmap() && context->device() != this) {
+ qWarning("QGLWidget::setContext: Context must refer to this widget");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+
+ if (parentWidget()) {
+ // force creation of delay-created widgets
+ parentWidget()->winId();
+ if (parentWidget()->x11Info().screen() != x11Info().screen())
+ d_func()->xinfo = parentWidget()->d_func()->xinfo;
+ }
+
+ // If the application has set WA_TranslucentBackground and not explicitly set
+ // the alpha buffer size to zero, modify the format so it have an alpha channel
+ QGLFormat& fmt = d->glcx->d_func()->glFormat;
+ const bool tryArgbVisual = testAttribute(Qt::WA_TranslucentBackground);
+ if (tryArgbVisual && fmt.alphaBufferSize() == -1)
+ fmt.setAlphaBufferSize(1);
+
+ bool createFailed = false;
+ if (!d->glcx->isValid()) {
+ if (!d->glcx->create(shareContext ? shareContext : oldcx))
+ createFailed = true;
+ }
+ if (createFailed) {
+ if (deleteOldContext)
+ delete oldcx;
+ return;
+ }
+
+ if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) {
+ if (deleteOldContext)
+ delete oldcx;
+ return;
+ }
+
+ bool visible = isVisible();
+ if (visible)
+ hide();
+
+ XVisualInfo vi;
+ QEglContext *eglContext = d->glcx->d_func()->eglContext;
+ bool usingArgbVisual = qt_egl_setup_x11_visual(vi, eglContext->display(), eglContext->config(),
+ x11Info(), tryArgbVisual);
XSetWindowAttributes a;
@@ -311,7 +331,7 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext,
a.border_pixel = colmap.pixel(Qt::black);
unsigned int valueMask = CWBackPixel|CWBorderPixel;
- if(useArgb) {
+ if (usingArgbVisual) {
a.colormap = XCreateColormap(x11Info().display(), p, vi.visual, AllocNone);
valueMask |= CWColormap;
}
@@ -408,6 +428,103 @@ void QGLWidgetPrivate::recreateEglSurface(bool force)
}
}
+// 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::defaultDisplay(0), 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 (*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::defaultDisplay(0),
+ pixmapConfig,
+ (EGLNativePixmapType) pixmapData->handle(),
+ pixmapAttribs.properties());
+ if (pixmapSurface == EGL_NO_SURFACE) {
+ qWarning("Failed to create a pixmap surface using config %d", (int)pixmapConfig);
+ return false;
+ }
+
+ Q_ASSERT(sizeof(Qt::HANDLE) >= sizeof(EGLSurface)); // Just to make totally sure!
+ pixmapData->gl_surface = (Qt::HANDLE)pixmapSurface;
+ pixmapData->is_cached = true; // Make sure the cleanup hook gets called
+
+ return true;
+}
+
QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, const qint64 key,
QGLContext::BindOptions options)
@@ -449,82 +566,14 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, cons
destroyGlSurfaceForPixmap(pixmapData);
}
- EGLint pixmapAttribs[] = {
- EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
- EGL_TEXTURE_FORMAT, hasAlpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB,
- EGL_NONE
- };
- Q_ASSERT(sizeof(Qt::HANDLE) >= sizeof(EGLSurface)); // Just to make totally sure!
- if (pixmapData->gl_surface == 0)
- pixmapData->gl_surface = (Qt::HANDLE)EGL_NO_SURFACE;
- EGLSurface pixmapSurface = (EGLSurface)pixmapData->gl_surface;
- static EGLConfig pixmapRGBConfig = 0;
- static EGLConfig pixmapRGBAConfig = 0;
-
- // Check to see if we need to find a config
- if ((hasAlpha && !pixmapRGBAConfig) || (!hasAlpha && !pixmapRGBConfig) ) {
- const EGLint configAttribs[] = {
- EGL_SURFACE_TYPE, EGL_PIXMAP_BIT,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_DEPTH_SIZE, 0,
- hasAlpha ? EGL_BIND_TO_TEXTURE_RGBA : EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE,
- EGL_NONE
- };
-
- EGLint configCount = 0;
- eglChooseConfig(eglContext->display(), configAttribs, 0, 256, &configCount);
- if (configCount == 0) {
- haveTFP = false;
- qWarning("bindTextureFromNativePixmap() - Couldn't find a suitable config");
- return 0;
- }
-
- EGLConfig *configList = new EGLConfig[configCount];
- eglChooseConfig(eglContext->display(), configAttribs, configList, configCount, &configCount);
- Q_ASSERT(configCount);
-
- // Try to create a pixmap surface for each config until one works
- for (int i = 0; i < configCount; ++i) {
- pixmapSurface = eglCreatePixmapSurface(eglContext->display(), configList[i],
- (EGLNativePixmapType) pixmapData->handle(),
- pixmapAttribs);
- if (pixmapSurface != EGL_NO_SURFACE) {
- // Got one!
- qDebug() << "Found an" << (hasAlpha ? "ARGB" : "RGB")
- << "config (" << int(configList[i]) << ") to create a pixmap surface";
- if (hasAlpha)
- pixmapRGBAConfig = configList[i];
- else
- pixmapRGBConfig = configList[i];
- pixmapData->gl_surface = (Qt::HANDLE)pixmapSurface;
- break;
- }
- }
- delete configList;
-
- if ((hasAlpha && !pixmapRGBAConfig) || (!hasAlpha && !pixmapRGBConfig) ) {
- qDebug("Couldn't create a pixmap surface with any of the provided configs");
- haveTFP = false;
- return 0;
- }
- }
-
- if (pixmapSurface == EGL_NO_SURFACE) {
- pixmapSurface = eglCreatePixmapSurface(eglContext->display(),
- hasAlpha? pixmapRGBAConfig : pixmapRGBConfig,
- (EGLNativePixmapType) pixmapData->handle(),
- pixmapAttribs);
- if (pixmapSurface == EGL_NO_SURFACE) {
- qWarning("Failed to create a pixmap surface using config %d",
- (int)(hasAlpha? pixmapRGBAConfig : pixmapRGBConfig));
+ if (pixmapData->gl_surface == 0) {
+ bool success = qt_createEGLSurfaceForPixmap(pixmapData, true);
+ if (!success) {
haveTFP = false;
return 0;
}
- pixmapData->gl_surface = (Qt::HANDLE)pixmapSurface;
}
- // Make sure the cleanup hook gets called so we can delete the glx pixmap
- pixmapData->is_cached = true;
Q_ASSERT(pixmapData->gl_surface);
GLuint textureId;
@@ -534,10 +583,10 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, cons
// bind the egl pixmap surface to a texture
EGLBoolean success;
- success = eglBindTexImage(eglContext->display(), pixmapSurface, EGL_BACK_BUFFER);
+ success = eglBindTexImage(eglContext->display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER);
if (success == EGL_FALSE) {
qWarning() << "eglBindTexImage() failed:" << eglContext->errorString(eglGetError());
- eglDestroySurface(eglContext->display(), pixmapSurface);
+ eglDestroySurface(eglContext->display(), (EGLSurface)pixmapData->gl_surface);
pixmapData->gl_surface = (Qt::HANDLE)EGL_NO_SURFACE;
haveTFP = false;
return 0;
diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp
index c728902..8fc95cf 100644
--- a/src/opengl/qglframebufferobject.cpp
+++ b/src/opengl/qglframebufferobject.cpp
@@ -64,7 +64,8 @@ QT_BEGIN_NAMESPACE
extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool);
-#define QGL_FUNC_CONTEXT QGLContextGroup *ctx = d_ptr->ctx;
+#define QGL_FUNC_CONTEXT const QGLContext *ctx = d_ptr->fbo_guard.context();
+#define QGL_FUNCP_CONTEXT const QGLContext *ctx = fbo_guard.context();
#ifndef QT_NO_DEBUG
#define QT_RESET_GLERROR() \
@@ -317,7 +318,7 @@ void QGLFBOGLPaintDevice::setFBO(QGLFramebufferObject* f,
QGLFramebufferObject::Attachment attachment)
{
fbo = f;
- m_thisFBO = fbo->d_func()->fbo; // This shouldn't be needed
+ m_thisFBO = fbo->d_func()->fbo(); // This shouldn't be needed
// The context that the fbo was created in may not have depth
// and stencil buffers, but the fbo itself might.
@@ -334,7 +335,7 @@ void QGLFBOGLPaintDevice::ensureActiveTarget()
{
QGLContext* ctx = const_cast<QGLContext*>(QGLContext::currentContext());
Q_ASSERT(ctx);
- const GLuint fboId = fbo->d_func()->fbo;
+ const GLuint fboId = fbo->d_func()->fbo();
if (ctx->d_func()->current_fbo != fboId) {
ctx->d_func()->current_fbo = fboId;
glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);
@@ -359,6 +360,9 @@ void QGLFBOGLPaintDevice::endPaint()
bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const
{
+ QGL_FUNCP_CONTEXT;
+ if (!ctx)
+ return false; // Context no longer exists.
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
switch(status) {
case GL_NO_ERROR:
@@ -405,11 +409,11 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
QGLFramebufferObject::Attachment attachment,
GLenum texture_target, GLenum internal_format, GLint samples)
{
- QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext());
- ctx = QGLContextPrivate::contextGroup(currentContext);
+ QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
+ fbo_guard.setContext(ctx);
bool ext_detected = (QGLExtensions::glExtensions & QGLExtensions::FramebufferObject);
- if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(currentContext)))
+ if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx)))
return;
size = sz;
@@ -417,8 +421,10 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
// texture dimensions
QT_RESET_GLERROR(); // reset error state
+ GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo);
+ fbo_guard.setId(fbo);
glDevice.setFBO(q, attachment);
@@ -445,6 +451,7 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
QT_CHECK_GLERROR();
valid = checkFramebufferStatus();
+ glBindTexture(target, 0);
color_buffer = 0;
} else {
@@ -535,13 +542,14 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
fbo_attachment = QGLFramebufferObject::NoAttachment;
}
- glBindFramebuffer(GL_FRAMEBUFFER_EXT, currentContext->d_ptr->current_fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
if (!valid) {
if (color_buffer)
glDeleteRenderbuffers(1, &color_buffer);
else
glDeleteTextures(1, &texture);
glDeleteFramebuffers(1, &fbo);
+ fbo_guard.setId(0);
}
QT_CHECK_GLERROR();
@@ -810,19 +818,16 @@ QGLFramebufferObject::~QGLFramebufferObject()
delete d->engine;
- if (isValid()) {
- const QGLContext *oldContext = QGLContext::currentContext();
- bool switchContext = !oldContext || QGLContextPrivate::contextGroup(oldContext) != ctx;
- if (switchContext)
- const_cast<QGLContext *>(ctx->context())->makeCurrent();
- glDeleteTextures(1, &d->texture);
+ if (isValid() && ctx) {
+ QGLShareContextScope scope(ctx);
+ if (d->texture)
+ glDeleteTextures(1, &d->texture);
if (d->color_buffer)
glDeleteRenderbuffers(1, &d->color_buffer);
if (d->depth_stencil_buffer)
glDeleteRenderbuffers(1, &d->depth_stencil_buffer);
- glDeleteFramebuffers(1, &d->fbo);
- if (oldContext && switchContext)
- const_cast<QGLContext *>(oldContext)->makeCurrent();
+ GLuint fbo = d->fbo();
+ glDeleteFramebuffers(1, &fbo);
}
}
@@ -838,11 +843,16 @@ QGLFramebufferObject::~QGLFramebufferObject()
The non-power of two limitation does not apply if the OpenGL version
is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension
is present.
+
+ The framebuffer can also become invalid if the QGLContext that
+ the framebuffer was created within is destroyed and there are
+ no other shared contexts that can take over ownership of the
+ framebuffer.
*/
bool QGLFramebufferObject::isValid() const
{
Q_D(const QGLFramebufferObject);
- return d->valid;
+ return d->valid && d->fbo_guard.context();
}
/*!
@@ -867,15 +877,17 @@ bool QGLFramebufferObject::bind()
return false;
Q_D(QGLFramebufferObject);
QGL_FUNC_CONTEXT;
- glBindFramebuffer(GL_FRAMEBUFFER_EXT, d->fbo);
+ if (!ctx)
+ return false; // Context no longer exists.
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, d->fbo());
d->valid = d->checkFramebufferStatus();
const QGLContext *context = QGLContext::currentContext();
if (d->valid && context) {
- Q_ASSERT(QGLContextPrivate::contextGroup(context) == ctx);
+ Q_ASSERT(QGLContextPrivate::contextGroup(context) == QGLContextPrivate::contextGroup(ctx));
// Save the previous setting to automatically restore in release().
- if (context->d_ptr->current_fbo != d->fbo) {
+ if (context->d_ptr->current_fbo != d->fbo()) {
d->previous_fbo = context->d_ptr->current_fbo;
- context->d_ptr->current_fbo = d->fbo;
+ context->d_ptr->current_fbo = d->fbo();
}
}
return d->valid;
@@ -900,10 +912,12 @@ bool QGLFramebufferObject::release()
return false;
Q_D(QGLFramebufferObject);
QGL_FUNC_CONTEXT;
+ if (!ctx)
+ return false; // Context no longer exists.
const QGLContext *context = QGLContext::currentContext();
if (context) {
- Q_ASSERT(QGLContextPrivate::contextGroup(context) == ctx);
+ Q_ASSERT(QGLContextPrivate::contextGroup(context) == QGLContextPrivate::contextGroup(ctx));
// Restore the previous setting for stacked framebuffer objects.
if (d->previous_fbo != context->d_ptr->current_fbo) {
context->d_ptr->current_fbo = d->previous_fbo;
@@ -976,7 +990,7 @@ QImage QGLFramebufferObject::toImage() const
bool wasBound = isBound();
if (!wasBound)
const_cast<QGLFramebufferObject *>(this)->bind();
- QImage image = qt_gl_read_framebuffer(d->size, format().textureTarget() != GL_RGB, true);
+ QImage image = qt_gl_read_framebuffer(d->size, format().internalTextureFormat() != GL_RGB, true);
if (!wasBound)
const_cast<QGLFramebufferObject *>(this)->release();
@@ -1144,7 +1158,7 @@ int QGLFramebufferObject::metric(PaintDeviceMetric metric) const
GLuint QGLFramebufferObject::handle() const
{
Q_D(const QGLFramebufferObject);
- return d->fbo;
+ return d->fbo();
}
/*! \fn int QGLFramebufferObject::devType() const
@@ -1175,7 +1189,7 @@ QGLFramebufferObject::Attachment QGLFramebufferObject::attachment() const
bool QGLFramebufferObject::isBound() const
{
Q_D(const QGLFramebufferObject);
- return QGLContext::currentContext()->d_ptr->current_fbo == d->fbo;
+ return QGLContext::currentContext()->d_ptr->current_fbo == d->fbo();
}
/*!
diff --git a/src/opengl/qglframebufferobject_p.h b/src/opengl/qglframebufferobject_p.h
index f80209d..9fe80b8 100644
--- a/src/opengl/qglframebufferobject_p.h
+++ b/src/opengl/qglframebufferobject_p.h
@@ -127,15 +127,15 @@ private:
class QGLFramebufferObjectPrivate
{
public:
- QGLFramebufferObjectPrivate() : depth_stencil_buffer(0), valid(false), ctx(0), previous_fbo(0), engine(0) {}
+ QGLFramebufferObjectPrivate() : fbo_guard(0), texture(0), depth_stencil_buffer(0), color_buffer(0), valid(false), previous_fbo(0), engine(0) {}
~QGLFramebufferObjectPrivate() {}
void init(QGLFramebufferObject *q, const QSize& sz,
QGLFramebufferObject::Attachment attachment,
GLenum internal_format, GLenum texture_target, GLint samples = 0);
bool checkFramebufferStatus() const;
+ QGLSharedResourceGuard fbo_guard;
GLuint texture;
- GLuint fbo;
GLuint depth_stencil_buffer;
GLuint color_buffer;
GLenum target;
@@ -143,10 +143,11 @@ public:
QGLFramebufferObjectFormat format;
uint valid : 1;
QGLFramebufferObject::Attachment fbo_attachment;
- QGLContextGroup *ctx; // for Windows extension ptrs
GLuint previous_fbo;
mutable QPaintEngine *engine;
QGLFBOGLPaintDevice glDevice;
+
+ inline GLuint fbo() const { return fbo_guard.id(); }
};
diff --git a/src/opengl/qglpixelbuffer_egl.cpp b/src/opengl/qglpixelbuffer_egl.cpp
index 4cba1bb..744fbd4 100644
--- a/src/opengl/qglpixelbuffer_egl.cpp
+++ b/src/opengl/qglpixelbuffer_egl.cpp
@@ -151,7 +151,7 @@ bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidge
bool QGLPixelBufferPrivate::cleanup()
{
- eglDestroySurface(QEglContext::defaultDisplay(0), pbuf);
+ // No need to destroy "pbuf" here - it is done in QGLContext::reset().
return true;
}
@@ -202,13 +202,20 @@ GLuint QGLPixelBuffer::generateDynamicTexture() const
bool QGLPixelBuffer::hasOpenGLPbuffers()
{
// See if we have at least 1 configuration that matches the default format.
- QEglContext ctx;
- if (!ctx.openDisplay(0))
+ EGLDisplay dpy = QEglContext::defaultDisplay(0);
+ if (dpy == EGL_NO_DISPLAY)
return false;
QEglProperties configProps;
qt_egl_set_format(configProps, QInternal::Pbuffer, QGLFormat::defaultFormat());
configProps.setRenderableType(QEgl::OpenGL);
- return ctx.chooseConfig(configProps);
+ do {
+ EGLConfig cfg = 0;
+ EGLint matching = 0;
+ if (eglChooseConfig(dpy, configProps.properties(),
+ &cfg, 1, &matching) && matching > 0)
+ return true;
+ } while (configProps.reduceConfiguration());
+ return false;
}
QT_END_NAMESPACE
diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp
index 15714c2..0603369 100644
--- a/src/opengl/qglpixmapfilter.cpp
+++ b/src/opengl/qglpixmapfilter.cpp
@@ -100,22 +100,42 @@ private:
class QGLPixmapBlurFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapBlurFilter>
{
public:
- QGLPixmapBlurFilter(QPixmapBlurFilter::BlurHint hint);
+ QGLPixmapBlurFilter(Qt::RenderHint hint);
void setUniforms(QGLShaderProgram *program);
+ static QByteArray generateGaussianShader(int radius, bool dropShadow = false);
+
protected:
bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
private:
- static QByteArray generateGaussianShader(int radius);
mutable QSize m_textureSize;
mutable bool m_horizontalBlur;
mutable bool m_haveCached;
mutable int m_cachedRadius;
- mutable QPixmapBlurFilter::BlurHint m_hint;
+ mutable Qt::RenderHint m_hint;
+};
+
+class QGLPixmapDropShadowFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapDropShadowFilter>
+{
+public:
+ QGLPixmapDropShadowFilter(Qt::RenderHint hint);
+
+ void setUniforms(QGLShaderProgram *program);
+
+protected:
+ bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
+
+private:
+ mutable QSize m_textureSize;
+ mutable bool m_horizontalBlur;
+
+ mutable bool m_haveCached;
+ mutable int m_cachedRadius;
+ mutable Qt::RenderHint m_hint;
};
extern QGLWidget *qt_gl_share_widget();
@@ -131,16 +151,28 @@ QPixmapFilter *QGL2PaintEngineEx::pixmapFilter(int type, const QPixmapFilter *pr
case QPixmapFilter::BlurFilter: {
const QPixmapBlurFilter *proto = static_cast<const QPixmapBlurFilter *>(prototype);
- if (proto->blurHint() == QPixmapBlurFilter::PerformanceHint || proto->radius() <= 5) {
+ if (proto->blurHint() == Qt::PerformanceHint || proto->radius() <= 5) {
if (!d->fastBlurFilter)
- d->fastBlurFilter.reset(new QGLPixmapBlurFilter(QPixmapBlurFilter::PerformanceHint));
+ d->fastBlurFilter.reset(new QGLPixmapBlurFilter(Qt::PerformanceHint));
return d->fastBlurFilter.data();
}
if (!d->blurFilter)
- d->blurFilter.reset(new QGLPixmapBlurFilter(QPixmapBlurFilter::QualityHint));
+ d->blurFilter.reset(new QGLPixmapBlurFilter(Qt::QualityHint));
return d->blurFilter.data();
}
+ case QPixmapFilter::DropShadowFilter: {
+ const QPixmapDropShadowFilter *proto = static_cast<const QPixmapDropShadowFilter *>(prototype);
+ if (proto->blurRadius() <= 5) {
+ if (!d->fastDropShadowFilter)
+ d->fastDropShadowFilter.reset(new QGLPixmapDropShadowFilter(Qt::PerformanceHint));
+ return d->fastDropShadowFilter.data();
+ }
+ if (!d->dropShadowFilter)
+ d->dropShadowFilter.reset(new QGLPixmapDropShadowFilter(Qt::QualityHint));
+ return d->dropShadowFilter.data();
+ }
+
case QPixmapFilter::ConvolutionFilter:
if (!d->convolutionFilter)
d->convolutionFilter.reset(new QGLPixmapConvolutionFilter);
@@ -279,12 +311,26 @@ static const char *qt_gl_blur_filter_fast =
" return color * (1.0 / float(samples));"
"}";
-QGLPixmapBlurFilter::QGLPixmapBlurFilter(QPixmapBlurFilter::BlurHint hint)
+static const char *qt_gl_drop_shadow_filter_fast =
+ "const int samples = 9;"
+ "uniform mediump vec2 delta;"
+ "uniform mediump vec4 shadowColor;"
+ "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {"
+ " mediump vec4 color = vec4(0.0, 0.0, 0.0, 0.0);"
+ " mediump float offset = (float(samples) - 1.0) / 2.0;"
+ " for (int i = 0; i < samples; i++) {"
+ " mediump vec2 coord = srcCoords + delta * (offset - float(i)) / offset;"
+ " color += texture2D(src, coord).a * shadowColor;"
+ " }"
+ " return color * (1.0 / float(samples));"
+ "}";
+
+QGLPixmapBlurFilter::QGLPixmapBlurFilter(Qt::RenderHint hint)
: m_haveCached(false)
, m_cachedRadius(5)
, m_hint(hint)
{
- if (hint == PerformanceHint) {
+ if (hint == Qt::PerformanceHint) {
QGLPixmapBlurFilter *filter = const_cast<QGLPixmapBlurFilter *>(this);
filter->setSource(qt_gl_blur_filter_fast);
m_haveCached = true;
@@ -296,7 +342,7 @@ bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const
QGLPixmapBlurFilter *filter = const_cast<QGLPixmapBlurFilter *>(this);
int radius = this->radius();
- if (!m_haveCached || (m_hint == QualityHint && radius != m_cachedRadius)) {
+ if (!m_haveCached || (m_hint == Qt::QualityHint && radius != m_cachedRadius)) {
// Only regenerate the shader from source if parameters have changed.
m_haveCached = true;
m_cachedRadius = radius;
@@ -358,7 +404,7 @@ bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const
void QGLPixmapBlurFilter::setUniforms(QGLShaderProgram *program)
{
- if (m_hint == QualityHint) {
+ if (m_hint == Qt::QualityHint) {
if (m_horizontalBlur)
program->setUniformValue("delta", 1.0 / m_textureSize.width(), 0.0);
else
@@ -380,7 +426,7 @@ static inline qreal gaussian(qreal dx, qreal sigma)
return exp(-dx * dx / (2 * sigma * sigma)) / (Q_2PI * sigma * sigma);
}
-QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius)
+QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool dropShadow)
{
Q_ASSERT(radius >= 1);
@@ -388,6 +434,8 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius)
source.reserve(1000);
source.append("uniform highp vec2 delta;\n");
+ if (dropShadow)
+ source.append("uniform mediump vec4 shadowColor;\n");
source.append("lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {\n");
QVector<qreal> sampleOffsets;
@@ -444,7 +492,10 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius)
source.append(coordinate);
source.append(";\n");
- source.append(" sample += texture2D(src, coord)");
+ if (dropShadow)
+ source.append(" sample += texture2D(src, coord).a * shadowColor");
+ else
+ source.append(" sample += texture2D(src, coord)");
weightSum += weights.at(i);
if (weights.at(i) != qreal(1)) {
@@ -463,4 +514,114 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius)
return source;
}
+QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter(Qt::RenderHint hint)
+ : m_haveCached(false)
+ , m_cachedRadius(5)
+ , m_hint(hint)
+{
+ if (hint == Qt::PerformanceHint) {
+ QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this);
+ filter->setSource(qt_gl_drop_shadow_filter_fast);
+ m_haveCached = true;
+ }
+}
+
+bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const
+{
+ QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this);
+
+ int radius = this->blurRadius();
+ if (!m_haveCached || (m_hint == Qt::QualityHint && radius != m_cachedRadius)) {
+ // Only regenerate the shader from source if parameters have changed.
+ m_haveCached = true;
+ m_cachedRadius = radius;
+ filter->setSource(QGLPixmapBlurFilter::generateGaussianShader(radius, true));
+ }
+
+ QGLFramebufferObjectFormat format;
+ format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB));
+ QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(src.size(), format);
+
+ if (!fbo)
+ return false;
+
+ glBindTexture(GL_TEXTURE_2D, fbo->texture());
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // prepare for updateUniforms
+ m_textureSize = src.size();
+
+ // horizontal pass, to pixmap
+ m_horizontalBlur = true;
+
+ QPainter fboPainter(fbo);
+
+ if (src.hasAlphaChannel()) {
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ // ensure GL_LINEAR filtering is used
+ fboPainter.setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(&fboPainter);
+ fboPainter.drawPixmap(0, 0, src);
+ filter->removeFromPainter(&fboPainter);
+ fboPainter.end();
+
+ QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
+
+ // vertical pass, to painter
+ m_horizontalBlur = false;
+
+ painter->save();
+ // ensure GL_LINEAR filtering is used
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(painter);
+ QPointF ofs = offset();
+ engine->drawTexture(src.rect().translated(pos.x() + ofs.x(), pos.y() + ofs.y()), fbo->texture(), fbo->size(), src.rect().translated(0, fbo->height() - src.height()));
+ filter->removeFromPainter(painter);
+ painter->restore();
+
+ qgl_fbo_pool()->release(fbo);
+
+ // Now draw the actual pixmap over the top.
+ painter->drawPixmap(pos, src, srcRect);
+
+ return true;
+}
+
+void QGLPixmapDropShadowFilter::setUniforms(QGLShaderProgram *program)
+{
+ QColor col = color();
+ if (m_horizontalBlur) {
+ program->setUniformValue("shadowColor", 1.0f, 1.0f, 1.0f, 1.0f);
+ } else {
+ qreal alpha = col.alphaF();
+ program->setUniformValue("shadowColor", col.redF() * alpha,
+ col.greenF() * alpha,
+ col.blueF() * alpha,
+ alpha);
+ }
+ if (m_hint == Qt::QualityHint) {
+ if (m_horizontalBlur)
+ program->setUniformValue("delta", 1.0 / m_textureSize.width(), 0.0);
+ else
+ program->setUniformValue("delta", 0.0, 1.0 / m_textureSize.height());
+ } else {
+ // 1.4 is chosen to most closely match the blurriness of the gaussian blur
+ // at low radii
+ qreal blur = blurRadius() / 1.4f;
+
+ if (m_horizontalBlur)
+ program->setUniformValue("delta", blur / m_textureSize.width(), 0.0);
+ else
+ program->setUniformValue("delta", 0.0, blur / m_textureSize.height());
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/opengl/qglshaderprogram.cpp b/src/opengl/qglshaderprogram.cpp
index ebcd723..dfa6c40 100644
--- a/src/opengl/qglshaderprogram.cpp
+++ b/src/opengl/qglshaderprogram.cpp
@@ -349,7 +349,7 @@ QGLShader::QGLShader(QGLShader::ShaderType type, const QGLContext *context, QObj
context = QGLContext::currentContext();
d = new QGLShaderPrivate(context, type);
#ifndef QT_NO_DEBUG
- if (context && !qgl_share_reg()->checkSharing(context, QGLContext::currentContext())) {
+ if (context && !QGLContext::areSharing(context, QGLContext::currentContext())) {
qWarning("QGLShader::QGLShader: \'context\' must be the currect context or sharing with it.");
return;
}
@@ -374,7 +374,7 @@ QGLShader::QGLShader
context = QGLContext::currentContext();
d = new QGLShaderPrivate(context, type);
#ifndef QT_NO_DEBUG
- if (context && !qgl_share_reg()->checkSharing(context, QGLContext::currentContext())) {
+ if (context && !QGLContext::areSharing(context, QGLContext::currentContext())) {
qWarning("QGLShader::QGLShader: \'context\' must be currect context or sharing with it.");
return;
}
@@ -806,8 +806,8 @@ bool QGLShaderProgram::addShader(QGLShader *shader)
if (d->shaders.contains(shader))
return true; // Already added to this shader program.
if (d->programGuard.id() && shader) {
- if (!qgl_share_reg()->checkSharing(shader->d->shaderGuard.context(),
- d->programGuard.context())) {
+ if (!QGLContext::areSharing(shader->d->shaderGuard.context(),
+ d->programGuard.context())) {
qWarning("QGLShaderProgram::addShader: Program and shader are not associated with same context.");
return false;
}
diff --git a/src/opengl/qpaintengine_opengl.cpp b/src/opengl/qpaintengine_opengl.cpp
index a904064..3e4a8e7 100644
--- a/src/opengl/qpaintengine_opengl.cpp
+++ b/src/opengl/qpaintengine_opengl.cpp
@@ -338,7 +338,7 @@ void QGLOffscreen::initialize()
int dim = qMax(2048, static_cast<int>(qt_next_power_of_two(qMax(device->size().width(), device->size().height()))));
- bool shared_context = qgl_share_reg()->checkSharing(device->context(), ctx);
+ bool shared_context = QGLContext::areSharing(device->context(), ctx);
bool would_fail = last_failed_size.isValid() &&
(device->size().width() >= last_failed_size.width() ||
device->size().height() >= last_failed_size.height());
@@ -555,7 +555,7 @@ public:
QList<const QGLContext *> contexts = programs.uniqueKeys();
for (int i=0; i<contexts.size(); ++i) {
const QGLContext *cx = contexts.at(i);
- if (cx != ctx && qgl_share_reg()->checkSharing(cx, ctx)) {
+ if (cx != ctx && QGLContext::areSharing(cx, ctx)) {
QList<GLProgram> progs = programs.values(cx);
for (int k=0; k<progs.size(); ++k) {
const GLProgram &prg = progs.at(k);
@@ -1015,7 +1015,7 @@ public:
}
inline GLuint getBuffer(const QGradient &gradient, qreal opacity, QGLContext *ctx) {
- if (buffer_ctx && !qgl_share_reg()->checkSharing(buffer_ctx, ctx))
+ if (buffer_ctx && !QGLContext::areSharing(buffer_ctx, ctx))
cleanCache();
buffer_ctx = ctx;
@@ -1365,7 +1365,7 @@ bool QOpenGLPaintEngine::begin(QPaintDevice *pdev)
#ifdef QT_OPENGL_ES
d->max_texture_size = ctx->d_func()->maxTextureSize();
#else
- bool shared_ctx = qgl_share_reg()->checkSharing(d->device->context(), d->shader_ctx);
+ bool shared_ctx = QGLContext::areSharing(d->device->context(), d->shader_ctx);
if (shared_ctx) {
d->max_texture_size = d->shader_ctx->d_func()->maxTextureSize();
@@ -2311,7 +2311,7 @@ void QOpenGLPaintEnginePrivate::updateDepthClip()
void QOpenGLPaintEnginePrivate::systemStateChanged()
{
Q_Q(QOpenGLPaintEngine);
- if (q->state()->hasClipping)
+ if (q->painter()->hasClipping())
q->updateClipRegion(q->painter()->clipRegion(), Qt::ReplaceClip);
else
q->updateClipRegion(QRegion(), Qt::NoClip);
@@ -4683,7 +4683,7 @@ void QGLGlyphCache::cacheGlyphs(QGLContext *context, const QTextItemInt &ti,
QList<const QGLContext *> contexts = qt_context_cache.keys();
for (int i=0; i<contexts.size(); ++i) {
const QGLContext *ctx = contexts.at(i);
- if (ctx != context && qgl_share_reg()->checkSharing(context, ctx)) {
+ if (ctx != context && QGLContext::areSharing(context, ctx)) {
context_key = ctx;
dev_it = qt_context_cache.constFind(context_key);
break;
diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp
index 2331c6d..cbb310b 100644
--- a/src/opengl/qpixmapdata_gl.cpp
+++ b/src/opengl/qpixmapdata_gl.cpp
@@ -48,9 +48,12 @@
#include <private/qgl_p.h>
#include <private/qdrawhelper_p.h>
+#include <private/qimage_p.h>
#include <private/qpaintengineex_opengl2_p.h>
+#include <qdesktopwidget.h>
+
QT_BEGIN_NAMESPACE
extern QGLWidget* qt_gl_share_widget();
@@ -248,7 +251,7 @@ bool QGLPixmapData::isValidContext(const QGLContext *ctx) const
return true;
const QGLContext *share_ctx = qt_gl_share_widget()->context();
- return ctx == share_ctx || qgl_share_reg()->checkSharing(ctx, share_ctx);
+ return ctx == share_ctx || QGLContext::areSharing(ctx, share_ctx);
}
void QGLPixmapData::resize(int width, int height)
@@ -315,7 +318,7 @@ void QGLPixmapData::ensureCreated() const
}
void QGLPixmapData::fromImage(const QImage &image,
- Qt::ImageConversionFlags)
+ Qt::ImageConversionFlags /*flags*/)
{
if (image.size() == QSize(w, h))
setSerialNumber(++qt_gl_pixmap_serial);
@@ -323,20 +326,26 @@ void QGLPixmapData::fromImage(const QImage &image,
if (pixelType() == BitmapType) {
m_source = image.convertToFormat(QImage::Format_MonoLSB);
+
} else {
- m_source = image.hasAlphaChannel()
- ? image.convertToFormat(QImage::Format_ARGB32_Premultiplied)
- : image.convertToFormat(QImage::Format_RGB32);
+ QImage::Format format = QImage::Format_RGB32;
+ if (qApp->desktop()->depth() == 16)
+ format = QImage::Format_RGB16;
+
+ if (image.hasAlphaChannel() && const_cast<QImage &>(image).data_ptr()->checkForAlphaPixels())
+ format = QImage::Format_ARGB32_Premultiplied;;
+
+ m_source = image.convertToFormat(format);
}
m_dirty = true;
m_hasFillColor = false;
- m_hasAlpha = image.hasAlphaChannel();
+ m_hasAlpha = m_source.hasAlphaChannel();
w = image.width();
h = image.height();
is_null = (w <= 0 || h <= 0);
- d = pixelType() == QPixmapData::PixmapType ? 32 : 1;
+ d = m_source.depth();
if (m_texture.id) {
QGLShareContextScope ctx(qt_gl_share_widget()->context());
diff --git a/src/opengl/qwindowsurface_gl.cpp b/src/opengl/qwindowsurface_gl.cpp
index 3a348bc..7f8577a 100644
--- a/src/opengl/qwindowsurface_gl.cpp
+++ b/src/opengl/qwindowsurface_gl.cpp
@@ -306,8 +306,13 @@ QGLWindowSurface::QGLWindowSurface(QWidget *window)
d_ptr->pb = 0;
d_ptr->fbo = 0;
d_ptr->ctx = 0;
+#if defined (QT_OPENGL_ES_2)
+ d_ptr->tried_fbo = true;
+ d_ptr->tried_pb = true;
+#else
d_ptr->tried_fbo = false;
d_ptr->tried_pb = false;
+#endif
d_ptr->destructive_swap_buffers = qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull();
d_ptr->glDevice.d = d_ptr;
d_ptr->q_ptr = this;
@@ -438,6 +443,7 @@ void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &
QRect rect = br.translated(-offset - wOffset);
const GLenum target = GL_TEXTURE_2D;
+ Q_UNUSED(target);
if (context()) {
context()->makeCurrent();
diff --git a/src/opengl/util/fragmentprograms_p.h b/src/opengl/util/fragmentprograms_p.h
index 18da5c8..340023c 100644
--- a/src/opengl/util/fragmentprograms_p.h
+++ b/src/opengl/util/fragmentprograms_p.h
@@ -71,7 +71,7 @@ enum FragmentVariable {
VAR_FMP2_M_RADIUS2,
VAR_FMP,
VAR_INV_MATRIX_M0,
- VAR_ANGLE,
+ VAR_ANGLE
};
enum FragmentBrushType {
@@ -80,7 +80,7 @@ enum FragmentBrushType {
FRAGMENT_PROGRAM_BRUSH_CONICAL,
FRAGMENT_PROGRAM_BRUSH_LINEAR,
FRAGMENT_PROGRAM_BRUSH_TEXTURE,
- FRAGMENT_PROGRAM_BRUSH_PATTERN,
+ FRAGMENT_PROGRAM_BRUSH_PATTERN
};
enum FragmentCompositionModeType {
@@ -109,12 +109,12 @@ enum FragmentCompositionModeType {
COMPOSITION_MODES_DIFFERENCE_NOMASK,
COMPOSITION_MODES_EXCLUSION_NOMASK,
COMPOSITION_MODE_BLEND_MODE_MASK,
- COMPOSITION_MODE_BLEND_MODE_NOMASK,
+ COMPOSITION_MODE_BLEND_MODE_NOMASK
};
enum FragmentMaskType {
FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA,
- FRAGMENT_PROGRAM_MASK_ELLIPSE_AA,
+ FRAGMENT_PROGRAM_MASK_ELLIPSE_AA
};
static const unsigned int num_fragment_variables = 19;