| /* |
| Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) |
| Copyright (C) 2012 Igalia S.L. |
| Copyright (C) 2011 Google Inc. All rights reserved. |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Library General Public |
| License as published by the Free Software Foundation; either |
| version 2 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Library General Public License for more details. |
| |
| You should have received a copy of the GNU Library General Public License |
| along with this library; see the file COPYING.LIB. If not, write to |
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "TextureMapperShaderManager.h" |
| |
| #if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) |
| |
| #include "LengthFunctions.h" |
| #include "Logging.h" |
| #include "TextureMapperGL.h" |
| |
| namespace WebCore { |
| |
| #ifndef TEXMAP_OPENGL_ES_2 |
| #define OES2_PRECISION_DEFINITIONS \ |
| "#define lowp\n#define highp\n" |
| #define OES2_FRAGMENT_SHADER_DEFAULT_PRECISION |
| #else |
| #define OES2_PRECISION_DEFINITIONS |
| #define OES2_FRAGMENT_SHADER_DEFAULT_PRECISION \ |
| "precision mediump float; \n" |
| #endif |
| |
| #define STRINGIFY_VAL(src...) #src |
| #define VERTEX_SHADER(src...) OES2_PRECISION_DEFINITIONS\ |
| STRINGIFY_VAL(src) |
| #define FRAGMENT_SHADER(src...) OES2_PRECISION_DEFINITIONS\ |
| OES2_FRAGMENT_SHADER_DEFAULT_PRECISION\ |
| STRINGIFY_VAL(src) |
| |
| static const char* fragmentShaderSourceOpacityAndMask = |
| FRAGMENT_SHADER( |
| uniform sampler2D s_source; |
| uniform sampler2D s_mask; |
| uniform lowp float u_opacity; |
| varying highp vec2 v_sourceTexCoord; |
| varying highp vec2 v_maskTexCoord; |
| void main(void) |
| { |
| lowp vec4 color = texture2D(s_source, v_sourceTexCoord); |
| lowp vec4 maskColor = texture2D(s_mask, v_maskTexCoord); |
| lowp float fragmentAlpha = u_opacity * maskColor.a; |
| gl_FragColor = vec4(color.rgb * fragmentAlpha, color.a * fragmentAlpha); |
| } |
| ); |
| |
| static const char* fragmentShaderSourceRectOpacityAndMask = |
| FRAGMENT_SHADER( |
| uniform sampler2DRect s_source; |
| uniform sampler2DRect s_mask; |
| uniform lowp float u_opacity; |
| varying highp vec2 v_sourceTexCoord; |
| varying highp vec2 v_maskTexCoord; |
| void main(void) |
| { |
| lowp vec4 color = texture2DRect(s_source, v_sourceTexCoord); |
| lowp vec4 maskColor = texture2DRect(s_mask, v_maskTexCoord); |
| lowp float fragmentAlpha = u_opacity * maskColor.a; |
| gl_FragColor = vec4(color.rgb * fragmentAlpha, color.a * fragmentAlpha); |
| } |
| ); |
| |
| static const char* vertexShaderSourceOpacityAndMask = |
| VERTEX_SHADER( |
| uniform mat4 u_matrix; |
| uniform lowp float u_flip; |
| attribute vec4 a_vertex; |
| varying highp vec2 v_sourceTexCoord; |
| varying highp vec2 v_maskTexCoord; |
| void main(void) |
| { |
| v_sourceTexCoord = vec2(a_vertex.x, mix(a_vertex.y, 1. - a_vertex.y, u_flip)); |
| v_maskTexCoord = vec2(a_vertex); |
| gl_Position = u_matrix * a_vertex; |
| } |
| ); |
| |
| static const char* fragmentShaderSourceSimple = |
| FRAGMENT_SHADER( |
| uniform sampler2D s_source; |
| uniform lowp float u_opacity; |
| varying highp vec2 v_sourceTexCoord; |
| void main(void) |
| { |
| lowp vec4 color = texture2D(s_source, v_sourceTexCoord); |
| gl_FragColor = vec4(color.rgb * u_opacity, color.a * u_opacity); |
| } |
| ); |
| |
| static const char* fragmentShaderSourceAntialiasingNoMask = |
| FRAGMENT_SHADER( |
| uniform sampler2D s_source; |
| varying highp vec2 v_sourceTexCoord; |
| uniform lowp float u_opacity; |
| uniform vec3 u_expandedQuadEdgesInScreenSpace[8]; |
| void main() |
| { |
| vec4 sampledColor = texture2D(s_source, clamp(v_sourceTexCoord, 0.0, 1.0)); |
| vec3 pos = vec3(gl_FragCoord.xy, 1); |
| |
| // The data passed in u_expandedQuadEdgesInScreenSpace is merely the |
| // pre-scaled coeffecients of the line equations describing the four edges |
| // of the expanded quad in screen space and the rectangular bounding box |
| // of the expanded quad. |
| // |
| // We are doing a simple distance calculation here according to the formula: |
| // (A*p.x + B*p.y + C) / sqrt(A^2 + B^2) = distance from line to p |
| // Note that A, B and C have already been scaled by 1 / sqrt(A^2 + B^2). |
| float a0 = clamp(dot(u_expandedQuadEdgesInScreenSpace[0], pos), 0.0, 1.0); |
| float a1 = clamp(dot(u_expandedQuadEdgesInScreenSpace[1], pos), 0.0, 1.0); |
| float a2 = clamp(dot(u_expandedQuadEdgesInScreenSpace[2], pos), 0.0, 1.0); |
| float a3 = clamp(dot(u_expandedQuadEdgesInScreenSpace[3], pos), 0.0, 1.0); |
| float a4 = clamp(dot(u_expandedQuadEdgesInScreenSpace[4], pos), 0.0, 1.0); |
| float a5 = clamp(dot(u_expandedQuadEdgesInScreenSpace[5], pos), 0.0, 1.0); |
| float a6 = clamp(dot(u_expandedQuadEdgesInScreenSpace[6], pos), 0.0, 1.0); |
| float a7 = clamp(dot(u_expandedQuadEdgesInScreenSpace[7], pos), 0.0, 1.0); |
| |
| // Now we want to reduce the alpha value of the fragment if it is close to the |
| // edges of the expanded quad (or rectangular bounding box -- which seems to be |
| // important for backfacing quads). Note that we are combining the contribution |
| // from the (top || bottom) and (left || right) edge by simply multiplying. This follows |
| // the approach described at: http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter22.html, |
| // in this case without using Gaussian weights. |
| gl_FragColor = sampledColor * u_opacity * min(min(a0, a2) * min(a1, a3), min(a4, a6) * min(a5, a7)); |
| } |
| ); |
| |
| static const char* fragmentShaderSourceRectSimple = |
| FRAGMENT_SHADER( |
| uniform sampler2DRect s_source; |
| uniform lowp vec2 u_textureSize; |
| uniform lowp float u_opacity; |
| varying highp vec2 v_sourceTexCoord; |
| void main(void) |
| { |
| lowp vec4 color = texture2DRect(s_source, u_textureSize * v_sourceTexCoord); |
| gl_FragColor = vec4(color.rgb * u_opacity, color.a * u_opacity); |
| } |
| ); |
| |
| static const char* vertexShaderSourceSimple = |
| VERTEX_SHADER( |
| uniform mat4 u_matrix; |
| uniform lowp float u_flip; |
| attribute vec4 a_vertex; |
| varying highp vec2 v_sourceTexCoord; |
| void main(void) |
| { |
| v_sourceTexCoord = vec2(a_vertex.x, mix(a_vertex.y, 1. - a_vertex.y, u_flip)); |
| gl_Position = u_matrix * a_vertex; |
| } |
| ); |
| |
| static const char* vertexShaderSourceSolidColor = |
| VERTEX_SHADER( |
| uniform mat4 u_matrix; |
| attribute vec4 a_vertex; |
| void main(void) |
| { |
| gl_Position = u_matrix * a_vertex; |
| } |
| ); |
| |
| |
| static const char* fragmentShaderSourceSolidColor = |
| VERTEX_SHADER( |
| uniform vec4 u_color; |
| void main(void) |
| { |
| gl_FragColor = u_color; |
| } |
| ); |
| |
| PassRefPtr<TextureMapperShaderProgramSolidColor> TextureMapperShaderManager::solidColorProgram() |
| { |
| return static_pointer_cast<TextureMapperShaderProgramSolidColor>(getShaderProgram(SolidColor)); |
| } |
| |
| PassRefPtr<TextureMapperShaderProgramAntialiasingNoMask> TextureMapperShaderManager::antialiasingNoMaskProgram() |
| { |
| return static_pointer_cast<TextureMapperShaderProgramAntialiasingNoMask>(getShaderProgram(AntialiasingNoMask)); |
| } |
| |
| PassRefPtr<TextureMapperShaderProgram> TextureMapperShaderManager::getShaderProgram(ShaderType shaderType) |
| { |
| RefPtr<TextureMapperShaderProgram> program; |
| if (shaderType == Invalid) |
| return program; |
| |
| TextureMapperShaderProgramMap::iterator it = m_textureMapperShaderProgramMap.find(shaderType); |
| if (it != m_textureMapperShaderProgramMap.end()) |
| return it->second; |
| |
| switch (shaderType) { |
| case Simple: |
| program = TextureMapperShaderProgramSimple::create(); |
| break; |
| case RectSimple: |
| program = TextureMapperShaderProgramRectSimple::create(); |
| break; |
| case AntialiasingNoMask: |
| program = TextureMapperShaderProgramAntialiasingNoMask::create(); |
| break; |
| case OpacityAndMask: |
| program = TextureMapperShaderProgramOpacityAndMask::create(); |
| break; |
| case RectOpacityAndMask: |
| program = TextureMapperShaderProgramRectOpacityAndMask::create(); |
| break; |
| case SolidColor: |
| program = TextureMapperShaderProgramSolidColor::create(); |
| break; |
| case Invalid: |
| ASSERT_NOT_REACHED(); |
| } |
| m_textureMapperShaderProgramMap.add(shaderType, program); |
| return program; |
| } |
| |
| TextureMapperShaderProgram::TextureMapperShaderProgram(const char* vertexShaderSource, const char* fragmentShaderSource) |
| : m_id(0) |
| , m_vertexAttrib(0) |
| , m_vertexShader(0) |
| , m_fragmentShader(0) |
| , m_matrixLocation(-1) |
| , m_flipLocation(-1) |
| , m_textureSizeLocation(-1) |
| , m_sourceTextureLocation(-1) |
| , m_opacityLocation(-1) |
| , m_maskTextureLocation(-1) |
| , m_vertexShaderSource(vertexShaderSource) |
| , m_fragmentShaderSource(fragmentShaderSource) |
| { |
| } |
| |
| void TextureMapperShaderProgram::initializeProgram() |
| { |
| const char* vertexShaderSourceProgram = vertexShaderSource(); |
| const char* fragmentShaderSourceProgram = fragmentShaderSource(); |
| GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); |
| GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); |
| glShaderSource(vertexShader, 1, &vertexShaderSourceProgram, 0); |
| glShaderSource(fragmentShader, 1, &fragmentShaderSourceProgram, 0); |
| GLuint programID = glCreateProgram(); |
| glCompileShader(vertexShader); |
| glCompileShader(fragmentShader); |
| |
| glAttachShader(programID, vertexShader); |
| glAttachShader(programID, fragmentShader); |
| glLinkProgram(programID); |
| |
| m_vertexAttrib = glGetAttribLocation(programID, "a_vertex"); |
| |
| m_id = programID; |
| m_vertexShader = vertexShader; |
| m_fragmentShader = fragmentShader; |
| } |
| |
| void TextureMapperShaderProgram::getUniformLocation(GLint &variable, const char* name) |
| { |
| variable = glGetUniformLocation(m_id, name); |
| ASSERT(variable >= 0); |
| } |
| |
| TextureMapperShaderProgram::~TextureMapperShaderProgram() |
| { |
| GLuint programID = m_id; |
| if (!programID) |
| return; |
| |
| glDetachShader(programID, m_vertexShader); |
| glDeleteShader(m_vertexShader); |
| glDetachShader(programID, m_fragmentShader); |
| glDeleteShader(m_fragmentShader); |
| glDeleteProgram(programID); |
| } |
| |
| TextureMapperShaderProgramSimple::TextureMapperShaderProgramSimple() |
| : TextureMapperShaderProgram(vertexShaderSourceSimple, fragmentShaderSourceSimple) |
| { |
| initializeProgram(); |
| getUniformLocation(m_flipLocation, "u_flip"); |
| getUniformLocation(m_matrixLocation, "u_matrix"); |
| getUniformLocation(m_sourceTextureLocation, "s_source"); |
| getUniformLocation(m_opacityLocation, "u_opacity"); |
| } |
| |
| TextureMapperShaderProgramSolidColor::TextureMapperShaderProgramSolidColor() |
| : TextureMapperShaderProgram(vertexShaderSourceSolidColor, fragmentShaderSourceSolidColor) |
| { |
| initializeProgram(); |
| getUniformLocation(m_matrixLocation, "u_matrix"); |
| getUniformLocation(m_colorLocation, "u_color"); |
| } |
| |
| TextureMapperShaderProgramRectSimple::TextureMapperShaderProgramRectSimple() |
| : TextureMapperShaderProgram(vertexShaderSourceSimple, fragmentShaderSourceRectSimple) |
| { |
| initializeProgram(); |
| getUniformLocation(m_matrixLocation, "u_matrix"); |
| getUniformLocation(m_flipLocation, "u_flip"); |
| getUniformLocation(m_textureSizeLocation, "u_textureSize"); |
| getUniformLocation(m_sourceTextureLocation, "s_source"); |
| getUniformLocation(m_opacityLocation, "u_opacity"); |
| } |
| |
| TextureMapperShaderProgramOpacityAndMask::TextureMapperShaderProgramOpacityAndMask() |
| : TextureMapperShaderProgram(vertexShaderSourceOpacityAndMask, fragmentShaderSourceOpacityAndMask) |
| { |
| initializeProgram(); |
| getUniformLocation(m_matrixLocation, "u_matrix"); |
| getUniformLocation(m_flipLocation, "u_flip"); |
| getUniformLocation(m_sourceTextureLocation, "s_source"); |
| getUniformLocation(m_maskTextureLocation, "s_mask"); |
| getUniformLocation(m_opacityLocation, "u_opacity"); |
| } |
| |
| TextureMapperShaderProgramRectOpacityAndMask::TextureMapperShaderProgramRectOpacityAndMask() |
| : TextureMapperShaderProgram(vertexShaderSourceOpacityAndMask, fragmentShaderSourceRectOpacityAndMask) |
| { |
| initializeProgram(); |
| getUniformLocation(m_matrixLocation, "u_matrix"); |
| getUniformLocation(m_flipLocation, "u_flip"); |
| getUniformLocation(m_sourceTextureLocation, "s_source"); |
| getUniformLocation(m_maskTextureLocation, "s_mask"); |
| getUniformLocation(m_opacityLocation, "u_opacity"); |
| } |
| |
| TextureMapperShaderProgramAntialiasingNoMask::TextureMapperShaderProgramAntialiasingNoMask() |
| : TextureMapperShaderProgram(vertexShaderSourceSimple, fragmentShaderSourceAntialiasingNoMask) |
| { |
| initializeProgram(); |
| getUniformLocation(m_matrixLocation, "u_matrix"); |
| getUniformLocation(m_sourceTextureLocation, "s_source"); |
| getUniformLocation(m_opacityLocation, "u_opacity"); |
| getUniformLocation(m_expandedQuadEdgesInScreenSpaceLocation, "u_expandedQuadEdgesInScreenSpace"); |
| getUniformLocation(m_flipLocation, "u_flip"); |
| } |
| |
| TextureMapperShaderManager::TextureMapperShaderManager() |
| { |
| ASSERT(initializeOpenGLShims()); |
| } |
| |
| TextureMapperShaderManager::~TextureMapperShaderManager() |
| { |
| } |
| |
| #if ENABLE(CSS_FILTERS) |
| |
| // Create a normal distribution of 21 values between -2 and 2. |
| #define GAUSSIAN_KERNEL_HALF_WIDTH 11 |
| #define GAUSSIAN_KERNEL_STEP 0.2 |
| |
| StandardFilterProgram::~StandardFilterProgram() |
| { |
| glDetachShader(m_id, m_vertexShader); |
| glDeleteShader(m_vertexShader); |
| glDetachShader(m_id, m_fragmentShader); |
| glDeleteShader(m_fragmentShader); |
| glDeleteProgram(m_id); |
| } |
| |
| StandardFilterProgram::StandardFilterProgram(FilterOperation::OperationType type, unsigned pass) |
| : m_id(0) |
| { |
| const char* vertexShaderSource = |
| VERTEX_SHADER( |
| attribute vec4 a_vertex; |
| attribute vec4 a_texCoord; |
| varying highp vec2 v_texCoord; |
| void main(void) |
| { |
| v_texCoord = vec2(a_texCoord); |
| gl_Position = a_vertex; |
| } |
| ); |
| |
| #define STANDARD_FILTER(x...) \ |
| OES2_PRECISION_DEFINITIONS\ |
| OES2_FRAGMENT_SHADER_DEFAULT_PRECISION\ |
| "varying highp vec2 v_texCoord;\n"\ |
| "uniform highp float u_amount;\n"\ |
| "uniform sampler2D u_texture;\n"\ |
| #x\ |
| "void main(void)\n { gl_FragColor = shade(texture2D(u_texture, v_texCoord)); }" |
| |
| const char* fragmentShaderSource = 0; |
| switch (type) { |
| case FilterOperation::GRAYSCALE: |
| fragmentShaderSource = STANDARD_FILTER( |
| lowp vec4 shade(lowp vec4 color) |
| { |
| lowp float amount = 1.0 - u_amount; |
| return vec4((0.2126 + 0.7874 * amount) * color.r + (0.7152 - 0.7152 * amount) * color.g + (0.0722 - 0.0722 * amount) * color.b, |
| (0.2126 - 0.2126 * amount) * color.r + (0.7152 + 0.2848 * amount) * color.g + (0.0722 - 0.0722 * amount) * color.b, |
| (0.2126 - 0.2126 * amount) * color.r + (0.7152 - 0.7152 * amount) * color.g + (0.0722 + 0.9278 * amount) * color.b, |
| color.a); |
| } |
| ); |
| break; |
| case FilterOperation::SEPIA: |
| fragmentShaderSource = STANDARD_FILTER( |
| lowp vec4 shade(lowp vec4 color) |
| { |
| lowp float amount = 1.0 - u_amount; |
| return vec4((0.393 + 0.607 * amount) * color.r + (0.769 - 0.769 * amount) * color.g + (0.189 - 0.189 * amount) * color.b, |
| (0.349 - 0.349 * amount) * color.r + (0.686 + 0.314 * amount) * color.g + (0.168 - 0.168 * amount) * color.b, |
| (0.272 - 0.272 * amount) * color.r + (0.534 - 0.534 * amount) * color.g + (0.131 + 0.869 * amount) * color.b, |
| color.a); |
| } |
| ); |
| break; |
| case FilterOperation::SATURATE: |
| fragmentShaderSource = STANDARD_FILTER( |
| lowp vec4 shade(lowp vec4 color) |
| { |
| return vec4((0.213 + 0.787 * u_amount) * color.r + (0.715 - 0.715 * u_amount) * color.g + (0.072 - 0.072 * u_amount) * color.b, |
| (0.213 - 0.213 * u_amount) * color.r + (0.715 + 0.285 * u_amount) * color.g + (0.072 - 0.072 * u_amount) * color.b, |
| (0.213 - 0.213 * u_amount) * color.r + (0.715 - 0.715 * u_amount) * color.g + (0.072 + 0.928 * u_amount) * color.b, |
| color.a); |
| } |
| ); |
| break; |
| case FilterOperation::HUE_ROTATE: |
| fragmentShaderSource = STANDARD_FILTER( |
| lowp vec4 shade(lowp vec4 color) |
| { |
| highp float pi = 3.14159265358979323846; |
| highp float c = cos(u_amount * pi / 180.0); |
| highp float s = sin(u_amount * pi / 180.0); |
| return vec4(color.r * (0.213 + c * 0.787 - s * 0.213) + color.g * (0.715 - c * 0.715 - s * 0.715) + color.b * (0.072 - c * 0.072 + s * 0.928), |
| color.r * (0.213 - c * 0.213 + s * 0.143) + color.g * (0.715 + c * 0.285 + s * 0.140) + color.b * (0.072 - c * 0.072 - s * 0.283), |
| color.r * (0.213 - c * 0.213 - s * 0.787) + color.g * (0.715 - c * 0.715 + s * 0.715) + color.b * (0.072 + c * 0.928 + s * 0.072), |
| color.a); |
| } |
| ); |
| break; |
| case FilterOperation::INVERT: |
| fragmentShaderSource = STANDARD_FILTER( |
| lowp float invert(lowp float n) { return (1.0 - n) * u_amount + n * (1.0 - u_amount); } |
| lowp vec4 shade(lowp vec4 color) |
| { |
| return vec4(invert(color.r), invert(color.g), invert(color.b), color.a); |
| } |
| ); |
| break; |
| case FilterOperation::BRIGHTNESS: |
| fragmentShaderSource = STANDARD_FILTER( |
| lowp vec4 shade(lowp vec4 color) |
| { |
| return vec4(color.rgb * (1.0 + u_amount), color.a); |
| } |
| ); |
| break; |
| case FilterOperation::CONTRAST: |
| fragmentShaderSource = STANDARD_FILTER( |
| lowp float contrast(lowp float n) { return (n - 0.5) * u_amount + 0.5; } |
| lowp vec4 shade(lowp vec4 color) |
| { |
| return vec4(contrast(color.r), contrast(color.g), contrast(color.b), color.a); |
| } |
| ); |
| break; |
| case FilterOperation::OPACITY: |
| fragmentShaderSource = STANDARD_FILTER( |
| lowp vec4 shade(lowp vec4 color) |
| { |
| return vec4(color.r, color.g, color.b, color.a * u_amount); |
| } |
| ); |
| break; |
| case FilterOperation::BLUR: |
| fragmentShaderSource = FRAGMENT_SHADER( |
| varying highp vec2 v_texCoord; |
| uniform lowp vec2 u_blurRadius; |
| uniform sampler2D u_texture; |
| uniform float u_gaussianKernel[GAUSSIAN_KERNEL_HALF_WIDTH]; |
| |
| lowp vec4 sampleColor(float radius) |
| { |
| vec2 coord = v_texCoord + radius * u_blurRadius; |
| return texture2D(u_texture, coord) * float(coord.x > 0. && coord.y > 0. && coord.x < 1. && coord.y < 1.); |
| } |
| |
| vec4 blur() |
| { |
| vec4 total = sampleColor(0) * u_gaussianKernel[0]; |
| for (int i = 1; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) { |
| total += sampleColor(float(i) * GAUSSIAN_KERNEL_STEP) * u_gaussianKernel[i]; |
| total += sampleColor(float(-1 * i) * GAUSSIAN_KERNEL_STEP) * u_gaussianKernel[i]; |
| } |
| |
| return total; |
| } |
| |
| void main(void) |
| { |
| gl_FragColor = blur(); |
| } |
| ); |
| break; |
| case FilterOperation::DROP_SHADOW: |
| switch (pass) { |
| case 0: { |
| // First pass: horizontal alpha blur. |
| fragmentShaderSource = FRAGMENT_SHADER( |
| varying highp vec2 v_texCoord; |
| uniform lowp float u_shadowBlurRadius; |
| uniform lowp vec2 u_shadowOffset; |
| uniform sampler2D u_texture; |
| uniform float u_gaussianKernel[GAUSSIAN_KERNEL_HALF_WIDTH]; |
| |
| lowp float sampleAlpha(float radius) |
| { |
| vec2 coord = v_texCoord - u_shadowOffset + vec2(radius * u_shadowBlurRadius, 0.); |
| return texture2D(u_texture, coord).a * float(coord.x > 0. && coord.y > 0. && coord.x < 1. && coord.y < 1.); |
| } |
| |
| lowp float shadowBlurHorizontal() |
| { |
| float total = sampleAlpha(0) * u_gaussianKernel[0]; |
| for (int i = 1; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) { |
| total += sampleAlpha(float(i) * GAUSSIAN_KERNEL_STEP) * u_gaussianKernel[i]; |
| total += sampleAlpha(float(-1 * i) * GAUSSIAN_KERNEL_STEP) * u_gaussianKernel[i]; |
| } |
| |
| return total; |
| } |
| |
| void main(void) |
| { |
| gl_FragColor = vec4(1., 1., 1., 1.) * shadowBlurHorizontal(); |
| } |
| ); |
| break; |
| } |
| case 1: { |
| // Second pass: vertical alpha blur and composite with origin. |
| fragmentShaderSource = FRAGMENT_SHADER( |
| varying highp vec2 v_texCoord; |
| uniform lowp float u_shadowBlurRadius; |
| uniform lowp vec4 u_shadowColor; |
| uniform sampler2D u_texture; |
| uniform sampler2D u_contentTexture; |
| uniform float u_gaussianKernel[GAUSSIAN_KERNEL_HALF_WIDTH]; |
| |
| lowp float sampleAlpha(float r) |
| { |
| vec2 coord = v_texCoord + vec2(0., r * u_shadowBlurRadius); |
| return texture2D(u_texture, coord).a * float(coord.x > 0. && coord.y > 0. && coord.x < 1. && coord.y < 1.); |
| } |
| |
| lowp float shadowBlurVertical() |
| { |
| float total = sampleAlpha(0) * u_gaussianKernel[0]; |
| for (int i = 1; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) { |
| total += sampleAlpha(float(i) * GAUSSIAN_KERNEL_STEP) * u_gaussianKernel[i]; |
| total += sampleAlpha(float(-1 * i) * GAUSSIAN_KERNEL_STEP) * u_gaussianKernel[i]; |
| } |
| |
| return total; |
| } |
| |
| lowp vec4 sourceOver(lowp vec4 source, lowp vec4 destination) |
| { |
| // Composite the shadow with the original texture. |
| return source + destination * (1. - source.a); |
| } |
| |
| void main(void) |
| { |
| gl_FragColor = sourceOver(texture2D(u_contentTexture, v_texCoord), shadowBlurVertical() * u_shadowColor); |
| } |
| ); |
| break; |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| |
| if (!fragmentShaderSource) |
| return; |
| GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); |
| GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); |
| glShaderSource(vertexShader, 1, &vertexShaderSource, 0); |
| glShaderSource(fragmentShader, 1, &fragmentShaderSource, 0); |
| GLuint programID = glCreateProgram(); |
| glCompileShader(vertexShader); |
| glCompileShader(fragmentShader); |
| #if !LOG_DISABLED |
| GLchar log[100]; |
| GLint len; |
| glGetShaderInfoLog(fragmentShader, 100, &len, log); |
| WTFLog(&LogCompositing, "%s\n", log); |
| #endif |
| glAttachShader(programID, vertexShader); |
| glAttachShader(programID, fragmentShader); |
| glLinkProgram(programID); |
| |
| m_vertexAttrib = glGetAttribLocation(programID, "a_vertex"); |
| m_texCoordAttrib = glGetAttribLocation(programID, "a_texCoord"); |
| m_textureUniformLocation = glGetUniformLocation(programID, "u_texture"); |
| switch (type) { |
| case FilterOperation::GRAYSCALE: |
| case FilterOperation::SEPIA: |
| case FilterOperation::SATURATE: |
| case FilterOperation::HUE_ROTATE: |
| case FilterOperation::INVERT: |
| case FilterOperation::BRIGHTNESS: |
| case FilterOperation::CONTRAST: |
| case FilterOperation::OPACITY: |
| m_uniformLocations.amount = glGetUniformLocation(programID, "u_amount"); |
| break; |
| case FilterOperation::BLUR: |
| m_uniformLocations.blur.radius = glGetUniformLocation(programID, "u_blurRadius"); |
| m_uniformLocations.blur.gaussianKernel = glGetUniformLocation(programID, "u_gaussianKernel"); |
| break; |
| case FilterOperation::DROP_SHADOW: |
| m_uniformLocations.shadow.blurRadius = glGetUniformLocation(programID, "u_shadowBlurRadius"); |
| m_uniformLocations.shadow.gaussianKernel = glGetUniformLocation(programID, "u_gaussianKernel"); |
| if (!pass) |
| m_uniformLocations.shadow.offset = glGetUniformLocation(programID, "u_shadowOffset"); |
| else { |
| // We only need the color and the content texture in the second pass, the first pass is only a horizontal alpha blur. |
| m_uniformLocations.shadow.color = glGetUniformLocation(programID, "u_shadowColor"); |
| m_uniformLocations.shadow.contentTexture = glGetUniformLocation(programID, "u_contentTexture"); |
| } |
| break; |
| default: |
| break; |
| } |
| m_id = programID; |
| m_vertexShader = vertexShader; |
| m_fragmentShader = fragmentShader; |
| } |
| |
| PassRefPtr<StandardFilterProgram> StandardFilterProgram::create(FilterOperation::OperationType type, unsigned pass) |
| { |
| RefPtr<StandardFilterProgram> program = adoptRef(new StandardFilterProgram(type, pass)); |
| if (!program->m_id) |
| return 0; |
| |
| return program; |
| } |
| |
| static inline float gauss(float x) |
| { |
| return exp(-(x * x) / 2.); |
| } |
| |
| static float* gaussianKernel() |
| { |
| static bool prepared = false; |
| static float kernel[GAUSSIAN_KERNEL_HALF_WIDTH] = {0, }; |
| |
| if (prepared) |
| return kernel; |
| |
| kernel[0] = gauss(0); |
| float sum = kernel[0]; |
| for (unsigned i = 1; i < GAUSSIAN_KERNEL_HALF_WIDTH; ++i) { |
| kernel[i] = gauss(i * GAUSSIAN_KERNEL_STEP); |
| sum += 2 * kernel[i]; |
| } |
| |
| // Normalize the kernel |
| float scale = 1 / sum; |
| for (unsigned i = 0; i < GAUSSIAN_KERNEL_HALF_WIDTH; ++i) |
| kernel[i] *= scale; |
| |
| prepared = true; |
| return kernel; |
| } |
| |
| void StandardFilterProgram::prepare(const FilterOperation& operation, unsigned pass, const IntSize& size, GLuint contentTexture) |
| { |
| glUseProgram(m_id); |
| switch (operation.getOperationType()) { |
| case FilterOperation::GRAYSCALE: |
| case FilterOperation::SEPIA: |
| case FilterOperation::SATURATE: |
| case FilterOperation::HUE_ROTATE: |
| glUniform1f(m_uniformLocations.amount, static_cast<const BasicColorMatrixFilterOperation&>(operation).amount()); |
| break; |
| case FilterOperation::INVERT: |
| case FilterOperation::BRIGHTNESS: |
| case FilterOperation::CONTRAST: |
| case FilterOperation::OPACITY: |
| glUniform1f(m_uniformLocations.amount, static_cast<const BasicComponentTransferFilterOperation&>(operation).amount()); |
| break; |
| case FilterOperation::BLUR: { |
| const BlurFilterOperation& blur = static_cast<const BlurFilterOperation&>(operation); |
| FloatSize radius; |
| |
| // Blur is done in two passes, first horizontally and then vertically. The same shader is used for both. |
| if (pass) |
| radius.setHeight(floatValueForLength(blur.stdDeviation(), size.height()) / size.height()); |
| else |
| radius.setWidth(floatValueForLength(blur.stdDeviation(), size.width()) / size.width()); |
| |
| glUniform2f(m_uniformLocations.blur.radius, radius.width(), radius.height()); |
| glUniform1fv(m_uniformLocations.blur.gaussianKernel, GAUSSIAN_KERNEL_HALF_WIDTH, gaussianKernel()); |
| break; |
| } |
| case FilterOperation::DROP_SHADOW: { |
| const DropShadowFilterOperation& shadow = static_cast<const DropShadowFilterOperation&>(operation); |
| switch (pass) { |
| case 0: |
| // First pass: vertical alpha blur. |
| glUniform2f(m_uniformLocations.shadow.offset, float(shadow.location().x()) / float(size.width()), float(shadow.location().y()) / float(size.height())); |
| glUniform1f(m_uniformLocations.shadow.blurRadius, shadow.stdDeviation() / float(size.width())); |
| glUniform1fv(m_uniformLocations.shadow.gaussianKernel, GAUSSIAN_KERNEL_HALF_WIDTH, gaussianKernel()); |
| break; |
| case 1: |
| // Second pass: we need the shadow color and the content texture for compositing. |
| glUniform1f(m_uniformLocations.shadow.blurRadius, shadow.stdDeviation() / float(size.height())); |
| glUniform1fv(m_uniformLocations.shadow.gaussianKernel, GAUSSIAN_KERNEL_HALF_WIDTH, gaussianKernel()); |
| glActiveTexture(GL_TEXTURE1); |
| glBindTexture(GL_TEXTURE_2D, contentTexture); |
| glUniform1i(m_uniformLocations.shadow.contentTexture, 1); |
| float r, g, b, a; |
| shadow.color().getRGBA(r, g, b, a); |
| glUniform4f(m_uniformLocations.shadow.color, r, g, b, a); |
| break; |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| PassRefPtr<StandardFilterProgram> TextureMapperShaderManager::getShaderForFilter(const FilterOperation& filter, unsigned pass) |
| { |
| RefPtr<StandardFilterProgram> program; |
| FilterOperation::OperationType type = filter.getOperationType(); |
| int key = int(type) | (pass << 16); |
| FilterMap::iterator iterator = m_filterMap.find(key); |
| if (iterator == m_filterMap.end()) { |
| program = StandardFilterProgram::create(type, pass); |
| if (!program) |
| return 0; |
| |
| m_filterMap.add(key, program); |
| } else |
| program = iterator->second; |
| |
| return program; |
| } |
| |
| unsigned TextureMapperShaderManager::getPassesRequiredForFilter(const FilterOperation& operation) const |
| { |
| switch (operation.getOperationType()) { |
| case FilterOperation::GRAYSCALE: |
| case FilterOperation::SEPIA: |
| case FilterOperation::SATURATE: |
| case FilterOperation::HUE_ROTATE: |
| case FilterOperation::INVERT: |
| case FilterOperation::BRIGHTNESS: |
| case FilterOperation::CONTRAST: |
| case FilterOperation::OPACITY: |
| return 1; |
| case FilterOperation::BLUR: |
| case FilterOperation::DROP_SHADOW: |
| // We use two-passes (vertical+horizontal) for blur and drop-shadow. |
| return 2; |
| default: |
| return 0; |
| } |
| |
| } |
| |
| #endif |
| }; |
| |
| #endif |