summaryrefslogtreecommitdiffstats
path: root/src/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'src/opengl')
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager.cpp2
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadersource_p.h16
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp12
-rw-r--r--src/opengl/gl2paintengineex/qtriangulatingstroker.cpp4
-rw-r--r--src/opengl/gl2paintengineex/qtriangulatingstroker_p.h11
-rw-r--r--src/opengl/qgl.cpp5
-rw-r--r--src/opengl/qgl_x11egl.cpp1
-rw-r--r--src/opengl/qglpixmapfilter.cpp426
-rw-r--r--src/opengl/qpixmapdata_gl.cpp17
9 files changed, 301 insertions, 193 deletions
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
index e22303d..af9306f 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
@@ -476,7 +476,7 @@ 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");
}
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 a0810bc..a9744b3 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -535,7 +535,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);
@@ -550,7 +550,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);
@@ -858,9 +862,7 @@ 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)
- {
+ } else if (path.isConvex()) {
vertexCoordinateArray.clear();
vertexCoordinateArray.addPath(path, inverseScale, false);
prepareForDraw(currentBrush->isOpaque());
diff --git a/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp b/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp
index ec05020..ad18a51 100644
--- a/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp
+++ b/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp
@@ -131,8 +131,8 @@ void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen)
if (m_roundness > 24)
m_roundness = 24;
- m_sin_theta = qSin(Q_PI / m_roundness); // ### Use qFastSin
- m_cos_theta = qCos(Q_PI / m_roundness);
+ 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;
diff --git a/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h b/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h
index 483cea9..97eabef 100644
--- a/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h
+++ b/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h
@@ -125,7 +125,16 @@ inline void QTriangulatingStroker::normalVector(float x1, float y1, float x2, fl
{
float dx = x2 - x1;
float dy = y2 - y1;
- float pw = m_width / sqrt(dx*dx + dy*dy);
+
+ float pw;
+
+ if (dx == 0)
+ pw = m_width / dy;
+ else if (dy == 0)
+ pw = m_width / dx;
+ else
+ pw = m_width / sqrt(dx*dx + dy*dy);
+
*nx = -dy * pw;
*ny = dx * pw;
}
diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp
index 6720ae7..e80521b 100644
--- a/src/opengl/qgl.cpp
+++ b/src/opengl/qgl.cpp
@@ -2156,6 +2156,11 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
time.start();
#endif
+#ifndef QT_NO_DEBUG
+ // Reset the gl error stack...git
+ while (glGetError() != GL_NO_ERROR);
+#endif
+
// Scale the pixmap if needed. GL textures needs to have the
// dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL
// 2.0 or use the GL_TEXTURE_RECTANGLE texture target
diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp
index 3894ed1..7180682 100644
--- a/src/opengl/qgl_x11egl.cpp
+++ b/src/opengl/qgl_x11egl.cpp
@@ -589,7 +589,6 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, cons
GLuint textureId;
glGenTextures(1, &textureId);
- glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);
// bind the egl pixmap surface to a texture
diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp
index 0603369..2af69e0 100644
--- a/src/opengl/qglpixmapfilter.cpp
+++ b/src/opengl/qglpixmapfilter.cpp
@@ -104,7 +104,7 @@ public:
void setUniforms(QGLShaderProgram *program);
- static QByteArray generateGaussianShader(int radius, bool dropShadow = false);
+ static QByteArray generateGaussianShader(int radius, bool singlePass = false, bool dropShadow = false);
protected:
bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
@@ -113,6 +113,7 @@ private:
mutable QSize m_textureSize;
mutable bool m_horizontalBlur;
+ mutable bool m_singlePass;
mutable bool m_haveCached;
mutable int m_cachedRadius;
@@ -132,6 +133,7 @@ protected:
private:
mutable QSize m_textureSize;
mutable bool m_horizontalBlur;
+ mutable bool m_singlePass;
mutable bool m_haveCached;
mutable int m_cachedRadius;
@@ -298,123 +300,147 @@ bool QGLPixmapConvolutionFilter::processGL(QPainter *painter, const QPointF &pos
return true;
}
-static const char *qt_gl_blur_filter_fast =
- "const int samples = 9;"
- "uniform mediump vec2 delta;"
- "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {"
- " mediump vec4 color = vec4(0.0, 0.0, 0.0, 0.0);"
- " mediump float offset = (float(samples) - 1.0) / 2.0;"
- " for (int i = 0; i < samples; i++) {"
- " mediump vec2 coord = srcCoords + delta * (offset - float(i)) / offset;"
- " color += texture2D(src, coord);"
- " }"
- " return color * (1.0 / float(samples));"
- "}";
-
-static const char *qt_gl_drop_shadow_filter_fast =
- "const int samples = 9;"
- "uniform mediump vec2 delta;"
- "uniform mediump vec4 shadowColor;"
- "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {"
- " mediump vec4 color = vec4(0.0, 0.0, 0.0, 0.0);"
- " mediump float offset = (float(samples) - 1.0) / 2.0;"
- " for (int i = 0; i < samples; i++) {"
- " mediump vec2 coord = srcCoords + delta * (offset - float(i)) / offset;"
- " color += texture2D(src, coord).a * shadowColor;"
- " }"
- " return color * (1.0 / float(samples));"
- "}";
+static const char *qt_gl_texture_sampling_helper =
+ "lowp float texture2DAlpha(lowp sampler2D src, highp vec2 srcCoords) {\n"
+ " return texture2D(src, srcCoords).a;\n"
+ "}\n";
+
+static const char *qt_gl_clamped_texture_sampling_helper =
+ "highp vec4 texture_dimensions;\n" // x = width, y = height, z = 0.5/width, w = 0.5/height
+ "lowp float clampedTexture2DAlpha(lowp sampler2D src, highp vec2 srcCoords) {\n"
+ " highp vec2 clampedCoords = clamp(srcCoords, texture_dimensions.zw, vec2(1.0) - texture_dimensions.zw);\n"
+ " highp vec2 t = clamp(min(srcCoords, vec2(1.0) - srcCoords) * srcDim + 0.5, 0.0, 1.0);\n"
+ " return texture2D(src, clampedCoords).a * t.x * t.y;\n"
+ "}\n"
+ "lowp vec4 clampedTexture2D(lowp sampler2D src, highp vec2 srcCoords) {\n"
+ " highp vec2 clampedCoords = clamp(srcCoords, texture_dimensions.zw, vec2(1.0) - texture_dimensions.zw);\n"
+ " highp vec2 t = clamp(min(srcCoords, vec2(1.0) - srcCoords) * srcDim + 0.5, 0.0, 1.0);\n"
+ " return texture2D(src, clampedCoords) * t.x * t.y;\n"
+ "}\n";
+
+static QByteArray qt_gl_convertToClamped(const QByteArray &source)
+{
+ QByteArray result;
+ result.append(qt_gl_clamped_texture_sampling_helper);
+ result.append(QByteArray(source).replace("texture2DAlpha", "clampedTexture2DAlpha")
+ .replace("texture2D", "clampedTexture2D"));
+ return result;
+}
QGLPixmapBlurFilter::QGLPixmapBlurFilter(Qt::RenderHint hint)
: m_haveCached(false)
- , m_cachedRadius(5)
+ , m_cachedRadius(0)
, m_hint(hint)
{
- if (hint == Qt::PerformanceHint) {
- QGLPixmapBlurFilter *filter = const_cast<QGLPixmapBlurFilter *>(this);
- filter->setSource(qt_gl_blur_filter_fast);
- m_haveCached = true;
- }
}
bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const
{
QGLPixmapBlurFilter *filter = const_cast<QGLPixmapBlurFilter *>(this);
- int radius = this->radius();
- if (!m_haveCached || (m_hint == Qt::QualityHint && radius != m_cachedRadius)) {
- // Only regenerate the shader from source if parameters have changed.
- m_haveCached = true;
- m_cachedRadius = radius;
- filter->setSource(generateGaussianShader(radius));
+ int actualRadius = qRound(radius());
+ int filterRadius = actualRadius;
+ int fastRadii[] = { 1, 2, 3, 5, 8, 15, 25 };
+ if (m_hint == Qt::PerformanceHint) {
+ uint i = 0;
+ for (; i < (sizeof(fastRadii)/sizeof(*fastRadii))-1; ++i) {
+ if (fastRadii[i+1] > filterRadius)
+ break;
+ }
+ filterRadius = fastRadii[i];
}
- QGLFramebufferObjectFormat format;
- format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB));
- QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(src.size(), format);
+ m_singlePass = filterRadius <= 3;
- if (!fbo)
- return false;
-
- glBindTexture(GL_TEXTURE_2D, fbo->texture());
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glBindTexture(GL_TEXTURE_2D, 0);
+ if (!m_haveCached || filterRadius != m_cachedRadius) {
+ // Only regenerate the shader from source if parameters have changed.
+ m_haveCached = true;
+ m_cachedRadius = filterRadius;
+ QByteArray source = generateGaussianShader(filterRadius, m_singlePass);
+ filter->setSource(source);
+ }
- // prepare for updateUniforms
- m_textureSize = src.size();
+ QRect targetRect = QRectF(src.rect()).translated(pos).adjusted(-actualRadius, -actualRadius, actualRadius, actualRadius).toAlignedRect();
- // horizontal pass, to pixmap
- m_horizontalBlur = true;
+ if (m_singlePass) {
+ // prepare for updateUniforms
+ m_textureSize = src.size();
- QPainter fboPainter(fbo);
+ // ensure GL_LINEAR filtering is used
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(painter);
+ QBrush pixmapBrush = src;
+ pixmapBrush.setTransform(QTransform::fromTranslate(pos.x(), pos.y()));
+ painter->fillRect(targetRect, pixmapBrush);
+ filter->removeFromPainter(painter);
+ } else {
+ QGLFramebufferObjectFormat format;
+ format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB));
+ QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(targetRect.size(), format);
- if (src.hasAlphaChannel()) {
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT);
- }
+ if (!fbo)
+ return false;
- // ensure GL_LINEAR filtering is used
- fboPainter.setRenderHint(QPainter::SmoothPixmapTransform);
- filter->setOnPainter(&fboPainter);
- fboPainter.drawPixmap(0, 0, src);
- filter->removeFromPainter(&fboPainter);
- fboPainter.end();
+ // prepare for updateUniforms
+ m_textureSize = src.size();
- QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
+ // horizontal pass, to pixmap
+ m_horizontalBlur = true;
- // vertical pass, to painter
- m_horizontalBlur = false;
+ QPainter fboPainter(fbo);
- painter->save();
- // ensure GL_LINEAR filtering is used
- painter->setRenderHint(QPainter::SmoothPixmapTransform);
- filter->setOnPainter(painter);
- engine->drawTexture(src.rect().translated(pos.x(), pos.y()), fbo->texture(), fbo->size(), src.rect().translated(0, fbo->height() - src.height()));
- filter->removeFromPainter(painter);
- painter->restore();
+ if (src.hasAlphaChannel()) {
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
- qgl_fbo_pool()->release(fbo);
+ // ensure GL_LINEAR filtering is used
+ fboPainter.setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(&fboPainter);
+ QBrush pixmapBrush = src;
+ pixmapBrush.setTransform(QTransform::fromTranslate(actualRadius, actualRadius));
+ fboPainter.fillRect(QRect(0, 0, targetRect.width(), targetRect.height()), pixmapBrush);
+ filter->removeFromPainter(&fboPainter);
+ fboPainter.end();
+
+ QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
+
+ // vertical pass, to painter
+ m_horizontalBlur = false;
+ m_textureSize = fbo->size();
+
+ painter->save();
+ // ensure GL_LINEAR filtering is used
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(painter);
+ engine->drawTexture(targetRect, fbo->texture(), fbo->size(), QRect(QPoint(), targetRect.size()).translated(0, fbo->height() - targetRect.height()));
+ filter->removeFromPainter(painter);
+ painter->restore();
+
+ qgl_fbo_pool()->release(fbo);
+ }
return true;
}
void QGLPixmapBlurFilter::setUniforms(QGLShaderProgram *program)
{
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
if (m_hint == Qt::QualityHint) {
- if (m_horizontalBlur)
+ if (m_singlePass)
+ program->setUniformValue("delta", 1.0 / m_textureSize.width(), 1.0 / m_textureSize.height());
+ else if (m_horizontalBlur)
program->setUniformValue("delta", 1.0 / m_textureSize.width(), 0.0);
else
program->setUniformValue("delta", 0.0, 1.0 / m_textureSize.height());
} else {
- // 1.4 is chosen to most closely match the blurriness of the gaussian blur
- // at low radii
- qreal blur = radius() / 1.4f;
+ qreal blur = radius() / qreal(m_cachedRadius);
- if (m_horizontalBlur)
+ if (m_singlePass)
+ program->setUniformValue("delta", blur / m_textureSize.width(), blur / m_textureSize.height());
+ else if (m_horizontalBlur)
program->setUniformValue("delta", blur / m_textureSize.width(), 0.0);
else
program->setUniformValue("delta", 0.0, blur / m_textureSize.height());
@@ -426,12 +452,21 @@ static inline qreal gaussian(qreal dx, qreal sigma)
return exp(-dx * dx / (2 * sigma * sigma)) / (Q_2PI * sigma * sigma);
}
-QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool dropShadow)
+QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool singlePass, bool dropShadow)
{
Q_ASSERT(radius >= 1);
+ radius = qMin(127, radius);
+
+ static QCache<uint, QByteArray> shaderSourceCache;
+ uint key = radius | (int(singlePass) << 7) | (int(dropShadow) << 8);
+ QByteArray *cached = shaderSourceCache.object(key);
+ if (cached)
+ return *cached;
+
QByteArray source;
source.reserve(1000);
+ source.append(qt_gl_texture_sampling_helper);
source.append("uniform highp vec2 delta;\n");
if (dropShadow)
@@ -446,7 +481,7 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool dropShad
qreal sigma = radius / 1.65;
qreal sum = 0;
- for (int i = -radius; i <= radius; ++i) {
+ for (int i = -radius; i < radius; ++i) {
float value = gaussian(i, sigma);
gaussianComponents << value;
sum += value;
@@ -464,43 +499,67 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool dropShad
weights << weight;
}
- // odd size ?
- if (gaussianComponents.size() & 1) {
- sampleOffsets << radius;
- weights << gaussianComponents.last();
- }
-
- int currentVariable = 1;
- source.append(" mediump vec4 sample = vec4(0.0);\n");
- source.append(" mediump vec2 coord;\n");
-
- qreal weightSum = 0;
- source.append(" mediump float c;\n");
- for (int i = 0; i < sampleOffsets.size(); ++i) {
- qreal delta = sampleOffsets.at(i);
-
- ++currentVariable;
+ int limit = sampleOffsets.size();
+ if (singlePass)
+ limit *= limit;
+
+ QByteArray baseCoordinate = "srcCoords";
+
+ for (int i = 0; i < limit; ++i) {
+ QByteArray coordinate = baseCoordinate;
+
+ qreal weight;
+ if (singlePass) {
+ const int xIndex = i % sampleOffsets.size();
+ const int yIndex = i / sampleOffsets.size();
+
+ const qreal deltaX = sampleOffsets.at(xIndex);
+ const qreal deltaY = sampleOffsets.at(yIndex);
+ weight = weights.at(xIndex) * weights.at(yIndex);
+
+ if (!qFuzzyCompare(deltaX, deltaY)) {
+ coordinate.append(" + vec2(delta.x * float(");
+ coordinate.append(QByteArray::number(deltaX));
+ coordinate.append("), delta.y * float(");
+ coordinate.append(QByteArray::number(deltaY));
+ coordinate.append("))");
+ } else if (!qFuzzyIsNull(deltaX)) {
+ coordinate.append(" + delta * float(");
+ coordinate.append(QByteArray::number(deltaX));
+ coordinate.append(")");
+ }
+ } else {
+ const qreal delta = sampleOffsets.at(i);
+ weight = weights.at(i);
+ if (!qFuzzyIsNull(delta)) {
+ coordinate.append(" + delta * float(");
+ coordinate.append(QByteArray::number(delta));
+ coordinate.append(")");
+ }
+ }
- QByteArray coordinate = "srcCoords";
- if (delta != qreal(0)) {
- coordinate.append(" + delta * float(");
- coordinate.append(QByteArray::number(delta));
- coordinate.append(")");
+ if (i == 0) {
+ if (dropShadow)
+ source.append(" mediump float sample = ");
+ else
+ source.append(" mediump vec4 sample = ");
+ } else {
+ if (dropShadow)
+ source.append(" sample += ");
+ else
+ source.append(" sample += ");
}
- source.append(" coord = ");
+ source.append("texture2D(src, ");
source.append(coordinate);
- source.append(";\n");
+ source.append(")");
if (dropShadow)
- source.append(" sample += texture2D(src, coord).a * shadowColor");
- else
- source.append(" sample += texture2D(src, coord)");
+ source.append(".a");
- weightSum += weights.at(i);
- if (weights.at(i) != qreal(1)) {
+ if (!qFuzzyCompare(weight, qreal(1))) {
source.append(" * float(");
- source.append(QByteArray::number(weights.at(i)));
+ source.append(QByteArray::number(weight));
source.append(");\n");
} else {
source.append(";\n");
@@ -508,87 +567,102 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool dropShad
}
source.append(" return ");
+ if (dropShadow)
+ source.append("shadowColor * ");
source.append("sample;\n");
source.append("}\n");
+ cached = new QByteArray(source);
+ shaderSourceCache.insert(key, cached);
+
return source;
}
QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter(Qt::RenderHint hint)
: m_haveCached(false)
- , m_cachedRadius(5)
+ , m_cachedRadius(0)
, m_hint(hint)
{
- if (hint == Qt::PerformanceHint) {
- QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this);
- filter->setSource(qt_gl_drop_shadow_filter_fast);
- m_haveCached = true;
- }
}
bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const
{
QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this);
- int radius = this->blurRadius();
- if (!m_haveCached || (m_hint == Qt::QualityHint && radius != m_cachedRadius)) {
+ int actualRadius = qRound(blurRadius());
+ int filterRadius = actualRadius;
+ m_singlePass = filterRadius <= 3;
+
+ if (!m_haveCached || filterRadius != m_cachedRadius) {
// Only regenerate the shader from source if parameters have changed.
m_haveCached = true;
- m_cachedRadius = radius;
- filter->setSource(QGLPixmapBlurFilter::generateGaussianShader(radius, true));
+ m_cachedRadius = filterRadius;
+ QByteArray source = QGLPixmapBlurFilter::generateGaussianShader(filterRadius, m_singlePass, true);
+ filter->setSource(source);
}
- QGLFramebufferObjectFormat format;
- format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB));
- QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(src.size(), format);
+ QRect targetRect = QRectF(src.rect()).translated(pos + offset()).adjusted(-actualRadius, -actualRadius, actualRadius, actualRadius).toAlignedRect();
+
+ if (m_singlePass) {
+ // prepare for updateUniforms
+ m_textureSize = src.size();
+
+ painter->save();
+ // ensure GL_LINEAR filtering is used
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(painter);
+ QBrush pixmapBrush = src;
+ pixmapBrush.setTransform(QTransform::fromTranslate(pos.x() + offset().x(), pos.y() + offset().y()));
+ painter->fillRect(targetRect, pixmapBrush);
+ filter->removeFromPainter(painter);
+ painter->restore();
+ } else {
+ QGLFramebufferObjectFormat format;
+ format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB));
+ QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(targetRect.size(), format);
- if (!fbo)
- return false;
+ if (!fbo)
+ return false;
- glBindTexture(GL_TEXTURE_2D, fbo->texture());
+ // prepare for updateUniforms
+ m_textureSize = src.size();
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glBindTexture(GL_TEXTURE_2D, 0);
+ // horizontal pass, to pixmap
+ m_horizontalBlur = true;
- // prepare for updateUniforms
- m_textureSize = src.size();
+ QPainter fboPainter(fbo);
- // horizontal pass, to pixmap
- m_horizontalBlur = true;
-
- QPainter fboPainter(fbo);
+ if (src.hasAlphaChannel()) {
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
- if (src.hasAlphaChannel()) {
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT);
+ // ensure GL_LINEAR filtering is used
+ fboPainter.setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(&fboPainter);
+ QBrush pixmapBrush = src;
+ pixmapBrush.setTransform(QTransform::fromTranslate(actualRadius, actualRadius));
+ fboPainter.fillRect(QRect(0, 0, targetRect.width(), targetRect.height()), pixmapBrush);
+ filter->removeFromPainter(&fboPainter);
+ fboPainter.end();
+
+ QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
+
+ // vertical pass, to painter
+ m_horizontalBlur = false;
+ m_textureSize = fbo->size();
+
+ painter->save();
+ // ensure GL_LINEAR filtering is used
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(painter);
+ engine->drawTexture(targetRect, fbo->texture(), fbo->size(), src.rect().translated(0, fbo->height() - src.height()));
+ filter->removeFromPainter(painter);
+ painter->restore();
+
+ qgl_fbo_pool()->release(fbo);
}
- // ensure GL_LINEAR filtering is used
- fboPainter.setRenderHint(QPainter::SmoothPixmapTransform);
- filter->setOnPainter(&fboPainter);
- fboPainter.drawPixmap(0, 0, src);
- filter->removeFromPainter(&fboPainter);
- fboPainter.end();
-
- QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
-
- // vertical pass, to painter
- m_horizontalBlur = false;
-
- painter->save();
- // ensure GL_LINEAR filtering is used
- painter->setRenderHint(QPainter::SmoothPixmapTransform);
- filter->setOnPainter(painter);
- QPointF ofs = offset();
- engine->drawTexture(src.rect().translated(pos.x() + ofs.x(), pos.y() + ofs.y()), fbo->texture(), fbo->size(), src.rect().translated(0, fbo->height() - src.height()));
- filter->removeFromPainter(painter);
- painter->restore();
-
- qgl_fbo_pool()->release(fbo);
-
// Now draw the actual pixmap over the top.
painter->drawPixmap(pos, src, srcRect);
@@ -597,8 +671,11 @@ bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos,
void QGLPixmapDropShadowFilter::setUniforms(QGLShaderProgram *program)
{
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
QColor col = color();
- if (m_horizontalBlur) {
+ if (m_horizontalBlur && !m_singlePass) {
program->setUniformValue("shadowColor", 1.0f, 1.0f, 1.0f, 1.0f);
} else {
qreal alpha = col.alphaF();
@@ -607,17 +684,20 @@ void QGLPixmapDropShadowFilter::setUniforms(QGLShaderProgram *program)
col.blueF() * alpha,
alpha);
}
+
if (m_hint == Qt::QualityHint) {
- if (m_horizontalBlur)
+ if (m_singlePass)
+ program->setUniformValue("delta", 1.0 / m_textureSize.width(), 1.0 / m_textureSize.height());
+ else if (m_horizontalBlur)
program->setUniformValue("delta", 1.0 / m_textureSize.width(), 0.0);
else
program->setUniformValue("delta", 0.0, 1.0 / m_textureSize.height());
} else {
- // 1.4 is chosen to most closely match the blurriness of the gaussian blur
- // at low radii
- qreal blur = blurRadius() / 1.4f;
+ qreal blur = blurRadius() / qreal(m_cachedRadius);
- if (m_horizontalBlur)
+ if (m_singlePass)
+ program->setUniformValue("delta", blur / m_textureSize.width(), blur / m_textureSize.height());
+ else if (m_horizontalBlur)
program->setUniformValue("delta", blur / m_textureSize.width(), 0.0);
else
program->setUniformValue("delta", 0.0, blur / m_textureSize.height());
diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp
index 83ebece..c965947 100644
--- a/src/opengl/qpixmapdata_gl.cpp
+++ b/src/opengl/qpixmapdata_gl.cpp
@@ -76,6 +76,19 @@ static inline int areaDiff(const QSize &size, const QGLFramebufferObject *fbo)
return qAbs(size.width() * size.height() - fbo->width() * fbo->height());
}
+extern int qt_next_power_of_two(int v);
+
+static inline QSize maybeRoundToNextPowerOfTwo(const QSize &sz)
+{
+#ifdef QT_OPENGL_ES_2
+ QSize rounded(qt_next_power_of_two(sz.width()), qt_next_power_of_two(sz.height()));
+ if (rounded.width() * rounded.height() < 1.20 * sz.width() * sz.height())
+ return rounded;
+#endif
+ return sz;
+}
+
+
QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat)
{
QGLFramebufferObject *chosen = 0;
@@ -106,7 +119,7 @@ QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize
if (sz != fboSize) {
delete candidate;
- candidate = new QGLFramebufferObject(sz, requestFormat);
+ candidate = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(sz), requestFormat);
}
chosen = candidate;
@@ -114,7 +127,7 @@ QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize
}
if (!chosen) {
- chosen = new QGLFramebufferObject(requestSize, requestFormat);
+ chosen = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(requestSize), requestFormat);
}
if (!chosen->isValid()) {