1/*
2 Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
3 Copyright (C) 2012 Igalia S.L.
4 Copyright (C) 2011 Google Inc. All rights reserved.
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB.  If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23#include "TextureMapperShaderProgram.h"
24
25#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
26#include "LengthFunctions.h"
27#include "Logging.h"
28#include "TextureMapperGL.h"
29
30#include <wtf/text/StringBuilder.h>
31
32#define STRINGIFY(...) #__VA_ARGS__
33
34namespace WebCore {
35
36static inline bool compositingLogEnabled()
37{
38#if !LOG_DISABLED
39    return LogCompositing.state == WTFLogChannelOn;
40#else
41    return false;
42#endif
43}
44
45TextureMapperShaderProgram::TextureMapperShaderProgram(PassRefPtr<GraphicsContext3D> context, const String& vertex, const String& fragment)
46    : m_context(context)
47{
48    m_vertexShader = m_context->createShader(GraphicsContext3D::VERTEX_SHADER);
49    m_fragmentShader = m_context->createShader(GraphicsContext3D::FRAGMENT_SHADER);
50    m_context->shaderSource(m_vertexShader, vertex);
51    m_context->shaderSource(m_fragmentShader, fragment);
52    m_id = m_context->createProgram();
53    m_context->compileShader(m_vertexShader);
54    m_context->compileShader(m_fragmentShader);
55    m_context->attachShader(m_id, m_vertexShader);
56    m_context->attachShader(m_id, m_fragmentShader);
57    m_context->linkProgram(m_id);
58
59    if (!compositingLogEnabled())
60        return;
61
62    if (m_context->getError() == GraphicsContext3D::NO_ERROR)
63        return;
64
65    String log = m_context->getShaderInfoLog(m_vertexShader);
66    LOG(Compositing, "Vertex shader log: %s\n", log.utf8().data());
67    log = m_context->getShaderInfoLog(m_fragmentShader);
68    LOG(Compositing, "Fragment shader log: %s\n", log.utf8().data());
69    log = m_context->getProgramInfoLog(m_id);
70    LOG(Compositing, "Program log: %s\n", log.utf8().data());
71}
72
73void TextureMapperShaderProgram::setMatrix(GC3Duint location, const TransformationMatrix& matrix)
74{
75    GC3Dfloat matrixAsFloats[] = {
76        GC3Dfloat(matrix.m11()), GC3Dfloat(matrix.m12()), GC3Dfloat(matrix.m13()), GC3Dfloat(matrix.m14()),
77        GC3Dfloat(matrix.m21()), GC3Dfloat(matrix.m22()), GC3Dfloat(matrix.m23()), GC3Dfloat(matrix.m24()),
78        GC3Dfloat(matrix.m31()), GC3Dfloat(matrix.m32()), GC3Dfloat(matrix.m33()), GC3Dfloat(matrix.m34()),
79        GC3Dfloat(matrix.m41()), GC3Dfloat(matrix.m42()), GC3Dfloat(matrix.m43()), GC3Dfloat(matrix.m44())
80    };
81
82    m_context->uniformMatrix4fv(location, 1, false, matrixAsFloats);
83}
84
85GC3Duint TextureMapperShaderProgram::getLocation(const AtomicString& name, VariableType type)
86{
87    HashMap<AtomicString, GC3Duint>::iterator it = m_variables.find(name);
88    if (it != m_variables.end())
89        return it->value;
90
91    GC3Duint location = 0;
92    switch (type) {
93    case UniformVariable:
94        location = m_context->getUniformLocation(m_id, name);
95        break;
96    case AttribVariable:
97        location = m_context->getAttribLocation(m_id, name);
98        break;
99    default:
100        ASSERT_NOT_REACHED();
101        break;
102    }
103
104    m_variables.add(name, location);
105    return location;
106}
107
108TextureMapperShaderProgram::~TextureMapperShaderProgram()
109{
110    Platform3DObject programID = m_id;
111    if (!programID)
112        return;
113
114    m_context->detachShader(programID, m_vertexShader);
115    m_context->deleteShader(m_vertexShader);
116    m_context->detachShader(programID, m_fragmentShader);
117    m_context->deleteShader(m_fragmentShader);
118    m_context->deleteProgram(programID);
119}
120
121#define GLSL_DIRECTIVE(...) "#"#__VA_ARGS__"\n"
122static const char* vertexTemplate =
123    STRINGIFY(
124        attribute vec4 a_vertex;
125        uniform mat4 u_modelViewMatrix;
126        uniform mat4 u_projectionMatrix;
127        uniform mat4 u_textureSpaceMatrix;
128
129        varying vec2 v_texCoord;
130        varying float v_antialias;
131
132        void noop(inout vec2 dummyParameter) { }
133
134        vec4 toViewportSpace(vec2 pos) { return vec4(pos, 0., 1.) * u_modelViewMatrix; }
135
136        // This function relies on the assumption that we get edge triangles with control points,
137        // a control point being the nearest point to the coordinate that is on the edge.
138        void applyAntialiasing(inout vec2 position)
139        {
140            // We count on the fact that quad passed in is always a unit rect,
141            // and the transformation matrix applies the real rect.
142            const vec2 center = vec2(0.5, 0.5);
143            const float antialiasInflationDistance = 1.;
144
145            // We pass the control point as the zw coordinates of the vertex.
146            // The control point is the point on the edge closest to the current position.
147            // The control point is used to compute the antialias value.
148            vec2 controlPoint = a_vertex.zw;
149
150            // First we calculate the distance in viewport space.
151            vec4 centerInViewportCoordinates = toViewportSpace(center);
152            vec4 controlPointInViewportCoordinates = toViewportSpace(controlPoint);
153            float viewportSpaceDistance = distance(centerInViewportCoordinates, controlPointInViewportCoordinates);
154
155            // We add the inflation distance to the computed distance, and compute the ratio.
156            float inflationRatio = (viewportSpaceDistance + antialiasInflationDistance) / viewportSpaceDistance;
157
158            // v_antialias needs to be 0 for the outer edge and 1. for the inner edge.
159            // Since the controlPoint is equal to the position in the edge vertices, the value is always 0 for those.
160            // For the center point, the distance is always 0.5, so we normalize to 1. by multiplying by 2.
161            // By multplying by inflationRatio and dividing by (inflationRatio - 1),
162            // We make sure that the varying interpolates between 0 (outer edge), 1 (inner edge) and n > 1 (center).
163            v_antialias = distance(controlPoint, position) * 2. * inflationRatio / (inflationRatio - 1.);
164
165            // Now inflate the actual position. By using this formula instead of inflating position directly,
166            // we ensure that the center vertex is never inflated.
167            position = center + (position - center) * inflationRatio;
168        }
169
170        void main(void)
171        {
172            vec2 position = a_vertex.xy;
173            applyAntialiasingIfNeeded(position);
174
175            // The texture position needs to be clamped to 0..1 before the texture matrix is applied.
176            vec4 clampedPosition = clamp(vec4(position, 0., 1.), 0., 1.);
177            v_texCoord = (u_textureSpaceMatrix * clampedPosition).xy;
178            gl_Position = u_projectionMatrix * u_modelViewMatrix * vec4(position, 0., 1.);
179        }
180    );
181
182#define RECT_TEXTURE_DIRECTIVE \
183    GLSL_DIRECTIVE(ifdef ENABLE_Rect) \
184        GLSL_DIRECTIVE(define SamplerType sampler2DRect) \
185        GLSL_DIRECTIVE(define SamplerFunction texture2DRect) \
186    GLSL_DIRECTIVE(else) \
187        GLSL_DIRECTIVE(define SamplerType sampler2D) \
188        GLSL_DIRECTIVE(define SamplerFunction texture2D) \
189    GLSL_DIRECTIVE(endif)
190
191#define ENABLE_APPLIER(Name) "#define ENABLE_"#Name"\n#define apply"#Name"IfNeeded apply"#Name"\n"
192#define DISABLE_APPLIER(Name) "#define apply"#Name"IfNeeded noop\n"
193#define BLUR_CONSTANTS \
194    GLSL_DIRECTIVE(define GAUSSIAN_KERNEL_HALF_WIDTH 11) \
195    GLSL_DIRECTIVE(define GAUSSIAN_KERNEL_STEP 0.2)
196
197
198static const char* fragmentTemplate =
199    RECT_TEXTURE_DIRECTIVE
200    BLUR_CONSTANTS
201    STRINGIFY(
202        precision mediump float;
203        uniform SamplerType s_sampler;
204        uniform sampler2D s_contentTexture;
205        uniform float u_opacity;
206        varying float v_antialias;
207        varying vec2 v_texCoord;
208        uniform float u_filterAmount;
209        uniform vec2 u_blurRadius;
210        uniform vec2 u_shadowOffset;
211        uniform vec4 u_color;
212        uniform float u_gaussianKernel[GAUSSIAN_KERNEL_HALF_WIDTH];
213
214        void noop(inout vec4 dummyParameter) { }
215
216        float antialias() { return smoothstep(v_antialias, 0., 1.); }
217
218        void applyTexture(inout vec4 color) { color = SamplerFunction(s_sampler, v_texCoord); }
219        void applyOpacity(inout vec4 color) { color *= u_opacity; }
220        void applyAntialiasing(inout vec4 color) { color *= antialias(); }
221
222        void applyGrayscaleFilter(inout vec4 color)
223        {
224            float amount = 1.0 - u_filterAmount;
225            color = vec4((0.2126 + 0.7874 * amount) * color.r + (0.7152 - 0.7152 * amount) * color.g + (0.0722 - 0.0722 * amount) * color.b,
226                (0.2126 - 0.2126 * amount) * color.r + (0.7152 + 0.2848 * amount) * color.g + (0.0722 - 0.0722 * amount) * color.b,
227                (0.2126 - 0.2126 * amount) * color.r + (0.7152 - 0.7152 * amount) * color.g + (0.0722 + 0.9278 * amount) * color.b,
228                color.a);
229        }
230
231        void applySepiaFilter(inout vec4 color)
232        {
233            float amount = 1.0 - u_filterAmount;
234            color = vec4((0.393 + 0.607 * amount) * color.r + (0.769 - 0.769 * amount) * color.g + (0.189 - 0.189 * amount) * color.b,
235                (0.349 - 0.349 * amount) * color.r + (0.686 + 0.314 * amount) * color.g + (0.168 - 0.168 * amount) * color.b,
236                (0.272 - 0.272 * amount) * color.r + (0.534 - 0.534 * amount) * color.g + (0.131 + 0.869 * amount) * color.b,
237                color.a);
238        }
239
240        void applySaturateFilter(inout vec4 color)
241        {
242            color = vec4((0.213 + 0.787 * u_filterAmount) * color.r + (0.715 - 0.715 * u_filterAmount) * color.g + (0.072 - 0.072 * u_filterAmount) * color.b,
243                (0.213 - 0.213 * u_filterAmount) * color.r + (0.715 + 0.285 * u_filterAmount) * color.g + (0.072 - 0.072 * u_filterAmount) * color.b,
244                (0.213 - 0.213 * u_filterAmount) * color.r + (0.715 - 0.715 * u_filterAmount) * color.g + (0.072 + 0.928 * u_filterAmount) * color.b,
245                color.a);
246        }
247
248        void applyHueRotateFilter(inout vec4 color)
249        {
250            float pi = 3.14159265358979323846;
251            float c = cos(u_filterAmount * pi / 180.0);
252            float s = sin(u_filterAmount * pi / 180.0);
253            color = 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),
254                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),
255                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),
256                color.a);
257        }
258
259        float invert(float n) { return (1.0 - n) * u_filterAmount + n * (1.0 - u_filterAmount); }
260        void applyInvertFilter(inout vec4 color)
261        {
262            color = vec4(invert(color.r), invert(color.g), invert(color.b), color.a);
263        }
264
265        void applyBrightnessFilter(inout vec4 color)
266        {
267            color = vec4(color.rgb * u_filterAmount, color.a);
268        }
269
270        float contrast(float n) { return (n - 0.5) * u_filterAmount + 0.5; }
271        void applyContrastFilter(inout vec4 color)
272        {
273            color = vec4(contrast(color.r), contrast(color.g), contrast(color.b), color.a);
274        }
275
276        void applyOpacityFilter(inout vec4 color)
277        {
278            color = vec4(color.r, color.g, color.b, color.a * u_filterAmount);
279        }
280
281        vec4 sampleColorAtRadius(float radius)
282        {
283            vec2 coord = v_texCoord + radius * u_blurRadius;
284            return SamplerFunction(s_sampler, coord) * float(coord.x > 0. && coord.y > 0. && coord.x < 1. && coord.y < 1.);
285        }
286
287        float sampleAlphaAtRadius(float radius)
288        {
289            vec2 coord = v_texCoord - u_shadowOffset + radius * u_blurRadius;
290            return SamplerFunction(s_sampler, coord).a * float(coord.x > 0. && coord.y > 0. && coord.x < 1. && coord.y < 1.);
291        }
292
293        void applyBlurFilter(inout vec4 color)
294        {
295            vec4 total = sampleColorAtRadius(0.) * u_gaussianKernel[0];
296            for (int i = 1; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) {
297                total += sampleColorAtRadius(float(i) * GAUSSIAN_KERNEL_STEP) * u_gaussianKernel[i];
298                total += sampleColorAtRadius(float(-1 * i) * GAUSSIAN_KERNEL_STEP) * u_gaussianKernel[i];
299            }
300
301            color = total;
302        }
303
304        void applyAlphaBlur(inout vec4 color)
305        {
306            float total = sampleAlphaAtRadius(0.) * u_gaussianKernel[0];
307            for (int i = 1; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) {
308                total += sampleAlphaAtRadius(float(i) * GAUSSIAN_KERNEL_STEP) * u_gaussianKernel[i];
309                total += sampleAlphaAtRadius(float(-1 * i) * GAUSSIAN_KERNEL_STEP) * u_gaussianKernel[i];
310            }
311
312            color *= total;
313        }
314
315        vec4 sourceOver(vec4 src, vec4 dst) { return src + dst * (1. - dst.a); }
316
317        void applyContentTexture(inout vec4 color)
318        {
319            vec4 contentColor = texture2D(s_contentTexture, v_texCoord);
320            color = sourceOver(contentColor, color);
321        }
322
323        void applySolidColor(inout vec4 color) { color *= u_color; }
324
325        void main(void)
326        {
327            vec4 color = vec4(1., 1., 1., 1.);
328            applyTextureIfNeeded(color);
329            applySolidColorIfNeeded(color);
330            applyAntialiasingIfNeeded(color);
331            applyOpacityIfNeeded(color);
332            applyGrayscaleFilterIfNeeded(color);
333            applySepiaFilterIfNeeded(color);
334            applySaturateFilterIfNeeded(color);
335            applyHueRotateFilterIfNeeded(color);
336            applyInvertFilterIfNeeded(color);
337            applyBrightnessFilterIfNeeded(color);
338            applyContrastFilterIfNeeded(color);
339            applyOpacityFilterIfNeeded(color);
340            applyBlurFilterIfNeeded(color);
341            applyAlphaBlurIfNeeded(color);
342            applyContentTextureIfNeeded(color);
343            gl_FragColor = color;
344        }
345    );
346
347PassRefPtr<TextureMapperShaderProgram> TextureMapperShaderProgram::create(PassRefPtr<GraphicsContext3D> context, TextureMapperShaderProgram::Options options)
348{
349    StringBuilder shaderBuilder;
350#define SET_APPLIER_FROM_OPTIONS(Applier) \
351    shaderBuilder.append(\
352        (options & TextureMapperShaderProgram::Applier) ? ENABLE_APPLIER(Applier) : DISABLE_APPLIER(Applier))
353
354    SET_APPLIER_FROM_OPTIONS(Texture);
355    SET_APPLIER_FROM_OPTIONS(Rect);
356    SET_APPLIER_FROM_OPTIONS(SolidColor);
357    SET_APPLIER_FROM_OPTIONS(Opacity);
358    SET_APPLIER_FROM_OPTIONS(Antialiasing);
359    SET_APPLIER_FROM_OPTIONS(GrayscaleFilter);
360    SET_APPLIER_FROM_OPTIONS(SepiaFilter);
361    SET_APPLIER_FROM_OPTIONS(SaturateFilter);
362    SET_APPLIER_FROM_OPTIONS(HueRotateFilter);
363    SET_APPLIER_FROM_OPTIONS(BrightnessFilter);
364    SET_APPLIER_FROM_OPTIONS(ContrastFilter);
365    SET_APPLIER_FROM_OPTIONS(InvertFilter);
366    SET_APPLIER_FROM_OPTIONS(OpacityFilter);
367    SET_APPLIER_FROM_OPTIONS(BlurFilter);
368    SET_APPLIER_FROM_OPTIONS(AlphaBlur);
369    SET_APPLIER_FROM_OPTIONS(ContentTexture);
370    StringBuilder vertexBuilder;
371    vertexBuilder.append(shaderBuilder.toString());
372    vertexBuilder.append(vertexTemplate);
373    shaderBuilder.append(fragmentTemplate);
374
375    String vertexSource = vertexBuilder.toString();
376    String fragmentSource = shaderBuilder.toString();
377
378    return adoptRef(new TextureMapperShaderProgram(context, vertexSource, fragmentSource));
379}
380
381}
382#endif
383