summaryrefslogtreecommitdiffstats
path: root/src/opengl/gl2paintengineex
diff options
context:
space:
mode:
Diffstat (limited to 'src/opengl/gl2paintengineex')
-rw-r--r--src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp65
-rw-r--r--src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h10
-rw-r--r--src/opengl/gl2paintengineex/qglcustomshaderstage.cpp21
-rw-r--r--src/opengl/gl2paintengineex/qglcustomshaderstage_p.h3
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager.cpp426
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager_p.h131
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadersource_p.h16
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp445
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h32
-rw-r--r--src/opengl/gl2paintengineex/qtriangulatingstroker.cpp549
-rw-r--r--src/opengl/gl2paintengineex/qtriangulatingstroker_p.h156
11 files changed, 1396 insertions, 458 deletions
diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp
index 1fe3999..ee1a797 100644
--- a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp
+++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp
@@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE
void QGL2PEXVertexArray::clear()
{
vertexArray.reset();
- vertexArrayStops.clear();
+ vertexArrayStops.reset();
boundingRectDirty = true;
}
@@ -101,7 +101,7 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc
boundingRectDirty = false;
}
- if (!outline)
+ if (!outline && !path.isConvex())
addCentroid(path, 0);
int lastMoveTo = vertexArray.size();
@@ -120,15 +120,14 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc
// qDebug("QVectorPath has element types");
for (int i=1; i<path.elementCount(); ++i) {
- const QPainterPath::ElementType elementType = elements[i];
- switch (elementType) {
+ switch (elements[i]) {
case QPainterPath::MoveToElement:
if (!outline)
addClosingLine(lastMoveTo);
// qDebug("element[%d] is a MoveToElement", i);
- vertexArrayStops.append(vertexArray.size());
+ vertexArrayStops.add(vertexArray.size());
if (!outline) {
- addCentroid(path, i);
+ if (!path.isConvex()) addCentroid(path, i);
lastMoveTo = vertexArray.size();
}
lineToArray(points[i].x(), points[i].y()); // Add the moveTo as a new vertex
@@ -137,11 +136,22 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc
// qDebug("element[%d] is a LineToElement", i);
lineToArray(points[i].x(), points[i].y());
break;
- case QPainterPath::CurveToElement:
-// qDebug("element[%d] is a CurveToElement", i);
- curveToArray(points[i], points[i+1], points[i+2], curveInverseScale);
- i+=2;
- break;
+ case QPainterPath::CurveToElement: {
+ QBezier b = QBezier::fromPoints(*(((const QPointF *) points) + i - 1),
+ points[i],
+ points[i+1],
+ points[i+2]);
+ QRectF bounds = b.bounds();
+ // threshold based on same algorithm as in qtriangulatingstroker.cpp
+ int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * 3.14f / (curveInverseScale * 6));
+ if (threshold < 3) threshold = 3;
+ qreal one_over_threshold_minus_1 = 1.f / (threshold - 1);
+ for (int t=0; t<threshold; ++t) {
+ QPointF pt = b.pointAt(t * one_over_threshold_minus_1);
+ lineToArray(pt.x(), pt.y());
+ }
+ i += 2;
+ break; }
default:
break;
}
@@ -150,7 +160,7 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc
if (!outline)
addClosingLine(lastMoveTo);
- vertexArrayStops.append(vertexArray.size());
+ vertexArrayStops.add(vertexArray.size());
}
void QGL2PEXVertexArray::lineToArray(const GLfloat x, const GLfloat y)
@@ -167,35 +177,4 @@ void QGL2PEXVertexArray::lineToArray(const GLfloat x, const GLfloat y)
minY = y;
}
-void QGL2PEXVertexArray::curveToArray(const QGLPoint &cp1, const QGLPoint &cp2, const QGLPoint &ep, GLfloat inverseScale)
-{
- qreal inverseScaleHalf = inverseScale / 2;
-
- QBezier beziers[32];
- beziers[0] = QBezier::fromPoints(vertexArray.last(), cp1, cp2, ep);
- QBezier *b = beziers;
- while (b >= beziers) {
- // check if we can pop the top bezier curve from the stack
- qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
- qreal d;
- if (l > inverseScale) {
- d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2) - (b->y4 - b->y1)*(b->x1 - b->x2) )
- + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3) - (b->y4 - b->y1)*(b->x1 - b->x3) );
- d /= l;
- } else {
- d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
- qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
- }
- if (d < inverseScaleHalf || b == beziers + 31) {
- // good enough, we pop it off and add the endpoint
- lineToArray(b->x4, b->y4);
- --b;
- } else {
- // split, second half of the polygon goes lower into the stack
- b->split(b+1, b);
- ++b;
- }
- }
-}
-
QT_END_NAMESPACE
diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
index 719904f..98eaa91 100644
--- a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
+++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
@@ -108,23 +108,23 @@ public:
void clear();
QGLPoint* data() {return vertexArray.data();}
- QVector<int>& stops() {return vertexArrayStops;}
+ int *stops() const { return vertexArrayStops.data(); }
+ int stopCount() const { return vertexArrayStops.size(); }
QGLRect boundingRect() const;
+ int vertexCount() const { return vertexArray.size(); }
+
void lineToArray(const GLfloat x, const GLfloat y);
private:
QDataBuffer<QGLPoint> vertexArray;
- QVector<int> vertexArrayStops;
+ QDataBuffer<int> vertexArrayStops;
GLfloat maxX;
GLfloat maxY;
GLfloat minX;
GLfloat minY;
bool boundingRectDirty;
-
- inline void curveToArray(const QGLPoint &cp1, const QGLPoint &cp2, const QGLPoint &ep, GLfloat inverseScale);
-
void addClosingLine(int index);
void addCentroid(const QVectorPath &path, int subPathIndex);
};
diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp b/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp
index ab2026c..b71a7b7 100644
--- a/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp
+++ b/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp
@@ -67,8 +67,10 @@ QGLCustomShaderStage::QGLCustomShaderStage()
QGLCustomShaderStage::~QGLCustomShaderStage()
{
Q_D(QGLCustomShaderStage);
- if (d->m_manager)
- d->m_manager->removeCustomStage(this);
+ if (d->m_manager) {
+ d->m_manager->removeCustomStage();
+ d->m_manager->sharedShaders->cleanupCustomStage(this);
+ }
}
void QGLCustomShaderStage::setUniformsDirty()
@@ -85,6 +87,8 @@ bool QGLCustomShaderStage::setOnPainter(QPainter* p)
qWarning("QGLCustomShaderStage::setOnPainter() - paint engine not OpenGL2");
return false;
}
+ if (d->m_manager)
+ qWarning("Custom shader is already set on a painter");
QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx*>(p->paintEngine());
d->m_manager = QGL2PaintEngineExPrivate::shaderManagerForEngine(engine);
@@ -108,12 +112,21 @@ void QGLCustomShaderStage::removeFromPainter(QPainter* p)
// This should leave the program in a compiled/linked state
// if the next custom shader stage is this one again.
d->m_manager->setCustomStage(0);
+ d->m_manager = 0;
}
-const char* QGLCustomShaderStage::source() const
+QByteArray QGLCustomShaderStage::source() const
{
Q_D(const QGLCustomShaderStage);
- return d->m_source.constData();
+ return d->m_source;
+}
+
+// Called by the shader manager if another custom shader is attached or
+// the manager is deleted
+void QGLCustomShaderStage::setInactive()
+{
+ Q_D(QGLCustomShaderStage);
+ d->m_manager = 0;
}
void QGLCustomShaderStage::setSource(const QByteArray& s)
diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h
index f8c13c5..e319389 100644
--- a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h
+++ b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h
@@ -74,8 +74,9 @@ public:
bool setOnPainter(QPainter*);
void removeFromPainter(QPainter*);
- const char* source() const;
+ QByteArray source() const;
+ void setInactive();
protected:
void setSource(const QByteArray&);
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
index e22303d..8a8f483 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
@@ -66,7 +66,7 @@ QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLConte
return p;
}
-const char* QGLEngineSharedShaders::qglEngineShaderSourceCode[] = {
+const char* QGLEngineSharedShaders::qShaderSnippets[] = {
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
@@ -78,7 +78,6 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
, blitShaderProg(0)
, simpleShaderProg(0)
{
- memset(compiledShaders, 0, sizeof(compiledShaders));
/*
Rather than having the shader source array statically initialised, it is initialised
@@ -86,10 +85,10 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
around without having to change the order of the glsl strings. It is hoped this will
make future hard-to-find runtime bugs more obvious and generally give more solid code.
*/
- static bool qglEngineShaderSourceCodePopulated = false;
- if (!qglEngineShaderSourceCodePopulated) {
+ static bool snippetsPopulated = false;
+ if (!snippetsPopulated) {
- const char** code = qglEngineShaderSourceCode; // shortcut
+ const char** code = qShaderSnippets; // shortcut
code[MainVertexShader] = qglslMainVertexShader;
code[MainWithTexCoordsVertexShader] = qglslMainWithTexCoordsVertexShader;
@@ -121,7 +120,7 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
code[ImageSrcFragmentShader] = qglslImageSrcFragmentShader;
code[ImageSrcWithPatternFragmentShader] = qglslImageSrcWithPatternFragmentShader;
code[NonPremultipliedImageSrcFragmentShader] = qglslNonPremultipliedImageSrcFragmentShader;
- code[CustomImageSrcFragmentShader] = ""; // Supplied by app.
+ code[CustomImageSrcFragmentShader] = qglslCustomSrcFragmentShader; // Calls "customShader", which must be appended
code[SolidBrushSrcFragmentShader] = qglslSolidBrushSrcFragmentShader;
code[TextureBrushSrcFragmentShader] = qglslTextureBrushSrcFragmentShader;
code[TextureBrushSrcWithPatternFragmentShader] = qglslTextureBrushSrcWithPatternFragmentShader;
@@ -131,11 +130,13 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
code[ConicalGradientBrushSrcFragmentShader] = qglslConicalGradientBrushSrcFragmentShader;
code[ShockingPinkSrcFragmentShader] = qglslShockingPinkSrcFragmentShader;
+ code[NoMaskFragmentShader] = "";
code[MaskFragmentShader] = qglslMaskFragmentShader;
code[RgbMaskFragmentShaderPass1] = qglslRgbMaskFragmentShaderPass1;
code[RgbMaskFragmentShaderPass2] = qglslRgbMaskFragmentShaderPass2;
code[RgbMaskWithGammaFragmentShader] = ""; //###
+ code[NoCompositionModeFragmentShader] = "";
code[MultiplyCompositionModeFragmentShader] = ""; //###
code[ScreenCompositionModeFragmentShader] = ""; //###
code[OverlayCompositionModeFragmentShader] = ""; //###
@@ -150,29 +151,36 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
#if defined(QT_DEBUG)
// Check that all the elements have been filled:
- for (int i = 0; i < TotalShaderCount; ++i) {
- if (qglEngineShaderSourceCode[i] == 0) {
- int enumIndex = staticMetaObject.indexOfEnumerator("ShaderName");
- QMetaEnum m = staticMetaObject.enumerator(enumIndex);
-
- qCritical() << "qglEngineShaderSourceCode: Source for" << m.valueToKey(i)
- << "(shader" << i << ") missing!";
+ for (int i = 0; i < TotalSnippetCount; ++i) {
+ if (qShaderSnippets[i] == 0) {
+ qFatal("Shader snippet for %s (#%d) is missing!",
+ snippetNameStr(SnippetName(i)).constData(), i);
}
}
#endif
- qglEngineShaderSourceCodePopulated = true;
+ snippetsPopulated = true;
}
+ QGLShader* fragShader;
+ QGLShader* vertexShader;
+ QByteArray source;
+
// Compile up the simple shader:
+ source.clear();
+ source.append(qShaderSnippets[MainVertexShader]);
+ source.append(qShaderSnippets[PositionOnlyVertexShader]);
+ vertexShader = new QGLShader(QGLShader::Vertex, context, this);
+ vertexShader->compileSourceCode(source);
+
+ source.clear();
+ source.append(qShaderSnippets[MainFragmentShader]);
+ source.append(qShaderSnippets[ShockingPinkSrcFragmentShader]);
+ fragShader = new QGLShader(QGLShader::Fragment, context, this);
+ fragShader->compileSourceCode(source);
+
simpleShaderProg = new QGLShaderProgram(context, this);
- compileNamedShader(MainVertexShader, QGLShader::PartialVertexShader);
- compileNamedShader(PositionOnlyVertexShader, QGLShader::PartialVertexShader);
- compileNamedShader(MainFragmentShader, QGLShader::PartialFragmentShader);
- compileNamedShader(ShockingPinkSrcFragmentShader, QGLShader::PartialFragmentShader);
- simpleShaderProg->addShader(compiledShaders[MainVertexShader]);
- simpleShaderProg->addShader(compiledShaders[PositionOnlyVertexShader]);
- simpleShaderProg->addShader(compiledShaders[MainFragmentShader]);
- simpleShaderProg->addShader(compiledShaders[ShockingPinkSrcFragmentShader]);
+ simpleShaderProg->addShader(vertexShader);
+ simpleShaderProg->addShader(fragShader);
simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
simpleShaderProg->link();
if (!simpleShaderProg->isLinked()) {
@@ -181,152 +189,159 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
}
// Compile the blit shader:
+ source.clear();
+ source.append(qShaderSnippets[MainWithTexCoordsVertexShader]);
+ source.append(qShaderSnippets[UntransformedPositionVertexShader]);
+ vertexShader = new QGLShader(QGLShader::Vertex, context, this);
+ vertexShader->compileSourceCode(source);
+
+ source.clear();
+ source.append(qShaderSnippets[MainFragmentShader]);
+ source.append(qShaderSnippets[ImageSrcFragmentShader]);
+ fragShader = new QGLShader(QGLShader::Fragment, context, this);
+ fragShader->compileSourceCode(source);
+
blitShaderProg = new QGLShaderProgram(context, this);
- compileNamedShader(MainWithTexCoordsVertexShader, QGLShader::PartialVertexShader);
- compileNamedShader(UntransformedPositionVertexShader, QGLShader::PartialVertexShader);
- compileNamedShader(MainFragmentShader, QGLShader::PartialFragmentShader);
- compileNamedShader(ImageSrcFragmentShader, QGLShader::PartialFragmentShader);
- blitShaderProg->addShader(compiledShaders[MainWithTexCoordsVertexShader]);
- blitShaderProg->addShader(compiledShaders[UntransformedPositionVertexShader]);
- blitShaderProg->addShader(compiledShaders[MainFragmentShader]);
- blitShaderProg->addShader(compiledShaders[ImageSrcFragmentShader]);
+ blitShaderProg->addShader(vertexShader);
+ blitShaderProg->addShader(fragShader);
blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
blitShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
blitShaderProg->link();
if (!blitShaderProg->isLinked()) {
qCritical() << "Errors linking blit shader:"
- << blitShaderProg->log();
- }
-}
-
-void QGLEngineSharedShaders::shaderDestroyed(QObject *shader)
-{
- // Remove any shader programs which has this as the srcPixel shader:
- for (int i = 0; i < cachedPrograms.size(); ++i) {
- if (cachedPrograms.at(i).srcPixelFragShader == shader) {
- delete cachedPrograms.at(i).program;
- cachedPrograms.removeAt(i--);
- }
+ << simpleShaderProg->log();
}
- emit shaderProgNeedsChanging();
}
-QGLShader *QGLEngineSharedShaders::compileNamedShader(ShaderName name, QGLShader::ShaderType type)
+#if defined (QT_DEBUG)
+QByteArray QGLEngineSharedShaders::snippetNameStr(SnippetName name)
{
- Q_ASSERT(name != CustomImageSrcFragmentShader);
- Q_ASSERT(name < InvalidShaderName);
-
- if (compiledShaders[name])
- return compiledShaders[name];
-
- QByteArray source = qglEngineShaderSourceCode[name];
- QGLShader *newShader = new QGLShader(type, ctxGuard.context(), this);
- newShader->compile(source);
-
-#if defined(QT_DEBUG)
- // Name the shader for easier debugging
- QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("ShaderName"));
- newShader->setObjectName(QLatin1String(m.valueToKey(name)));
-#endif
-
- compiledShaders[name] = newShader;
- return newShader;
+ QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("SnippetName"));
+ return QByteArray(m.valueToKey(name));
}
-
-QGLShader *QGLEngineSharedShaders::compileCustomShader(QGLCustomShaderStage *stage, QGLShader::ShaderType type)
-{
- QByteArray source = stage->source();
- source += qglslCustomSrcFragmentShader;
-
- QGLShader *newShader = customShaderCache.object(source);
- if (newShader)
- return newShader;
-
- newShader = new QGLShader(type, ctxGuard.context(), this);
- newShader->compile(source);
- customShaderCache.insert(source, newShader);
-
- connect(newShader, SIGNAL(destroyed(QObject *)),
- this, SLOT(shaderDestroyed(QObject *)));
-
-#if defined(QT_DEBUG)
- // Name the shader for easier debugging
- QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("ShaderName"));
- newShader->setObjectName(QLatin1String(m.valueToKey(CustomImageSrcFragmentShader)));
#endif
- return newShader;
-}
-
// The address returned here will only be valid until next time this function is called.
QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineShaderProg &prog)
{
for (int i = 0; i < cachedPrograms.size(); ++i) {
- if (cachedPrograms[i] == prog)
- return &cachedPrograms[i];
+ QGLEngineShaderProg *cachedProg = cachedPrograms[i];
+ if (*cachedProg == prog) {
+ // Move the program to the top of the list as a poor-man's cache algo
+ cachedPrograms.move(i, 0);
+ return cachedProg;
+ }
}
- cachedPrograms.append(prog);
- QGLEngineShaderProg &cached = cachedPrograms.last();
+ QByteArray source;
+ source.append(qShaderSnippets[prog.mainFragShader]);
+ source.append(qShaderSnippets[prog.srcPixelFragShader]);
+ if (prog.srcPixelFragShader == CustomImageSrcFragmentShader)
+ source.append(prog.customStageSource);
+ if (prog.compositionFragShader)
+ source.append(qShaderSnippets[prog.compositionFragShader]);
+ if (prog.maskFragShader)
+ source.append(qShaderSnippets[prog.maskFragShader]);
+ QGLShader* fragShader = new QGLShader(QGLShader::Fragment, ctxGuard.context(), this);
+ fragShader->compileSourceCode(source);
+
+ source.clear();
+ source.append(qShaderSnippets[prog.mainVertexShader]);
+ source.append(qShaderSnippets[prog.positionVertexShader]);
+ QGLShader* vertexShader = new QGLShader(QGLShader::Vertex, ctxGuard.context(), this);
+ vertexShader->compileSourceCode(source);
+
+#if defined(QT_DEBUG)
+ // Name the shaders for easier debugging
+ QByteArray description;
+ description.append("Fragment shader: main=");
+ description.append(snippetNameStr(prog.mainFragShader));
+ description.append(", srcPixel=");
+ description.append(snippetNameStr(prog.srcPixelFragShader));
+ if (prog.compositionFragShader) {
+ description.append(", composition=");
+ description.append(snippetNameStr(prog.compositionFragShader));
+ }
+ if (prog.maskFragShader) {
+ description.append(", mask=");
+ description.append(snippetNameStr(prog.maskFragShader));
+ }
+ fragShader->setObjectName(QString::fromLatin1(description));
+
+ description.clear();
+ description.append("Vertex shader: main=");
+ description.append(snippetNameStr(prog.mainVertexShader));
+ description.append(", position=");
+ description.append(snippetNameStr(prog.positionVertexShader));
+ vertexShader->setObjectName(QString::fromLatin1(description));
+#endif
+
+ QGLEngineShaderProg* newProg = new QGLEngineShaderProg(prog);
// If the shader program's not found in the cache, create it now.
- cached.program = new QGLShaderProgram(ctxGuard.context(), this);
- cached.program->addShader(cached.mainVertexShader);
- cached.program->addShader(cached.positionVertexShader);
- cached.program->addShader(cached.mainFragShader);
- cached.program->addShader(cached.srcPixelFragShader);
- cached.program->addShader(cached.maskFragShader);
- cached.program->addShader(cached.compositionFragShader);
+ newProg->program = new QGLShaderProgram(ctxGuard.context(), this);
+ newProg->program->addShader(vertexShader);
+ newProg->program->addShader(fragShader);
// We have to bind the vertex attribute names before the program is linked:
- 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()) {
+ newProg->program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
+ if (newProg->useTextureCoords)
+ newProg->program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
+ if (newProg->useOpacityAttribute)
+ newProg->program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR);
+
+ newProg->program->link();
+ if (!newProg->program->isLinked()) {
QLatin1String none("none");
QLatin1String br("\n");
QString error;
error = QLatin1String("Shader program failed to link,")
#if defined(QT_DEBUG)
+ br
- + QLatin1String(" Shaders Used:\n")
- + QLatin1String(" mainVertexShader = ")
- + (cached.mainVertexShader ?
- cached.mainVertexShader->objectName() : none) + br
- + QLatin1String(" positionVertexShader = ")
- + (cached.positionVertexShader ?
- cached.positionVertexShader->objectName() : none) + br
- + QLatin1String(" mainFragShader = ")
- + (cached.mainFragShader ?
- cached.mainFragShader->objectName() : none) + br
- + QLatin1String(" srcPixelFragShader = ")
- + (cached.srcPixelFragShader ?
- cached.srcPixelFragShader->objectName() : none) + br
- + QLatin1String(" maskFragShader = ")
- + (cached.maskFragShader ?
- cached.maskFragShader->objectName() : none) + br
- + QLatin1String(" compositionFragShader = ")
- + (cached.compositionFragShader ?
- cached.compositionFragShader->objectName() : none) + br
+ + QLatin1String(" Shaders Used:") + br
+ + QLatin1String(" ") + vertexShader->objectName() + QLatin1String(": ") + br
+ + QLatin1String(vertexShader->sourceCode()) + br
+ + QLatin1String(" ") + fragShader->objectName() + QLatin1String(": ") + br
+ + QLatin1String(fragShader->sourceCode()) + br
#endif
+ QLatin1String(" Error Log:\n")
- + QLatin1String(" ") + cached.program->log();
+ + QLatin1String(" ") + newProg->program->log();
qWarning() << error;
- delete cached.program;
- cachedPrograms.removeLast();
- return 0;
- } else {
- // taking the address here is safe since
- // cachePrograms isn't resized anywhere else
- return &cached;
+ delete newProg; // Deletes the QGLShaderProgram in it's destructor
+ newProg = 0;
}
+ else {
+ if (cachedPrograms.count() > 30) {
+ // The cache is full, so delete the last 5 programs in the list.
+ // These programs will be least used, as a program us bumped to
+ // the top of the list when it's used.
+ for (int i = 0; i < 5; ++i) {
+ delete cachedPrograms.last();
+ cachedPrograms.removeLast();
+ }
+ }
+
+ cachedPrograms.insert(0, newProg);
+ }
+
+ return newProg;
}
+void QGLEngineSharedShaders::cleanupCustomStage(QGLCustomShaderStage* stage)
+{
+ // Remove any shader programs which has this as the custom shader src:
+ for (int i = 0; i < cachedPrograms.size(); ++i) {
+ QGLEngineShaderProg *cachedProg = cachedPrograms[i];
+ if (cachedProg->customStageSource == stage->source()) {
+ delete cachedProg;
+ cachedPrograms.removeAt(i);
+ i--;
+ }
+ }
+}
+
+
QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context)
: ctx(context),
shaderProgNeedsChanging(true),
@@ -335,8 +350,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context)
maskType(NoMask),
compositionMode(QPainter::CompositionMode_SourceOver),
customSrcStage(0),
- currentShaderProg(0),
- customShader(0)
+ currentShaderProg(0)
{
sharedShaders = QGLEngineSharedShaders::shadersForContext(context);
connect(sharedShaders, SIGNAL(shaderProgNeedsChanging()), this, SLOT(shaderProgNeedsChangingSlot()));
@@ -345,6 +359,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context)
QGLEngineShaderManager::~QGLEngineShaderManager()
{
//###
+ removeCustomStage();
}
uint QGLEngineShaderManager::getUniformLocation(Uniform id)
@@ -436,21 +451,20 @@ void QGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode)
void QGLEngineShaderManager::setCustomStage(QGLCustomShaderStage* stage)
{
+ if (customSrcStage)
+ removeCustomStage();
customSrcStage = stage;
- customShader = 0; // Will be compiled from 'customSrcStage' later.
shaderProgNeedsChanging = true;
}
-void QGLEngineShaderManager::removeCustomStage(QGLCustomShaderStage* stage)
+void QGLEngineShaderManager::removeCustomStage()
{
- Q_UNUSED(stage); // Currently we only support one at a time...
-
+ if (customSrcStage)
+ customSrcStage->setInactive();
customSrcStage = 0;
- customShader = 0;
shaderProgNeedsChanging = true;
}
-
QGLShaderProgram* QGLEngineShaderManager::currentProgram()
{
return currentShaderProg->program;
@@ -476,28 +490,27 @@ bool QGLEngineShaderManager::useCorrectShaderProg()
return false;
bool useCustomSrc = customSrcStage != 0;
- if (useCustomSrc && srcPixelType != QGLEngineShaderManager::ImageSrc) {
+ if (useCustomSrc && srcPixelType != QGLEngineShaderManager::ImageSrc && srcPixelType != Qt::TexturePattern) {
useCustomSrc = false;
qWarning("QGLEngineShaderManager - Ignoring custom shader stage for non image src");
}
QGLEngineShaderProg requiredProgram;
- requiredProgram.program = 0;
bool texCoords = false;
// Choose vertex shader shader position function (which typically also sets
// varyings) and the source pixel (srcPixel) fragment shader function:
- QGLEngineSharedShaders::ShaderName positionVertexShaderName = QGLEngineSharedShaders::InvalidShaderName;
- QGLEngineSharedShaders::ShaderName srcPixelFragShaderName = QGLEngineSharedShaders::InvalidShaderName;
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::InvalidSnippetName;
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::InvalidSnippetName;
bool isAffine = brushTransform.isAffine();
if ( (srcPixelType >= Qt::Dense1Pattern) && (srcPixelType <= Qt::DiagCrossPattern) ) {
if (isAffine)
- positionVertexShaderName = QGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader;
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader;
else
- positionVertexShaderName = QGLEngineSharedShaders::PositionWithPatternBrushVertexShader;
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionWithPatternBrushVertexShader;
- srcPixelFragShaderName = QGLEngineSharedShaders::PatternBrushSrcFragmentShader;
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::PatternBrushSrcFragmentShader;
}
else switch (srcPixelType) {
default:
@@ -505,177 +518,162 @@ bool QGLEngineShaderManager::useCorrectShaderProg()
qFatal("QGLEngineShaderManager::useCorrectShaderProg() - Qt::NoBrush style is set");
break;
case QGLEngineShaderManager::ImageSrc:
- srcPixelFragShaderName = QGLEngineSharedShaders::ImageSrcFragmentShader;
- positionVertexShaderName = QGLEngineSharedShaders::PositionOnlyVertexShader;
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ImageSrcFragmentShader;
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader;
texCoords = true;
break;
case QGLEngineShaderManager::NonPremultipliedImageSrc:
- srcPixelFragShaderName = QGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader;
- positionVertexShaderName = QGLEngineSharedShaders::PositionOnlyVertexShader;
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader;
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader;
texCoords = true;
break;
case QGLEngineShaderManager::PatternSrc:
- srcPixelFragShaderName = QGLEngineSharedShaders::ImageSrcWithPatternFragmentShader;
- positionVertexShaderName = QGLEngineSharedShaders::PositionOnlyVertexShader;
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ImageSrcWithPatternFragmentShader;
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader;
texCoords = true;
break;
case QGLEngineShaderManager::TextureSrcWithPattern:
- srcPixelFragShaderName = QGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader;
- positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader;
+ requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
: QGLEngineSharedShaders::PositionWithTextureBrushVertexShader;
break;
case Qt::SolidPattern:
- srcPixelFragShaderName = QGLEngineSharedShaders::SolidBrushSrcFragmentShader;
- positionVertexShaderName = QGLEngineSharedShaders::PositionOnlyVertexShader;
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::SolidBrushSrcFragmentShader;
+ requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader;
break;
case Qt::LinearGradientPattern:
- srcPixelFragShaderName = QGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader;
- positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader;
+ requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader
: QGLEngineSharedShaders::PositionWithLinearGradientBrushVertexShader;
break;
case Qt::ConicalGradientPattern:
- srcPixelFragShaderName = QGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader;
- positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader;
+ requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader
: QGLEngineSharedShaders::PositionWithConicalGradientBrushVertexShader;
break;
case Qt::RadialGradientPattern:
- srcPixelFragShaderName = QGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader;
- positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader;
+ requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader
: QGLEngineSharedShaders::PositionWithRadialGradientBrushVertexShader;
break;
case Qt::TexturePattern:
- srcPixelFragShaderName = QGLEngineSharedShaders::TextureBrushSrcFragmentShader;
- positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::TextureBrushSrcFragmentShader;
+ requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
: QGLEngineSharedShaders::PositionWithTextureBrushVertexShader;
break;
};
- requiredProgram.positionVertexShader = sharedShaders->compileNamedShader(positionVertexShaderName, QGLShader::PartialVertexShader);
+
if (useCustomSrc) {
- if (!customShader)
- customShader = sharedShaders->compileCustomShader(customSrcStage, QGLShader::PartialFragmentShader);
- requiredProgram.srcPixelFragShader = customShader;
- } else {
- requiredProgram.srcPixelFragShader = sharedShaders->compileNamedShader(srcPixelFragShaderName, QGLShader::PartialFragmentShader);
+ requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::CustomImageSrcFragmentShader;
+ requiredProgram.customStageSource = customSrcStage->source();
}
const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus;
const bool hasMask = maskType != QGLEngineShaderManager::NoMask;
// Choose fragment shader main function:
- QGLEngineSharedShaders::ShaderName mainFragShaderName;
-
if (opacityMode == AttributeOpacity) {
Q_ASSERT(!hasCompose && !hasMask);
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_ImageArrays;
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_ImageArrays;
} else {
bool useGlobalOpacity = (opacityMode == UniformOpacity);
if (hasCompose && hasMask && useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CMO;
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CMO;
if (hasCompose && hasMask && !useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CM;
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CM;
if (!hasCompose && hasMask && useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_MO;
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_MO;
if (!hasCompose && hasMask && !useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_M;
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_M;
if (hasCompose && !hasMask && useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CO;
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CO;
if (hasCompose && !hasMask && !useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_C;
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_C;
if (!hasCompose && !hasMask && useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_O;
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_O;
if (!hasCompose && !hasMask && !useGlobalOpacity)
- mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader;
+ requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader;
}
- requiredProgram.mainFragShader = sharedShaders->compileNamedShader(mainFragShaderName, QGLShader::PartialFragmentShader);
-
if (hasMask) {
- QGLEngineSharedShaders::ShaderName maskShaderName = QGLEngineSharedShaders::InvalidShaderName;
if (maskType == PixelMask) {
- maskShaderName = QGLEngineSharedShaders::MaskFragmentShader;
+ requiredProgram.maskFragShader = QGLEngineSharedShaders::MaskFragmentShader;
texCoords = true;
} else if (maskType == SubPixelMaskPass1) {
- maskShaderName = QGLEngineSharedShaders::RgbMaskFragmentShaderPass1;
+ requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskFragmentShaderPass1;
texCoords = true;
} else if (maskType == SubPixelMaskPass2) {
- maskShaderName = QGLEngineSharedShaders::RgbMaskFragmentShaderPass2;
+ requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskFragmentShaderPass2;
texCoords = true;
} else if (maskType == SubPixelWithGammaMask) {
- maskShaderName = QGLEngineSharedShaders::RgbMaskWithGammaFragmentShader;
+ requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskWithGammaFragmentShader;
texCoords = true;
} else {
qCritical("QGLEngineShaderManager::useCorrectShaderProg() - Unknown mask type");
}
-
- requiredProgram.maskFragShader = sharedShaders->compileNamedShader(maskShaderName, QGLShader::PartialFragmentShader);
} else {
- requiredProgram.maskFragShader = 0;
+ requiredProgram.maskFragShader = QGLEngineSharedShaders::NoMaskFragmentShader;
}
if (hasCompose) {
- QGLEngineSharedShaders::ShaderName compositionShaderName = QGLEngineSharedShaders::InvalidShaderName;
switch (compositionMode) {
case QPainter::CompositionMode_Multiply:
- compositionShaderName = QGLEngineSharedShaders::MultiplyCompositionModeFragmentShader;
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::MultiplyCompositionModeFragmentShader;
break;
case QPainter::CompositionMode_Screen:
- compositionShaderName = QGLEngineSharedShaders::ScreenCompositionModeFragmentShader;
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::ScreenCompositionModeFragmentShader;
break;
case QPainter::CompositionMode_Overlay:
- compositionShaderName = QGLEngineSharedShaders::OverlayCompositionModeFragmentShader;
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::OverlayCompositionModeFragmentShader;
break;
case QPainter::CompositionMode_Darken:
- compositionShaderName = QGLEngineSharedShaders::DarkenCompositionModeFragmentShader;
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::DarkenCompositionModeFragmentShader;
break;
case QPainter::CompositionMode_Lighten:
- compositionShaderName = QGLEngineSharedShaders::LightenCompositionModeFragmentShader;
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::LightenCompositionModeFragmentShader;
break;
case QPainter::CompositionMode_ColorDodge:
- compositionShaderName = QGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader;
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader;
break;
case QPainter::CompositionMode_ColorBurn:
- compositionShaderName = QGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader;
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader;
break;
case QPainter::CompositionMode_HardLight:
- compositionShaderName = QGLEngineSharedShaders::HardLightCompositionModeFragmentShader;
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::HardLightCompositionModeFragmentShader;
break;
case QPainter::CompositionMode_SoftLight:
- compositionShaderName = QGLEngineSharedShaders::SoftLightCompositionModeFragmentShader;
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::SoftLightCompositionModeFragmentShader;
break;
case QPainter::CompositionMode_Difference:
- compositionShaderName = QGLEngineSharedShaders::DifferenceCompositionModeFragmentShader;
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::DifferenceCompositionModeFragmentShader;
break;
case QPainter::CompositionMode_Exclusion:
- compositionShaderName = QGLEngineSharedShaders::ExclusionCompositionModeFragmentShader;
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::ExclusionCompositionModeFragmentShader;
break;
default:
qWarning("QGLEngineShaderManager::useCorrectShaderProg() - Unsupported composition mode");
}
- requiredProgram.compositionFragShader = sharedShaders->compileNamedShader(compositionShaderName, QGLShader::PartialFragmentShader);
} else {
- requiredProgram.compositionFragShader = 0;
+ requiredProgram.compositionFragShader = QGLEngineSharedShaders::NoCompositionModeFragmentShader;
}
- // Choose vertex shader main function
- QGLEngineSharedShaders::ShaderName mainVertexShaderName = QGLEngineSharedShaders::InvalidShaderName;
+ // Choose vertex shader main function
if (opacityMode == AttributeOpacity) {
Q_ASSERT(texCoords);
- mainVertexShaderName = QGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader;
+ requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader;
} else if (texCoords) {
- mainVertexShaderName = QGLEngineSharedShaders::MainWithTexCoordsVertexShader;
+ requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainWithTexCoordsVertexShader;
} else {
- mainVertexShaderName = QGLEngineSharedShaders::MainVertexShader;
+ requiredProgram.mainVertexShader = 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
currentShaderProg = sharedShaders->findProgramInCache(requiredProgram);
if (currentShaderProg) {
- currentShaderProg->program->enable();
+ currentShaderProg->program->bind();
if (useCustomSrc)
customSrcStage->setUniforms(currentShaderProg->program);
}
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
index 291d24c..50c1432 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
@@ -234,32 +234,6 @@ QT_BEGIN_NAMESPACE
QT_MODULE(OpenGL)
-struct QGLEngineShaderProg
-{
- QGLShader* mainVertexShader;
- QGLShader* positionVertexShader;
- QGLShader* mainFragShader;
- QGLShader* srcPixelFragShader;
- QGLShader* maskFragShader; // Can be null for no mask
- QGLShader* compositionFragShader; // Can be null for GL-handled mode
- QGLShaderProgram* program;
-
- QVector<uint> uniformLocations;
-
- bool useTextureCoords;
- bool useOpacityAttribute;
-
- bool operator==(const QGLEngineShaderProg& other) {
- // We don't care about the program
- return ( mainVertexShader == other.mainVertexShader &&
- positionVertexShader == other.positionVertexShader &&
- mainFragShader == other.mainFragShader &&
- srcPixelFragShader == other.srcPixelFragShader &&
- maskFragShader == other.maskFragShader &&
- compositionFragShader == other.compositionFragShader
- );
- }
-};
/*
struct QGLEngineCachedShaderProg
@@ -280,15 +254,19 @@ static const GLuint QT_VERTEX_COORDS_ATTR = 0;
static const GLuint QT_TEXTURE_COORDS_ATTR = 1;
static const GLuint QT_OPACITY_ATTR = 2;
+class QGLEngineShaderProg;
+
class QGLEngineSharedShaders : public QObject
{
Q_OBJECT
public:
- enum ShaderName {
+
+ enum SnippetName {
MainVertexShader,
MainWithTexCoordsVertexShader,
MainWithTexCoordsAndOpacityVertexShader,
+ // UntransformedPositionVertexShader must be first in the list:
UntransformedPositionVertexShader,
PositionOnlyVertexShader,
PositionWithPatternBrushVertexShader,
@@ -302,6 +280,7 @@ public:
AffinePositionWithRadialGradientBrushVertexShader,
AffinePositionWithTextureBrushVertexShader,
+ // MainFragmentShader_CMO must be first in the list:
MainFragmentShader_CMO,
MainFragmentShader_CM,
MainFragmentShader_MO,
@@ -312,6 +291,7 @@ public:
MainFragmentShader,
MainFragmentShader_ImageArrays,
+ // ImageSrcFragmentShader must be first in the list::
ImageSrcFragmentShader,
ImageSrcWithPatternFragmentShader,
NonPremultipliedImageSrcFragmentShader,
@@ -325,11 +305,15 @@ public:
ConicalGradientBrushSrcFragmentShader,
ShockingPinkSrcFragmentShader,
+ // NoMaskFragmentShader must be first in the list:
+ NoMaskFragmentShader,
MaskFragmentShader,
RgbMaskFragmentShaderPass1,
RgbMaskFragmentShaderPass2,
RgbMaskWithGammaFragmentShader,
+ // NoCompositionModeFragmentShader must be first in the list:
+ NoCompositionModeFragmentShader,
MultiplyCompositionModeFragmentShader,
ScreenCompositionModeFragmentShader,
OverlayCompositionModeFragmentShader,
@@ -342,37 +326,88 @@ public:
DifferenceCompositionModeFragmentShader,
ExclusionCompositionModeFragmentShader,
- TotalShaderCount, InvalidShaderName
+ TotalSnippetCount, InvalidSnippetName
};
+#if defined (QT_DEBUG)
+ Q_ENUMS(SnippetName)
+ static QByteArray snippetNameStr(SnippetName snippetName);
+#endif
- QGLEngineSharedShaders(const QGLContext *context);
+/*
+ // These allow the ShaderName enum to be used as a cache key
+ const int mainVertexOffset = 0;
+ const int positionVertexOffset = (1<<2) - PositionOnlyVertexShader;
+ const int mainFragOffset = (1<<6) - MainFragmentShader_CMO;
+ const int srcPixelOffset = (1<<10) - ImageSrcFragmentShader;
+ const int maskOffset = (1<<14) - NoMaskShader;
+ const int compositionOffset = (1 << 16) - MultiplyCompositionModeFragmentShader;
+*/
- QGLShader *compileNamedShader(ShaderName name, QGLShader::ShaderType type);
+ QGLEngineSharedShaders(const QGLContext *context);
QGLShaderProgram *simpleProgram() { return simpleShaderProg; }
QGLShaderProgram *blitProgram() { return blitShaderProg; }
// Compile the program if it's not already in the cache, return the item in the cache.
QGLEngineShaderProg *findProgramInCache(const QGLEngineShaderProg &prog);
// Compile the custom shader if it's not already in the cache, return the item in the cache.
- QGLShader *compileCustomShader(QGLCustomShaderStage *stage, QGLShader::ShaderType type);
static QGLEngineSharedShaders *shadersForContext(const QGLContext *context);
+ // Ideally, this would be static and cleanup all programs in all contexts which
+ // contain the custom code. Currently it is just a hint and we rely on deleted
+ // custom shaders being cleaned up by being kicked out of the cache when it's
+ // full.
+ void cleanupCustomStage(QGLCustomShaderStage* stage);
+
signals:
void shaderProgNeedsChanging();
-private slots:
- void shaderDestroyed(QObject *shader);
-
private:
QGLSharedResourceGuard ctxGuard;
QGLShaderProgram *blitShaderProg;
QGLShaderProgram *simpleShaderProg;
- QList<QGLEngineShaderProg> cachedPrograms;
- QCache<QByteArray, QGLShader> customShaderCache;
- QGLShader* compiledShaders[TotalShaderCount];
+ QList<QGLEngineShaderProg*> cachedPrograms;
- static const char* qglEngineShaderSourceCode[TotalShaderCount];
+ static const char* qShaderSnippets[TotalSnippetCount];
+};
+
+
+class QGLEngineShaderProg
+{
+public:
+ QGLEngineShaderProg() : program(0) {}
+
+ ~QGLEngineShaderProg() {
+ if (program)
+ delete program;
+ }
+
+ QGLEngineSharedShaders::SnippetName mainVertexShader;
+ QGLEngineSharedShaders::SnippetName positionVertexShader;
+ QGLEngineSharedShaders::SnippetName mainFragShader;
+ QGLEngineSharedShaders::SnippetName srcPixelFragShader;
+ QGLEngineSharedShaders::SnippetName maskFragShader;
+ QGLEngineSharedShaders::SnippetName compositionFragShader;
+
+ QByteArray customStageSource; //TODO: Decent cache key for custom stages
+ QGLShaderProgram* program;
+
+ QVector<uint> uniformLocations;
+
+ bool useTextureCoords;
+ bool useOpacityAttribute;
+
+ bool operator==(const QGLEngineShaderProg& other) {
+ // We don't care about the program
+ return ( mainVertexShader == other.mainVertexShader &&
+ positionVertexShader == other.positionVertexShader &&
+ mainFragShader == other.mainFragShader &&
+ srcPixelFragShader == other.srcPixelFragShader &&
+ maskFragShader == other.maskFragShader &&
+ compositionFragShader == other.compositionFragShader &&
+ customStageSource == other.customStageSource
+ );
+ }
};
class Q_OPENGL_EXPORT QGLEngineShaderManager : public QObject
@@ -426,7 +461,7 @@ public:
void setMaskType(MaskType);
void setCompositionMode(QPainter::CompositionMode);
void setCustomStage(QGLCustomShaderStage* stage);
- void removeCustomStage(QGLCustomShaderStage* stage);
+ void removeCustomStage();
uint getUniformLocation(Uniform id);
@@ -437,19 +472,7 @@ public:
QGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers
QGLShaderProgram* blitProgram(); // Used to blit a texture into the framebuffer
-/*
- // These allow the ShaderName enum to be used as a cache key
- const int mainVertexOffset = 0;
- const int positionVertexOffset = (1<<2) - PositionOnlyVertexShader;
- const int mainFragOffset = (1<<6) - MainFragmentShader_CMO;
- const int srcPixelOffset = (1<<10) - ImageSrcFragmentShader;
- const int maskOffset = (1<<14) - NoMaskShader;
- const int compositionOffset = (1 << 16) - MultiplyCompositionModeFragmentShader;
-*/
-
-#if defined (QT_DEBUG)
- Q_ENUMS(ShaderName)
-#endif
+ QGLEngineSharedShaders* sharedShaders;
private slots:
void shaderProgNeedsChangingSlot() { shaderProgNeedsChanging = true; }
@@ -466,9 +489,7 @@ private:
QPainter::CompositionMode compositionMode;
QGLCustomShaderStage* customSrcStage;
- QGLEngineShaderProg* currentShaderProg;
- QGLEngineSharedShaders *sharedShaders;
- QGLShader *customShader;
+ QGLEngineShaderProg* currentShaderProg;
};
QT_END_NAMESPACE
diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
index 3eef808..2407979 100644
--- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
@@ -258,7 +258,7 @@ static const char* const qglslPositionWithTextureBrushVertexShader = "\
uniform mediump vec2 halfViewportSize; \
uniform highp vec2 invertedTextureSize; \
uniform highp mat3 brushTransform; \
- varying highp vec2 brushTextureCoords; \
+ varying highp vec2 textureCoords; \
void setPosition(void) { \
gl_Position = pmvMatrix * vertexCoordsArray;\
gl_Position.xy = gl_Position.xy / gl_Position.w; \
@@ -267,7 +267,7 @@ static const char* const qglslPositionWithTextureBrushVertexShader = "\
mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \
gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \
gl_Position.w = invertedHTexCoordsZ; \
- brushTextureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \
+ textureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \
}";
static const char* const qglslAffinePositionWithTextureBrushVertexShader
@@ -278,26 +278,26 @@ static const char* const qglslAffinePositionWithTextureBrushVertexShader
// we emulate GL_REPEAT by only taking the fractional part of the texture coords.
// TODO: Special case POT textures which don't need this emulation
static const char* const qglslTextureBrushSrcFragmentShader = "\
- varying highp vec2 brushTextureCoords; \
+ varying highp vec2 textureCoords; \
uniform lowp sampler2D brushTexture; \
lowp vec4 srcPixel() { \
- return texture2D(brushTexture, fract(brushTextureCoords)); \
+ return texture2D(brushTexture, fract(textureCoords)); \
}";
#else
static const char* const qglslTextureBrushSrcFragmentShader = "\
- varying highp vec2 brushTextureCoords; \
+ varying highp vec2 textureCoords; \
uniform lowp sampler2D brushTexture; \
lowp vec4 srcPixel() { \
- return texture2D(brushTexture, brushTextureCoords); \
+ return texture2D(brushTexture, textureCoords); \
}";
#endif
static const char* const qglslTextureBrushSrcWithPatternFragmentShader = "\
- varying highp vec2 brushTextureCoords; \
+ varying highp vec2 textureCoords; \
uniform lowp vec4 patternColor; \
uniform lowp sampler2D brushTexture; \
lowp vec4 srcPixel() { \
- return patternColor * (1.0 - texture2D(brushTexture, brushTextureCoords).r); \
+ return patternColor * (1.0 - texture2D(brushTexture, textureCoords).r); \
}";
// Solid Fill Brush
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
index 13efbda..fb9bcb4 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -62,6 +62,8 @@
and use the correct program when we really need it.
*/
+// #define QT_OPENGL_CACHE_AS_VBOS
+
#include "qpaintengineex_opengl2_p.h"
#include <string.h> //for memcpy
@@ -81,6 +83,8 @@
#include "qglengineshadermanager_p.h"
#include "qgl2pexvertexarray_p.h"
+#include "qtriangulatingstroker_p.h"
+
#include <QDebug>
QT_BEGIN_NAMESPACE
@@ -156,8 +160,8 @@ QGLTextureGlyphCache::QGLTextureGlyphCache(QGLContext *context, QFontEngineGlyph
, m_height(0)
{
glGenFramebuffers(1, &m_fbo);
- connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext *)),
- SLOT(contextDestroyed(const QGLContext *)));
+ connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)),
+ SLOT(contextDestroyed(const QGLContext*)));
}
QGLTextureGlyphCache::~QGLTextureGlyphCache()
@@ -244,7 +248,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height)
glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray);
glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray);
- pex->shaderManager->blitProgram()->enable();
+ pex->shaderManager->blitProgram()->bind();
pex->shaderManager->blitProgram()->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT);
pex->shaderManager->setDirty();
@@ -342,6 +346,13 @@ extern QImage qt_imageForBrush(int brushStyle, bool invert);
QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate()
{
delete shaderManager;
+
+ while (pathCaches.size()) {
+ QVectorPath::CacheEntry *e = *(pathCaches.constBegin());
+ e->cleanup(e->engine, e->data);
+ e->data = 0;
+ e->engine = 0;
+ }
}
void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id)
@@ -393,7 +404,7 @@ void QGL2PaintEngineExPrivate::setBrush(const QBrush* brush)
void QGL2PaintEngineExPrivate::useSimpleShader()
{
- shaderManager->simpleProgram()->enable();
+ shaderManager->simpleProgram()->bind();
shaderManager->setDirty();
if (matrixDirty)
@@ -533,7 +544,7 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms()
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
}
- QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 * textureInvertedY / texPixmap.height());
+ QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 / texPixmap.height());
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::InvertedTextureSize), invertedTextureSize);
QVector2D halfViewportSize(width*0.5, height*0.5);
@@ -548,7 +559,11 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms()
QTransform translate(1, 0, 0, 1, -translationPoint.x(), -translationPoint.y());
QTransform gl_to_qt(1, 0, 0, -1, 0, height);
- QTransform inv_matrix = gl_to_qt * (brushQTransform * matrix).inverted() * translate;
+ QTransform inv_matrix;
+ if (style == Qt::TexturePattern && textureInvertedY == -1)
+ inv_matrix = gl_to_qt * (QTransform(1, 0, 0, -1, 0, currentBrush->texture().height()) * brushQTransform * matrix).inverted() * translate;
+ else
+ inv_matrix = gl_to_qt * (brushQTransform * matrix).inverted() * translate;
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTransform), inv_matrix);
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTexture), QT_BRUSH_TEXTURE_UNIT);
@@ -572,62 +587,42 @@ void QGL2PaintEngineExPrivate::updateMatrix()
//
// We expand out the multiplication to save the cost of a full 4x4
// matrix multiplication as most of the components are trivial.
-
const QTransform& transform = q->state()->matrix;
- if (mode == TextDrawingMode) {
- // Text drawing mode is only used for non-scaling transforms
- pmvMatrix[0][0] = 2.0 / width;
- pmvMatrix[0][1] = 0.0;
- pmvMatrix[0][2] = 0.0;
- pmvMatrix[0][3] = 0.0;
- pmvMatrix[1][0] = 0.0;
- pmvMatrix[1][1] = -2.0 / height;
- pmvMatrix[1][2] = 0.0;
- pmvMatrix[1][3] = 0.0;
- pmvMatrix[2][0] = 0.0;
- pmvMatrix[2][1] = 0.0;
- pmvMatrix[2][2] = -1.0;
- pmvMatrix[2][3] = 0.0;
- pmvMatrix[3][0] = pmvMatrix[0][0] * qRound(transform.dx()) - 1.0;
- pmvMatrix[3][1] = pmvMatrix[1][1] * qRound(transform.dy()) + 1.0;
- pmvMatrix[3][2] = 0.0;
- pmvMatrix[3][3] = 1.0;
-
- inverseScale = 1;
- } else {
- qreal wfactor = 2.0 / width;
- qreal hfactor = -2.0 / height;
-
- pmvMatrix[0][0] = wfactor * transform.m11() - transform.m13();
- pmvMatrix[0][1] = hfactor * transform.m12() + transform.m13();
- pmvMatrix[0][2] = 0.0;
- pmvMatrix[0][3] = transform.m13();
- pmvMatrix[1][0] = wfactor * transform.m21() - transform.m23();
- pmvMatrix[1][1] = hfactor * transform.m22() + transform.m23();
- pmvMatrix[1][2] = 0.0;
- pmvMatrix[1][3] = transform.m23();
- pmvMatrix[2][0] = 0.0;
- pmvMatrix[2][1] = 0.0;
- pmvMatrix[2][2] = -1.0;
- pmvMatrix[2][3] = 0.0;
- pmvMatrix[3][0] = wfactor * transform.dx() - transform.m33();
- pmvMatrix[3][1] = hfactor * transform.dy() + transform.m33();
- pmvMatrix[3][2] = 0.0;
- pmvMatrix[3][3] = transform.m33();
-
- // 1/10000 == 0.0001, so we have good enough res to cover curves
- // that span the entire widget...
- inverseScale = qMax(1 / qMax( qMax(qAbs(transform.m11()), qAbs(transform.m22())),
- qMax(qAbs(transform.m12()), qAbs(transform.m21())) ),
- qreal(0.0001));
- }
+ qreal wfactor = 2.0 / width;
+ qreal hfactor = -2.0 / height;
+
+ pmvMatrix[0][0] = wfactor * transform.m11() - transform.m13();
+ pmvMatrix[0][1] = hfactor * transform.m12() + transform.m13();
+ pmvMatrix[0][2] = 0.0;
+ pmvMatrix[0][3] = transform.m13();
+ pmvMatrix[1][0] = wfactor * transform.m21() - transform.m23();
+ pmvMatrix[1][1] = hfactor * transform.m22() + transform.m23();
+ pmvMatrix[1][2] = 0.0;
+ pmvMatrix[1][3] = transform.m23();
+ pmvMatrix[2][0] = 0.0;
+ pmvMatrix[2][1] = 0.0;
+ pmvMatrix[2][2] = -1.0;
+ pmvMatrix[2][3] = 0.0;
+ pmvMatrix[3][0] = wfactor * transform.dx() - transform.m33();
+ pmvMatrix[3][1] = hfactor * transform.dy() + transform.m33();
+ pmvMatrix[3][2] = 0.0;
+ pmvMatrix[3][3] = transform.m33();
+
+ // 1/10000 == 0.0001, so we have good enough res to cover curves
+ // that span the entire widget...
+ inverseScale = qMax(1 / qMax( qMax(qAbs(transform.m11()), qAbs(transform.m22())),
+ qMax(qAbs(transform.m12()), qAbs(transform.m21())) ),
+ qreal(0.0001));
matrixDirty = false;
// The actual data has been updated so both shader program's uniforms need updating
simpleShaderMatrixUniformDirty = true;
shaderMatrixUniformDirty = true;
+
+ dasher.setInvScale(inverseScale);
+ stroker.setInvScale(inverseScale);
}
@@ -800,17 +795,12 @@ void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode)
lastTexture = GLuint(-1);
}
- if (mode == TextDrawingMode)
- matrixDirty = true;
-
if (newMode == TextDrawingMode) {
glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
glEnableVertexAttribArray(QT_TEXTURE_COORDS_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());
-
- matrixDirty = true;
}
if (newMode == ImageDrawingMode) {
@@ -838,28 +828,30 @@ void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode)
mode = newMode;
}
-void QGL2PaintEngineExPrivate::drawOutline(const QVectorPath& path)
+struct QGL2PEVectorPathCache
{
- transferMode(BrushDrawingMode);
-
- // Might need to call updateMatrix to re-calculate inverseScale
- if (matrixDirty)
- updateMatrix();
-
- vertexCoordinateArray.clear();
- vertexCoordinateArray.addPath(path, inverseScale);
-
- if (path.hasImplicitClose()) {
- // Close the path's outline
- vertexCoordinateArray.lineToArray(path.points()[0], path.points()[1]);
- vertexCoordinateArray.stops().last() += 1;
- }
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+ GLuint vbo;
+#else
+ float *vertices;
+#endif
+ int vertexCount;
+ GLenum primitiveType;
+ qreal iscale;
+};
- prepareForDraw(currentBrush->isOpaque());
- drawVertexArrays(vertexCoordinateArray, GL_LINE_STRIP);
+void qopengl2paintengine_cleanup_vectorpath(QPaintEngineEx *engine, void *data)
+{
+ QGL2PEVectorPathCache *c = (QGL2PEVectorPathCache *) data;
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+ QGL2PaintEngineExPrivate *d = QGL2PaintEngineExPrivate::getData((QGL2PaintEngineEx *) engine);
+ d->unusedVBOSToClean << c->vbo;
+#else
+ qFree(c->vertices);
+#endif
+ delete c;
}
-
// Assumes everything is configured for the brush you want to use
void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
{
@@ -876,13 +868,75 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
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
- || path.shape() == QVectorPath::ConvexPolygonHint)
- {
- vertexCoordinateArray.clear();
- vertexCoordinateArray.addPath(path, inverseScale, false);
- prepareForDraw(currentBrush->isOpaque());
- drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
+ } else if (path.isConvex()) {
+
+ if (path.isCacheable()) {
+ QVectorPath::CacheEntry *data = path.lookupCacheData(q);
+ QGL2PEVectorPathCache *cache;
+
+ if (data) {
+ cache = (QGL2PEVectorPathCache *) data->data;
+ // Check if scale factor is exceeded for curved paths and generate curves if so...
+ if (path.isCurved()) {
+ qreal scaleFactor = cache->iscale / inverseScale;
+ if (scaleFactor < 0.5 || scaleFactor > 2.0) {
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+ glDeleteBuffers(1, &cache->vbo);
+ cache->vbo = 0;
+#else
+ qFree(cache->vertices);
+#endif
+ cache->vertexCount = 0;
+ }
+ }
+ } else {
+ cache = new QGL2PEVectorPathCache;
+ cache->vertexCount = 0;
+ data = const_cast<QVectorPath &>(path).addCacheData(q, cache, qopengl2paintengine_cleanup_vectorpath);
+ }
+
+ // Flatten the path at the current scale factor and fill it into the cache struct.
+ if (!cache->vertexCount) {
+ vertexCoordinateArray.clear();
+ vertexCoordinateArray.addPath(path, inverseScale, false);
+ int vertexCount = vertexCoordinateArray.vertexCount();
+ int floatSizeInBytes = vertexCount * 2 * sizeof(float);
+ cache->vertexCount = vertexCount;
+ cache->primitiveType = GL_TRIANGLE_FAN;
+ cache->iscale = inverseScale;
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+ glGenBuffers(1, &cache->vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
+ glBufferData(GL_ARRAY_BUFFER, floatSizeInBytes, vertexCoordinateArray.data(), GL_STATIC_DRAW);
+#else
+ cache->vertices = (float *) qMalloc(floatSizeInBytes);
+ memcpy(cache->vertices, vertexCoordinateArray.data(), floatSizeInBytes);
+#endif
+ }
+
+ prepareForDraw(currentBrush->isOpaque());
+ glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+ glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, false, 0, 0);
+#else
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, false, 0, cache->vertices);
+#endif
+ glDrawArrays(cache->primitiveType, 0, cache->vertexCount);
+
+ } else {
+ // printf(" - Marking path as cachable...\n");
+ // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable
+ // ### Remove before release...
+ static bool do_vectorpath_cache = qgetenv("QT_OPENGL_NO_PATH_CACHE").isEmpty();
+ if (do_vectorpath_cache)
+ path.makeCacheable();
+ vertexCoordinateArray.clear();
+ vertexCoordinateArray.addPath(path, inverseScale, false);
+ prepareForDraw(currentBrush->isOpaque());
+ drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
+ }
+
} else {
// The path is too complicated & needs the stencil technique
vertexCoordinateArray.clear();
@@ -922,8 +976,15 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
}
-void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill)
+void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data,
+ int count,
+ int *stops,
+ int stopCount,
+ const QGLRect &bounds,
+ StencilFillMode mode)
{
+ Q_ASSERT(count || stops);
+
// qDebug("QGL2PaintEngineExPrivate::fillStencilWithVertexArray()");
glStencilMask(0xff); // Enable stencil writes
@@ -955,19 +1016,20 @@ void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& ve
}
#endif
- if (useWindingFill) {
+ if (mode == WindingFillMode) {
+ Q_ASSERT(stops && !count);
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());
+ composite(bounds);
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());
+ composite(bounds);
}
// Inc. for front-facing triangle
@@ -975,19 +1037,43 @@ void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& ve
// 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);
+ drawVertexArrays(data, stops, stopCount, 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());
+ composite(bounds);
}
- } else {
+ } else if (mode == OddEvenFillMode) {
+ glStencilMask(GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
+ drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN);
+
+ } else { // TriStripStrokeFillMode
+ Q_ASSERT(count && !stops); // tristrips generated directly, so no vertexArray or stops
glStencilMask(GL_STENCIL_HIGH_BIT);
+#if 0
glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
- drawVertexArrays(vertexArray, GL_TRIANGLE_FAN);
+ glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, data);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
+ glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+#else
+
+ glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+ if (q->state()->clipTestEnabled) {
+ glStencilFunc(GL_LEQUAL, q->state()->currentClip | GL_STENCIL_HIGH_BIT,
+ ~GL_STENCIL_HIGH_BIT);
+ } else {
+ glStencilFunc(GL_ALWAYS, GL_STENCIL_HIGH_BIT, 0xff);
+ }
+ glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, data);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
+ glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+#endif
}
// Enable color writes & disable stencil writes
@@ -1122,14 +1208,16 @@ void QGL2PaintEngineExPrivate::composite(const QGLRect& boundingRect)
}
// Draws the vertex array as a set of <vertexArrayStops.size()> triangle fans.
-void QGL2PaintEngineExPrivate::drawVertexArrays(QGL2PEXVertexArray& vertexArray, GLenum primitive)
+void QGL2PaintEngineExPrivate::drawVertexArrays(const float *data, int *stops, int stopCount,
+ GLenum primitive)
{
// Now setup the pointer to the vertex array:
glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
- glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexArray.data());
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, data);
int previousStop = 0;
- foreach(int stop, vertexArray.stops()) {
+ for (int i=0; i<stopCount; ++i) {
+ int stop = stops[i];
/*
qDebug("Drawing triangle fan for vertecies %d -> %d:", previousStop, stop-1);
for (int i=previousStop; i<stop; ++i)
@@ -1180,14 +1268,36 @@ void QGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush)
{
Q_D(QGL2PaintEngineEx);
- if (qbrush_style(brush) == Qt::NoBrush)
+ Qt::BrushStyle style = qbrush_style(brush);
+ if (style == Qt::NoBrush)
return;
if (!d->inRenderText)
ensureActive();
+
+ QOpenGL2PaintEngineState *s = state();
+ bool doOffset = !(s->renderHints & QPainter::Antialiasing) &&
+ (style == Qt::SolidPattern) &&
+ !d->multisamplingAlwaysEnabled;
+
+ if (doOffset) {
+ d->temporaryTransform = s->matrix;
+ QTransform tx = QTransform::fromTranslate(.49, .49);
+ s->matrix = s->matrix * tx;
+ d->matrixDirty = true;
+ }
+
d->setBrush(&brush);
d->fill(path);
+
+ if (doOffset) {
+ s->matrix = d->temporaryTransform;
+ d->matrixDirty = true;
+ }
}
+extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
+
+
void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
{
Q_D(QGL2PaintEngineEx);
@@ -1197,23 +1307,95 @@ void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
if (penStyle == Qt::NoPen || qbrush_style(penBrush) == Qt::NoBrush)
return;
+ QOpenGL2PaintEngineState *s = state();
+ if (pen.isCosmetic() && !qt_scaleForTransform(s->transform(), 0)) {
+ // QTriangulatingStroker class is not meant to support cosmetically sheared strokes.
+ QPaintEngineEx::stroke(path, pen);
+ return;
+ }
+
ensureActive();
- qreal penWidth = qpen_widthf(pen);
- if ( (pen.isCosmetic() && (penStyle == Qt::SolidLine)) && (penWidth < 2.5f) )
- {
- // We only handle solid, cosmetic pens with a width of 1 pixel
- const QBrush& brush = pen.brush();
- d->setBrush(&brush);
+ bool doOffset = !(s->renderHints & QPainter::Antialiasing) && !d->multisamplingAlwaysEnabled;
+ if (doOffset) {
+ d->temporaryTransform = s->matrix;
+ QTransform tx = QTransform::fromTranslate(0.49, .49);
+ s->matrix = s->matrix * tx;
+ d->matrixDirty = true;
+ }
- if (penWidth < 0.01f)
- glLineWidth(1.0);
- else
- glLineWidth(penWidth);
+ bool opaque = penBrush.isOpaque() && s->opacity > 0.99;
+ d->setBrush(&penBrush);
+ d->transferMode(BrushDrawingMode);
+
+ // updateMatrix() is responsible for setting the inverse scale on
+ // the strokers, so we need to call it here and not wait for
+ // prepareForDraw() down below.
+ d->updateMatrix();
+
+ if (penStyle == Qt::SolidLine) {
+ d->stroker.process(path, pen);
+
+ } else { // Some sort of dash
+ d->dasher.process(path, pen);
- d->drawOutline(path);
- } else
- return QPaintEngineEx::stroke(path, pen);
+ QVectorPath dashStroke(d->dasher.points(),
+ d->dasher.elementCount(),
+ d->dasher.elementTypes());
+ d->stroker.process(dashStroke, pen);
+ }
+
+
+ QGLContext *ctx = d->ctx;
+ Q_UNUSED(ctx);
+
+ if (opaque) {
+ d->prepareForDraw(opaque);
+ glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+ glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, false, 0, d->stroker.vertices());
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, d->stroker.vertexCount() / 2);
+
+// QBrush b(Qt::green);
+// d->setBrush(&b);
+// d->prepareForDraw(true);
+// glDrawArrays(GL_LINE_STRIP, 0, d->stroker.vertexCount() / 2);
+
+ glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+
+ } else {
+ qreal width = qpen_widthf(pen) / 2;
+ if (width == 0)
+ width = 0.5;
+ qreal extra = pen.joinStyle() == Qt::MiterJoin
+ ? qMax(pen.miterLimit() * width, width)
+ : width;
+
+ if (pen.isCosmetic())
+ extra = extra * d->inverseScale;
+
+ QRectF bounds = path.controlPointRect().adjusted(-extra, -extra, extra, extra);
+
+ d->fillStencilWithVertexArray(d->stroker.vertices(), d->stroker.vertexCount() / 2,
+ 0, 0, bounds, QGL2PaintEngineExPrivate::TriStripStrokeFillMode);
+
+ glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
+
+ // Pass when any bit is set, replace stencil value with 0
+ glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT);
+ d->prepareForDraw(false);
+
+ // Stencil the brush onto the dest buffer
+ d->composite(bounds);
+
+ glStencilMask(0);
+
+ d->updateClipScissorTest();
+ }
+
+ if (doOffset) {
+ s->matrix = d->temporaryTransform;
+ d->matrixDirty = true;
+ }
}
void QGL2PaintEngineEx::penChanged() { }
@@ -1336,20 +1518,21 @@ void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem
const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
- bool drawCached = true;
+ QTransform::TransformationType txtype = s->matrix.type();
- if (s->matrix.type() > QTransform::TxTranslate)
- drawCached = false;
+ float det = s->matrix.determinant();
+ bool drawCached = txtype < QTransform::TxProject;
- // don't try to cache huge fonts
- if (ti.fontEngine->fontDef.pixelSize * qSqrt(s->matrix.determinant()) >= 64)
+ // don't try to cache huge fonts or vastly transformed fonts
+ const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
+ if (pixelSize * pixelSize * qAbs(det) >= 64 * 64 || det < 0.25f || det > 4.f)
drawCached = false;
QFontEngineGlyphCache::Type glyphType = ti.fontEngine->glyphFormat >= 0
? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat)
: d->glyphCacheType;
- if (d->inRenderText)
+ if (d->inRenderText || txtype > QTransform::TxTranslate)
glyphType = QFontEngineGlyphCache::Raster_A8;
if (glyphType == QFontEngineGlyphCache::Raster_RGBMask
@@ -1371,7 +1554,6 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly
const QTextItemInt &ti)
{
Q_Q(QGL2PaintEngineEx);
- QOpenGL2PaintEngineState *s = q->state();
QVarLengthArray<QFixedPoint> positions;
QVarLengthArray<glyph_t> glyphs;
@@ -1379,10 +1561,10 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly
ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
QGLTextureGlyphCache *cache =
- (QGLTextureGlyphCache *) ti.fontEngine->glyphCache(ctx, s->matrix);
+ (QGLTextureGlyphCache *) ti.fontEngine->glyphCache(ctx, glyphType, QTransform());
if (!cache || cache->cacheType() != glyphType) {
- cache = new QGLTextureGlyphCache(ctx, glyphType, s->matrix);
+ cache = new QGLTextureGlyphCache(ctx, glyphType, QTransform());
ti.fontEngine->setGlyphCache(ctx, cache);
}
@@ -1542,7 +1724,7 @@ void QGL2PaintEngineEx::drawPixmaps(const QDrawPixmaps::Data *drawingData, int d
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);
@@ -1643,7 +1825,8 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
d->device->beginPaint();
#if !defined(QT_OPENGL_ES_2)
- bool success = qt_resolve_version_2_0_functions(d->ctx);
+ bool success = qt_resolve_version_2_0_functions(d->ctx)
+ && qt_resolve_buffer_extensions(d->ctx);
Q_ASSERT(success);
Q_UNUSED(success);
#endif
@@ -1663,13 +1846,18 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
#if !defined(QT_OPENGL_ES_2)
- if (!d->device->format().alpha()
#if defined(Q_WS_WIN)
- && qt_cleartype_enabled
+ if (qt_cleartype_enabled)
#endif
- ) {
d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
- }
+#endif
+
+#if defined(QT_OPENGL_ES_2)
+ // OpenGL ES can't switch MSAA off, so if the gl paint device is
+ // multisampled, it's always multisampled.
+ d->multisamplingAlwaysEnabled = d->device->format().sampleBuffers();
+#else
+ d->multisamplingAlwaysEnabled = false;
#endif
return true;
@@ -1699,6 +1887,13 @@ bool QGL2PaintEngineEx::end()
delete d->shaderManager;
d->shaderManager = 0;
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+ if (!d->unusedVBOSToClean.isEmpty()) {
+ glDeleteBuffers(d->unusedVBOSToClean.size(), d->unusedVBOSToClean.constData());
+ d->unusedVBOSToClean.clear();
+ }
+#endif
+
return false;
}
@@ -1989,7 +2184,7 @@ void QGL2PaintEngineExPrivate::systemStateChanged()
q->state()->rectangleClip = use_system_clip ? systemClip.boundingRect() : QRect(0, 0, width, height);
updateClipScissorTest();
- if (systemClip.numRects() == 1) {
+ if (systemClip.rectCount() == 1) {
if (systemClip.boundingRect() == QRect(0, 0, width, height))
use_system_clip = false;
#ifndef QT_GL_NO_SCISSOR_TEST
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
index 5704a04..0084476 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
@@ -62,6 +62,7 @@
#include <private/qglpixmapfilter_p.h>
#include <private/qfontengine_p.h>
#include <private/qdatabuffer_p.h>
+#include <private/qtriangulatingstroker_p.h>
enum EngineMode {
ImageDrawingMode,
@@ -160,6 +161,12 @@ class QGL2PaintEngineExPrivate : public QPaintEngineExPrivate
{
Q_DECLARE_PUBLIC(QGL2PaintEngineEx)
public:
+ enum StencilFillMode {
+ OddEvenFillMode,
+ WindingFillMode,
+ TriStripStrokeFillMode
+ };
+
QGL2PaintEngineExPrivate(QGL2PaintEngineEx *q_ptr) :
q(q_ptr),
width(0), height(0),
@@ -185,15 +192,24 @@ public:
// fill, drawOutline, drawTexture & drawCachedGlyphs are the rendering entry points:
void fill(const QVectorPath &path);
- void drawOutline(const QVectorPath& path);
void drawTexture(const QGLRect& dest, const QGLRect& src, const QSize &textureSize, bool opaque, bool pattern = false);
void drawCachedGlyphs(const QPointF &p, QFontEngineGlyphCache::Type glyphType, const QTextItemInt &ti);
- void drawVertexArrays(QGL2PEXVertexArray& vertexArray, GLenum primitive);
+ void drawVertexArrays(const float *data, int *stops, int stopCount, GLenum primitive);
+ void drawVertexArrays(QGL2PEXVertexArray &vertexArray, GLenum primitive) {
+ drawVertexArrays((const float *) vertexArray.data(), vertexArray.stops(), vertexArray.stopCount(), primitive);
+ }
+
// ^ draws whatever is in the vertex array
void composite(const QGLRect& boundingRect);
// ^ Composites the bounding rect onto dest buffer
- void fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill);
+
+ void fillStencilWithVertexArray(const float *data, int count, int *stops, int stopCount, const QGLRect &bounds, StencilFillMode mode);
+ void fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill) {
+ fillStencilWithVertexArray((const float *) vertexArray.data(), 0, vertexArray.stops(), vertexArray.stopCount(),
+ vertexArray.boundingRect(),
+ useWindingFill ? WindingFillMode : OddEvenFillMode);
+ }
// ^ Calls drawVertexArrays to render into stencil buffer
bool prepareForDraw(bool srcPixelsAreOpaque);
@@ -205,6 +221,7 @@ public:
void restoreDepthRangeForRenderText();
static QGLEngineShaderManager* shaderManagerForEngine(QGL2PaintEngineEx *engine) { return engine->d_func()->shaderManager; }
+ static QGL2PaintEngineExPrivate *getData(QGL2PaintEngineEx *engine) { return engine->d_func(); }
QGL2PaintEngineEx* q;
QGLPaintDevice* device;
@@ -261,17 +278,26 @@ public:
bool needsSync;
bool inRenderText;
+ bool multisamplingAlwaysEnabled;
GLfloat depthRange[2];
float textureInvertedY;
+ QTriangulatingStroker stroker;
+ QDashedStrokeProcessor dasher;
+ QTransform temporaryTransform;
+
QScopedPointer<QPixmapFilter> convolutionFilter;
QScopedPointer<QPixmapFilter> colorizeFilter;
QScopedPointer<QPixmapFilter> blurFilter;
+ QScopedPointer<QPixmapFilter> animationBlurFilter;
QScopedPointer<QPixmapFilter> fastBlurFilter;
QScopedPointer<QPixmapFilter> dropShadowFilter;
QScopedPointer<QPixmapFilter> fastDropShadowFilter;
+
+ QSet<QVectorPath::CacheEntry *> pathCaches;
+ QVector<GLuint> unusedVBOSToClean;
};
QT_END_NAMESPACE
diff --git a/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp b/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp
new file mode 100644
index 0000000..6082f49
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp
@@ -0,0 +1,549 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtriangulatingstroker_p.h"
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+#define CURVE_FLATNESS Q_PI / 8
+
+
+
+
+void QTriangulatingStroker::endCapOrJoinClosed(const qreal *start, const qreal *cur,
+ bool implicitClose, bool endsAtStart)
+{
+ if (endsAtStart) {
+ join(start + 2);
+ } else if (implicitClose) {
+ join(start);
+ lineTo(start);
+ join(start+2);
+ } else {
+ endCap(cur);
+ }
+ int count = m_vertices.size();
+ m_vertices.add(m_vertices.at(count-2));
+ m_vertices.add(m_vertices.at(count-1));
+}
+
+
+void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen)
+{
+ const qreal *pts = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+ int count = path.elementCount();
+ if (count < 2)
+ return;
+
+ float realWidth = qpen_widthf(pen);
+ if (realWidth == 0)
+ realWidth = 1;
+
+ m_width = realWidth / 2;
+
+ bool cosmetic = pen.isCosmetic();
+ if (cosmetic) {
+ m_width = m_width * m_inv_scale;
+ }
+
+ m_join_style = qpen_joinStyle(pen);
+ m_cap_style = qpen_capStyle(pen);
+ m_vertices.reset();
+ m_miter_limit = pen.miterLimit() * qpen_widthf(pen);
+
+ // The curvyness is based on the notion that I originally wanted
+ // roughly one line segment pr 4 pixels. This may seem little, but
+ // because we sample at constantly incrementing B(t) E [0<t<1], we
+ // will get longer segments where the curvature is small and smaller
+ // segments when the curvature is high.
+ //
+ // To get a rough idea of the length of each curve, I pretend that
+ // the curve is a 90 degree arc, whose radius is
+ // qMax(curveBounds.width, curveBounds.height). Based on this
+ // logic we can estimate the length of the outline edges based on
+ // the radius + a pen width and adjusting for scale factors
+ // depending on if the pen is cosmetic or not.
+ //
+ // The curvyness value of PI/14 was based on,
+ // arcLength=2*PI*r/4=PI/2 and splitting length into somewhere
+ // between 3 and 8 where 5 seemed to be give pretty good results
+ // hence: Q_PI/14. Lower divisors will give more detail at the
+ // direct cost of performance.
+
+ // simplfy pens that are thin in device size (2px wide or less)
+ if (realWidth < 2.5 && (cosmetic || m_inv_scale == 1)) {
+ if (m_cap_style == Qt::RoundCap)
+ m_cap_style = Qt::SquareCap;
+ if (m_join_style == Qt::RoundJoin)
+ m_join_style = Qt::MiterJoin;
+ m_curvyness_add = 0.5;
+ m_curvyness_mul = CURVE_FLATNESS / m_inv_scale;
+ m_roundness = 1;
+ } else if (cosmetic) {
+ m_curvyness_add = realWidth / 2;
+ m_curvyness_mul = CURVE_FLATNESS;
+ m_roundness = qMax<int>(4, realWidth * CURVE_FLATNESS);
+ } else {
+ m_curvyness_add = m_width;
+ m_curvyness_mul = CURVE_FLATNESS / m_inv_scale;
+ m_roundness = qMax<int>(4, realWidth * m_curvyness_mul);
+ }
+
+ // Over this level of segmentation, there doesn't seem to be any
+ // benefit, even for huge penWidth
+ if (m_roundness > 24)
+ m_roundness = 24;
+
+ m_sin_theta = qFastSin(Q_PI / m_roundness);
+ m_cos_theta = qFastCos(Q_PI / m_roundness);
+
+ const qreal *endPts = pts + (count<<1);
+ const qreal *startPts;
+
+ Qt::PenCapStyle cap = m_cap_style;
+
+ if (!types) {
+ startPts = pts;
+
+ bool endsAtStart = startPts[0] == *(endPts-2) && startPts[1] == *(endPts-1);
+
+ if (endsAtStart || path.hasImplicitClose())
+ m_cap_style = Qt::FlatCap;
+ moveTo(pts);
+ m_cap_style = cap;
+ pts += 2;
+ lineTo(pts);
+ pts += 2;
+ while (pts < endPts) {
+ join(pts);
+ lineTo(pts);
+ pts += 2;
+ }
+
+ endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
+
+ } else {
+ bool endsAtStart;
+ while (pts < endPts) {
+ switch (*types) {
+ case QPainterPath::MoveToElement: {
+ if (pts != path.points())
+ endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
+
+ startPts = pts;
+ int end = (endPts - pts) / 2;
+ int i = 2; // Start looking to ahead since we never have two moveto's in a row
+ while (i<end && types[i] != QPainterPath::MoveToElement) {
+ ++i;
+ }
+ endsAtStart = startPts[0] == pts[i*2 - 2] && startPts[1] == pts[i*2 - 1];
+ if (endsAtStart || path.hasImplicitClose())
+ m_cap_style = Qt::FlatCap;
+
+ moveTo(pts);
+ m_cap_style = cap;
+ pts+=2;
+ ++types;
+ break; }
+ case QPainterPath::LineToElement:
+ if (*(types - 1) != QPainterPath::MoveToElement)
+ join(pts);
+ lineTo(pts);
+ pts+=2;
+ ++types;
+ break;
+ case QPainterPath::CurveToElement:
+ if (*(types - 1) != QPainterPath::MoveToElement)
+ join(pts);
+ cubicTo(pts);
+ pts+=6;
+ types+=3;
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+ }
+
+ endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
+ }
+}
+
+void QTriangulatingStroker::moveTo(const qreal *pts)
+{
+ m_cx = pts[0];
+ m_cy = pts[1];
+
+ float x2 = pts[2];
+ float y2 = pts[3];
+ normalVector(m_cx, m_cy, x2, y2, &m_nvx, &m_nvy);
+
+
+ // To acheive jumps we insert zero-area tringles. This is done by
+ // adding two identical points in both the end of previous strip
+ // and beginning of next strip
+ bool invisibleJump = m_vertices.size();
+
+ switch (m_cap_style) {
+ case Qt::FlatCap:
+ if (invisibleJump) {
+ m_vertices.add(m_cx + m_nvx);
+ m_vertices.add(m_cy + m_nvy);
+ }
+ break;
+ case Qt::SquareCap: {
+ float sx = m_cx - m_nvy;
+ float sy = m_cy + m_nvx;
+ if (invisibleJump) {
+ m_vertices.add(sx + m_nvx);
+ m_vertices.add(sy + m_nvy);
+ }
+ emitLineSegment(sx, sy, m_nvx, m_nvy);
+ break; }
+ case Qt::RoundCap: {
+ QVarLengthArray<float> points;
+ arcPoints(m_cx, m_cy, m_cx + m_nvx, m_cy + m_nvy, m_cx - m_nvx, m_cy - m_nvy, points);
+ m_vertices.resize(m_vertices.size() + points.size() + 2 * int(invisibleJump));
+ int count = m_vertices.size();
+ int front = 0;
+ int end = points.size() / 2;
+ while (front != end) {
+ m_vertices.at(--count) = points[2 * end - 1];
+ m_vertices.at(--count) = points[2 * end - 2];
+ --end;
+ if (front == end)
+ break;
+ m_vertices.at(--count) = points[2 * front + 1];
+ m_vertices.at(--count) = points[2 * front + 0];
+ ++front;
+ }
+
+ if (invisibleJump) {
+ m_vertices.at(count - 1) = m_vertices.at(count + 1);
+ m_vertices.at(count - 2) = m_vertices.at(count + 0);
+ }
+ break; }
+ default: break; // ssssh gcc...
+ }
+ emitLineSegment(m_cx, m_cy, m_nvx, m_nvy);
+}
+
+void QTriangulatingStroker::cubicTo(const qreal *pts)
+{
+ const QPointF *p = (const QPointF *) pts;
+ QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]);
+
+ QRectF bounds = bezier.bounds();
+ float rad = qMax(bounds.width(), bounds.height());
+ int threshold = qMin<float>(64, (rad + m_curvyness_add) * m_curvyness_mul);
+ if (threshold < 4)
+ threshold = 4;
+ qreal threshold_minus_1 = threshold - 1;
+ float vx, vy;
+
+ float cx = m_cx, cy = m_cy;
+ float x, y;
+
+ for (int i=1; i<threshold; ++i) {
+ qreal t = qreal(i) / threshold_minus_1;
+ QPointF p = bezier.pointAt(t);
+ x = p.x();
+ y = p.y();
+
+ normalVector(cx, cy, x, y, &vx, &vy);
+
+ emitLineSegment(x, y, vx, vy);
+
+ cx = x;
+ cy = y;
+ }
+
+ m_cx = cx;
+ m_cy = cy;
+
+ m_nvx = vx;
+ m_nvy = vy;
+}
+
+void QTriangulatingStroker::join(const qreal *pts)
+{
+ // Creates a join to the next segment (m_cx, m_cy) -> (pts[0], pts[1])
+ normalVector(m_cx, m_cy, pts[0], pts[1], &m_nvx, &m_nvy);
+
+ switch (m_join_style) {
+ case Qt::BevelJoin:
+ break;
+ case Qt::SvgMiterJoin:
+ case Qt::MiterJoin: {
+ // Find out on which side the join should be.
+ int count = m_vertices.size();
+ float prevNvx = m_vertices.at(count - 2) - m_cx;
+ float prevNvy = m_vertices.at(count - 1) - m_cy;
+ float xprod = prevNvx * m_nvy - prevNvy * m_nvx;
+ float px, py, qx, qy;
+
+ // If the segments are parallel, use bevel join.
+ if (qFuzzyIsNull(xprod))
+ break;
+
+ // Find the corners of the previous and next segment to join.
+ if (xprod < 0) {
+ px = m_vertices.at(count - 2);
+ py = m_vertices.at(count - 1);
+ qx = m_cx - m_nvx;
+ qy = m_cy - m_nvy;
+ } else {
+ px = m_vertices.at(count - 4);
+ py = m_vertices.at(count - 3);
+ qx = m_cx + m_nvx;
+ qy = m_cy + m_nvy;
+ }
+
+ // Find intersection point.
+ float pu = px * prevNvx + py * prevNvy;
+ float qv = qx * m_nvx + qy * m_nvy;
+ float ix = (m_nvy * pu - prevNvy * qv) / xprod;
+ float iy = (prevNvx * qv - m_nvx * pu) / xprod;
+
+ // Check that the distance to the intersection point is less than the miter limit.
+ if ((ix - px) * (ix - px) + (iy - py) * (iy - py) <= m_miter_limit * m_miter_limit) {
+ m_vertices.add(ix);
+ m_vertices.add(iy);
+ m_vertices.add(ix);
+ m_vertices.add(iy);
+ }
+ // else
+ // Do a plain bevel join if the miter limit is exceeded or if
+ // the lines are parallel. This is not what the raster
+ // engine's stroker does, but it is both faster and similar to
+ // what some other graphics API's do.
+
+ break; }
+ case Qt::RoundJoin: {
+ QVarLengthArray<float> points;
+ int count = m_vertices.size();
+ float prevNvx = m_vertices.at(count - 2) - m_cx;
+ float prevNvy = m_vertices.at(count - 1) - m_cy;
+ if (m_nvx * prevNvy - m_nvy * prevNvx < 0) {
+ arcPoints(0, 0, m_nvx, m_nvy, -prevNvx, -prevNvy, points);
+ for (int i = points.size() / 2; i > 0; --i)
+ emitLineSegment(m_cx, m_cy, points[2 * i - 2], points[2 * i - 1]);
+ } else {
+ arcPoints(0, 0, -prevNvx, -prevNvy, m_nvx, m_nvy, points);
+ for (int i = 0; i < points.size() / 2; ++i)
+ emitLineSegment(m_cx, m_cy, points[2 * i + 0], points[2 * i + 1]);
+ }
+ break; }
+ default: break; // gcc warn--
+ }
+
+ emitLineSegment(m_cx, m_cy, m_nvx, m_nvy);
+}
+
+void QTriangulatingStroker::endCap(const qreal *)
+{
+ switch (m_cap_style) {
+ case Qt::FlatCap:
+ break;
+ case Qt::SquareCap:
+ emitLineSegment(m_cx + m_nvy, m_cy - m_nvx, m_nvx, m_nvy);
+ break;
+ case Qt::RoundCap: {
+ QVarLengthArray<float> points;
+ int count = m_vertices.size();
+ arcPoints(m_cx, m_cy, m_vertices.at(count - 2), m_vertices.at(count - 1), m_vertices.at(count - 4), m_vertices.at(count - 3), points);
+ int front = 0;
+ int end = points.size() / 2;
+ while (front != end) {
+ m_vertices.add(points[2 * end - 2]);
+ m_vertices.add(points[2 * end - 1]);
+ --end;
+ if (front == end)
+ break;
+ m_vertices.add(points[2 * front + 0]);
+ m_vertices.add(points[2 * front + 1]);
+ ++front;
+ }
+ break; }
+ default: break; // to shut gcc up...
+ }
+}
+
+void QTriangulatingStroker::arcPoints(float cx, float cy, float fromX, float fromY, float toX, float toY, QVarLengthArray<float> &points)
+{
+ float dx1 = fromX - cx;
+ float dy1 = fromY - cy;
+ float dx2 = toX - cx;
+ float dy2 = toY - cy;
+
+ // while more than 180 degrees left:
+ while (dx1 * dy2 - dx2 * dy1 < 0) {
+ float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
+ float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
+ dx1 = tmpx;
+ dy1 = tmpy;
+ points.append(cx + dx1);
+ points.append(cy + dy1);
+ }
+
+ // while more than 90 degrees left:
+ while (dx1 * dx2 + dy1 * dy2 < 0) {
+ float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
+ float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
+ dx1 = tmpx;
+ dy1 = tmpy;
+ points.append(cx + dx1);
+ points.append(cy + dy1);
+ }
+
+ // while more than 0 degrees left:
+ while (dx1 * dy2 - dx2 * dy1 > 0) {
+ float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
+ float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
+ dx1 = tmpx;
+ dy1 = tmpy;
+ points.append(cx + dx1);
+ points.append(cy + dy1);
+ }
+
+ // remove last point which was rotated beyond [toX, toY].
+ if (!points.isEmpty())
+ points.resize(points.size() - 2);
+}
+
+static void qdashprocessor_moveTo(qreal x, qreal y, void *data)
+{
+ ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y);
+}
+
+static void qdashprocessor_lineTo(qreal x, qreal y, void *data)
+{
+ ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::LineToElement, x, y);
+}
+
+static void qdashprocessor_cubicTo(qreal, qreal, qreal, qreal, qreal, qreal, void *)
+{
+ Q_ASSERT(0); // The dasher should not produce curves...
+}
+
+QDashedStrokeProcessor::QDashedStrokeProcessor()
+ : m_dash_stroker(0), m_inv_scale(1)
+{
+ m_dash_stroker.setMoveToHook(qdashprocessor_moveTo);
+ m_dash_stroker.setLineToHook(qdashprocessor_lineTo);
+ m_dash_stroker.setCubicToHook(qdashprocessor_cubicTo);
+}
+
+void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen)
+{
+
+ const qreal *pts = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+ int count = path.elementCount();
+
+ m_points.reset();
+ m_types.reset();
+
+ qreal width = qpen_widthf(pen);
+ if (width == 0)
+ width = 1;
+
+ m_dash_stroker.setDashPattern(pen.dashPattern());
+ m_dash_stroker.setStrokeWidth(pen.isCosmetic() ? width * m_inv_scale : width);
+ m_dash_stroker.setMiterLimit(pen.miterLimit());
+ qreal curvyness = sqrt(width) * m_inv_scale / 8;
+
+ if (count < 2)
+ return;
+
+ const qreal *endPts = pts + (count<<1);
+
+ m_dash_stroker.begin(this);
+
+ if (!types) {
+ m_dash_stroker.moveTo(pts[0], pts[1]);
+ pts += 2;
+ while (pts < endPts) {
+ m_dash_stroker.lineTo(pts[0], pts[1]);
+ pts += 2;
+ }
+ } else {
+ while (pts < endPts) {
+ switch (*types) {
+ case QPainterPath::MoveToElement:
+ m_dash_stroker.moveTo(pts[0], pts[1]);
+ pts += 2;
+ ++types;
+ break;
+ case QPainterPath::LineToElement:
+ m_dash_stroker.lineTo(pts[0], pts[1]);
+ pts += 2;
+ ++types;
+ break;
+ case QPainterPath::CurveToElement: {
+ QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1),
+ *(((const QPointF *) pts)),
+ *(((const QPointF *) pts) + 1),
+ *(((const QPointF *) pts) + 2));
+ QRectF bounds = b.bounds();
+ int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * curvyness);
+ if (threshold < 4)
+ threshold = 4;
+ qreal threshold_minus_1 = threshold - 1;
+ for (int i=0; i<threshold; ++i) {
+ QPointF pt = b.pointAt(i / threshold_minus_1);
+ m_dash_stroker.lineTo(pt.x(), pt.y());
+ }
+ pts += 6;
+ types += 3;
+ break; }
+ default: break;
+ }
+ }
+ }
+
+ m_dash_stroker.end();
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h b/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h
new file mode 100644
index 0000000..2dba0ce
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTRIANGULATINGSTROKER_P_H
+#define QTRIANGULATINGSTROKER_P_H
+
+#include <private/qdatabuffer_p.h>
+#include <qvarlengtharray.h>
+#include <private/qvectorpath_p.h>
+#include <private/qbezier_p.h>
+#include <private/qnumeric_p.h>
+#include <private/qmath_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTriangulatingStroker
+{
+public:
+ void process(const QVectorPath &path, const QPen &pen);
+
+ inline int vertexCount() const { return m_vertices.size(); }
+ inline const float *vertices() const { return m_vertices.data(); }
+
+ inline void setInvScale(qreal invScale) { m_inv_scale = invScale; }
+
+private:
+ inline void emitLineSegment(float x, float y, float nx, float ny);
+ void moveTo(const qreal *pts);
+ inline void lineTo(const qreal *pts);
+ void cubicTo(const qreal *pts);
+ void join(const qreal *pts);
+ inline void normalVector(float x1, float y1, float x2, float y2, float *nx, float *ny);
+ void endCap(const qreal *pts);
+ void arcPoints(float cx, float cy, float fromX, float fromY, float toX, float toY, QVarLengthArray<float> &points);
+ void endCapOrJoinClosed(const qreal *start, const qreal *cur, bool implicitClose, bool endsAtStart);
+
+
+ QDataBuffer<float> m_vertices;
+
+ float m_cx, m_cy; // current points
+ float m_nvx, m_nvy; // normal vector...
+ float m_width;
+ qreal m_miter_limit;
+
+ int m_roundness; // Number of line segments in a round join
+ qreal m_sin_theta; // sin(m_roundness / 360);
+ qreal m_cos_theta; // cos(m_roundness / 360);
+ qreal m_inv_scale;
+ float m_curvyness_mul;
+ float m_curvyness_add;
+
+ Qt::PenJoinStyle m_join_style;
+ Qt::PenCapStyle m_cap_style;
+};
+
+class QDashedStrokeProcessor
+{
+public:
+ QDashedStrokeProcessor();
+
+ void process(const QVectorPath &path, const QPen &pen);
+
+ inline void addElement(QPainterPath::ElementType type, qreal x, qreal y) {
+ m_points.add(x);
+ m_points.add(y);
+ m_types.add(type);
+ }
+
+ inline int elementCount() const { return m_types.size(); }
+ inline qreal *points() const { return m_points.data(); }
+ inline QPainterPath::ElementType *elementTypes() const { return m_types.data(); }
+
+ inline void setInvScale(qreal invScale) { m_inv_scale = invScale; }
+
+private:
+ QDataBuffer<qreal> m_points;
+ QDataBuffer<QPainterPath::ElementType> m_types;
+ QDashStroker m_dash_stroker;
+ qreal m_inv_scale;
+};
+
+inline void QTriangulatingStroker::normalVector(float x1, float y1, float x2, float y2,
+ float *nx, float *ny)
+{
+ float dx = x2 - x1;
+ float dy = y2 - y1;
+
+ float pw;
+
+ if (dx == 0)
+ pw = m_width / qAbs(dy);
+ else if (dy == 0)
+ pw = m_width / qAbs(dx);
+ else
+ pw = m_width / sqrt(dx*dx + dy*dy);
+
+ *nx = -dy * pw;
+ *ny = dx * pw;
+}
+
+inline void QTriangulatingStroker::emitLineSegment(float x, float y, float vx, float vy)
+{
+ m_vertices.add(x + vx);
+ m_vertices.add(y + vy);
+ m_vertices.add(x - vx);
+ m_vertices.add(y - vy);
+}
+
+void QTriangulatingStroker::lineTo(const qreal *pts)
+{
+ emitLineSegment(pts[0], pts[1], m_nvx, m_nvy);
+ m_cx = pts[0];
+ m_cy = pts[1];
+}
+
+QT_END_NAMESPACE
+
+#endif