diff options
Diffstat (limited to 'src/opengl/qglpixmapfilter.cpp')
-rw-r--r-- | src/opengl/qglpixmapfilter.cpp | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp new file mode 100644 index 0000000000..ff23948a3c --- /dev/null +++ b/src/opengl/qglpixmapfilter.cpp @@ -0,0 +1,409 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qpixmapfilter_p.h" +#include "qglpixmapfilter_p.h" +#include "qgraphicssystem_gl_p.h" +#include "qpaintengine_opengl_p.h" + +#include "qglpixelbuffer.h" +#include "qglextensions_p.h" +#include "qgl_p.h" + +#include "private/qapplication_p.h" + + +#ifndef GL_FRAGMENT_SHADER +#define GL_FRAGMENT_SHADER 0x8B30 +#endif + +#ifndef GL_COMPILE_STATUS +#define GL_COMPILE_STATUS 0x8B81 +#endif + +#ifndef GL_LINK_STATUS +#define GL_LINK_STATUS 0x8B82 +#endif + + + + +QT_BEGIN_NAMESPACE + + +void QGLPixmapFilterBase::bindTexture(const QPixmap &src) const +{ + const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, true); +} + +void QGLPixmapFilterBase::drawImpl(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF& source) const +{ + processGL(painter, pos, src, source); +} + +QGLSLProgram::QGLSLProgram(const QString &src) + : ctx(QGLContext::currentContext()) +{ + if (!qt_resolve_glsl_extensions(const_cast<QGLContext *>(ctx))) { + qWarning("Failed to resolve GLSL functions"); + m_valid = false; + return; + } + + m_shader = glCreateShader(GL_FRAGMENT_SHADER); + + const QByteArray src_ba = src.toAscii(); + const char *src_cstr = src_ba.constData(); + + glShaderSource(m_shader, 1, &src_cstr, 0); + glCompileShader(m_shader); + glGetShaderiv(m_shader, GL_COMPILE_STATUS, &m_valid); + if (!m_valid) { + char data[4096]; + GLsizei len; + glGetShaderInfoLog(m_shader, 4096, &len, data); + qWarning("Failed to compile GLSL shader:\n%s\nCODE:\n%s\n", data, src_cstr); + return; + } + + m_program = glCreateProgram(); + glAttachShader(m_program, m_shader); + glLinkProgram(m_program); + glGetProgramiv(m_program, GL_LINK_STATUS, &m_valid); + if (!m_valid) { + char data[4096]; + GLsizei len; + glGetShaderInfoLog(m_shader, 4096, &len, data); + qWarning("Failed to link GLSL program:\n%s\nCODE:\n%s\n", data, src_cstr); + return; + } +} + +QGLSLProgram::~QGLSLProgram() +{ + glDeleteProgram(m_program); + glDeleteShader(m_shader); +} + +bool QGLSLProgram::success() const +{ + return m_valid; +} + +void QGLSLProgram::enable() +{ + glUseProgram(m_program); +} + +void QGLSLProgram::disable() +{ + glUseProgram(0); +} + +typedef GLuint (APIENTRY *_glGetUniformLocation) (GLuint, const char*); +typedef void (APIENTRY *_glUniform4fv) (GLint, GLsizei, GLfloat *); +typedef void (APIENTRY *_glUniform3fv) (GLint, GLsizei, GLfloat *); +typedef void (APIENTRY *_glUniform2fv) (GLint, GLsizei, GLfloat *); +typedef void (APIENTRY *_glUniform1fv) (GLint, GLsizei, GLfloat *); +typedef void (APIENTRY *_glUniform1i) (GLint, GLint); + +int QGLSLProgram::getUniformLocation(const QString &name) +{ + return glGetUniformLocation(m_program, name.toAscii().constData()); +} + +void QGLSLProgram::setUniform(int uniform, int value) +{ + enable(); + glUniform1i(uniform, value); +} + +void QGLSLProgram::setUniform(int uniform, qreal value) +{ + enable(); + GLfloat v[] = { value }; + glUniform1fv(uniform, 1, v); +} + +void QGLSLProgram::setUniform(int uniform, qreal v1, qreal v2) +{ + enable(); + GLfloat v[] = { v1, v2 }; + glUniform2fv(uniform, 1, v); +} + +void QGLSLProgram::setUniform(int uniform, qreal v1, qreal v2, qreal v3) +{ + enable(); + GLfloat v[] = { v1, v2, v3 }; + glUniform3fv(uniform, 1, v); +} + +void QGLSLProgram::setUniform(int uniform, qreal v1, qreal v2, qreal v3, qreal v4) +{ + enable(); + GLfloat v[] = { v1, v2, v3, v4 }; + glUniform4fv(uniform, 1, v); +} + +void QGLSLProgram::setUniform(int uniform, int count, float *v) +{ + enable(); + glUniform1fv(uniform, count, v); +} + +class QGLPixmapColorizeFilter: public QGLPixmapFilter<QPixmapColorizeFilter> +{ +public: + QGLPixmapColorizeFilter(); + +protected: + bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &pixmap, const QRectF &srcRect) const; + +private: + mutable QGLSLProgram m_program; + uint m_colorUniform; +}; + +class QGLPixmapConvolutionFilter: public QGLPixmapFilter<QPixmapConvolutionFilter> +{ +public: + QGLPixmapConvolutionFilter(); + ~QGLPixmapConvolutionFilter(); + +protected: + bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; + +private: + QString generateConvolutionShader() const; + + mutable QGLSLProgram *m_program; + mutable uint m_scaleUniform; + mutable uint m_matrixUniform; + + mutable int m_kernelWidth; + mutable int m_kernelHeight; +}; + +extern QGLWidget *qt_gl_share_widget(); + +QPixmapFilter *QGLContextPrivate::createPixmapFilter(int type) const +{ + switch (type) { + case QPixmapFilter::ColorizeFilter: + return new QGLPixmapColorizeFilter; + + + case QPixmapFilter::ConvolutionFilter: + return new QGLPixmapConvolutionFilter; + + default: + return 0; + break; + } + return 0; +} + +#if !defined(QT_OPENGL_ES_2) +extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array); +extern void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array); +#endif + +static void qgl_drawTexture(const QRectF &rect, int tx_width, int tx_height, const QRectF & src) +{ +#ifndef QT_OPENGL_ES_2 // XXX: needs to be ported +#ifndef QT_OPENGL_ES + glPushAttrib(GL_CURRENT_BIT); +#endif + qreal x1, x2, y1, y2; + + x1 = src.x() / tx_width; + x2 = x1 + src.width() / tx_width; + y1 = 1.0 - ((src.y() / tx_height) + (src.height() / tx_height)); + y2 = 1.0 - (src.y() / tx_height); + + q_vertexType vertexArray[4*2]; + q_vertexType texCoordArray[4*2]; + + qt_add_rect_to_array(rect, vertexArray); + qt_add_texcoords_to_array(x1, y2, x2, y1, texCoordArray); + + glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray); + glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + +#ifndef QT_OPENGL_ES + glPopAttrib(); +#endif +#endif +} + +static const char *qt_gl_colorize_filter = + "uniform sampler2D texture;" + "uniform vec3 color;" + "void main(void)" + "{" + " vec2 coords = gl_TexCoord[0].st;" + " vec4 src = texture2D(texture, coords);" + " float gray = dot(src.rgb, vec3(0.212671, 0.715160, 0.072169));" + " vec3 colorizeed = 1.0-((1.0-gray)*(1.0-color));" + " gl_FragColor = vec4(colorizeed, src.a);" + "}"; + +QGLPixmapColorizeFilter::QGLPixmapColorizeFilter() + : m_program(QLatin1String(qt_gl_colorize_filter)) +{ + m_program.setUniform(m_program.getUniformLocation(QLatin1String("texture")), 0); // GL_TEXTURE_0 + m_colorUniform = m_program.getUniformLocation(QLatin1String("color")); +} + +bool QGLPixmapColorizeFilter::processGL(QPainter *, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const +{ + bindTexture(src); + + QColor col = color(); + m_program.setUniform(m_colorUniform, col.redF(), col.greenF(), col.blueF()); + + QRectF target = (srcRect.isNull() ? QRectF(src.rect()) : srcRect).translated(pos); + m_program.enable(); + qgl_drawTexture(target, src.width(), src.height(), srcRect); + m_program.disable(); + + return true; +} + +// generates convolution filter code for arbitrary sized kernel +QString QGLPixmapConvolutionFilter::generateConvolutionShader() const { + QByteArray code; + code.append("uniform sampler2D texture;\n"); + code.append("uniform vec2 inv_texture_size;\n"); + code.append("uniform float matrix["); + code.append(QByteArray::number(m_kernelWidth * m_kernelHeight)); + code.append("];\n"); + code.append("vec2 offset["); + code.append(QByteArray::number(m_kernelWidth*m_kernelHeight)); + code.append("];\n"); + code.append("void main(void) {\n"); + + for(int y = 0; y < m_kernelHeight; y++) { + for(int x = 0; x < m_kernelWidth; x++) { + code.append(" offset["); + code.append(QByteArray::number(y * m_kernelWidth + x)); + code.append("] = vec2(inv_texture_size.x * "); + code.append(QByteArray::number(x-(int)(m_kernelWidth/2))); + code.append(".0, inv_texture_size.y * "); + code.append(QByteArray::number((int)(m_kernelHeight/2)-y)); + code.append(".0)"); + code.append(";\n"); + } + } + + code.append(" int i = 0;\n"); + code.append(" vec2 coords = gl_TexCoord[0].st;\n"); + code.append(" vec4 sum = vec4(0.0);\n"); + code.append(" for (i = 0; i < "); + code.append(QByteArray::number(m_kernelWidth * m_kernelHeight)); + code.append("; i++) {\n"); + code.append(" vec4 tmp = texture2D(texture,coords+offset[i]);\n"); + code.append(" sum += matrix[i] * tmp;\n"); + code.append(" }\n"); + code.append(" gl_FragColor = sum;\n"); + code.append("}"); + return QLatin1String(code); +} + +QGLPixmapConvolutionFilter::QGLPixmapConvolutionFilter() + : m_program(0) + , m_scaleUniform(0) + , m_matrixUniform(0) + , m_kernelWidth(0) + , m_kernelHeight(0) +{ +} + +QGLPixmapConvolutionFilter::~QGLPixmapConvolutionFilter() +{ + delete m_program; +} + +bool QGLPixmapConvolutionFilter::processGL(QPainter *, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const +{ + QRectF target = (srcRect.isNull() ? QRectF(src.rect()) : srcRect).translated(pos); + + bindTexture(src); +#ifdef GL_CLAMP + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); +#endif + if (!m_program || m_kernelWidth != columns() || m_kernelHeight != rows()) { + delete m_program; + + m_kernelWidth = columns(); + m_kernelHeight = rows(); + + QString code = generateConvolutionShader(); + m_program = new QGLSLProgram(code); + m_scaleUniform = m_program->getUniformLocation(QLatin1String("inv_texture_size")); + m_matrixUniform = m_program->getUniformLocation(QLatin1String("matrix")); + } + + const qreal *kernel = convolutionKernel(); + GLfloat *conv = new GLfloat[m_kernelWidth * m_kernelHeight]; + for(int i = 0; i < m_kernelWidth * m_kernelHeight; ++i) + conv[i] = kernel[i]; + + const qreal iw = 1.0 / src.width(); + const qreal ih = 1.0 / src.height(); + m_program->setUniform(m_scaleUniform, iw, ih); + m_program->setUniform(m_matrixUniform, m_kernelWidth * m_kernelHeight, conv); + + m_program->enable(); + qgl_drawTexture(target, src.width(), src.height(), boundingRectFor(srcRect)); + m_program->disable(); + return true; +} + +QT_END_NAMESPACE |