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(TEXTURE_MAPPER_GL)
26
27#include "GLContext.h"
28#include "Logging.h"
29#include "TextureMapperGL.h"
30#include <wtf/text/StringBuilder.h>
31
32namespace WebCore {
33
34static inline bool compositingLogEnabled()
35{
36#if !LOG_DISABLED
37 return LogCompositing.state == WTFLogChannelState::On;
38#else
39 return false;
40#endif
41}
42
43#define STRINGIFY(...) #__VA_ARGS__
44
45#define GLSL_DIRECTIVE(...) "#"#__VA_ARGS__"\n"
46
47#define TEXTURE_SPACE_MATRIX_PRECISION_DIRECTIVE \
48 GLSL_DIRECTIVE(ifdef GL_FRAGMENT_PRECISION_HIGH) \
49 GLSL_DIRECTIVE(define TextureSpaceMatrixPrecision highp) \
50 GLSL_DIRECTIVE(else) \
51 GLSL_DIRECTIVE(define TextureSpaceMatrixPrecision mediump) \
52 GLSL_DIRECTIVE(endif)
53
54
55// Input/output variables definition for both GLES and OpenGL < 3.2.
56// The default precision directive is only needed for GLES.
57static const char* vertexTemplateLT320Vars =
58#if USE(OPENGL_ES)
59 TEXTURE_SPACE_MATRIX_PRECISION_DIRECTIVE
60#endif
61#if USE(OPENGL_ES)
62 STRINGIFY(
63 precision TextureSpaceMatrixPrecision float;
64 )
65#endif
66 STRINGIFY(
67 attribute vec4 a_vertex;
68 varying vec2 v_texCoord;
69 varying vec2 v_transformedTexCoord;
70 varying float v_antialias;
71 );
72
73#if !USE(OPENGL_ES)
74// Input/output variables definition for OpenGL >= 3.2.
75static const char* vertexTemplateGE320Vars =
76 STRINGIFY(
77 in vec4 a_vertex;
78 out vec2 v_texCoord;
79 out vec2 v_transformedTexCoord;
80 out float v_antialias;
81 );
82#endif
83
84static const char* vertexTemplateCommon =
85 STRINGIFY(
86 uniform mat4 u_modelViewMatrix;
87 uniform mat4 u_projectionMatrix;
88 uniform mat4 u_textureSpaceMatrix;
89
90 void noop(inout vec2 dummyParameter) { }
91
92 vec4 toViewportSpace(vec2 pos) { return vec4(pos, 0., 1.) * u_modelViewMatrix; }
93
94 // This function relies on the assumption that we get edge triangles with control points,
95 // a control point being the nearest point to the coordinate that is on the edge.
96 void applyAntialiasing(inout vec2 position)
97 {
98 // We count on the fact that quad passed in is always a unit rect,
99 // and the transformation matrix applies the real rect.
100 const vec2 center = vec2(0.5, 0.5);
101 const float antialiasInflationDistance = 1.;
102
103 // We pass the control point as the zw coordinates of the vertex.
104 // The control point is the point on the edge closest to the current position.
105 // The control point is used to compute the antialias value.
106 vec2 controlPoint = a_vertex.zw;
107
108 // First we calculate the distance in viewport space.
109 vec4 centerInViewportCoordinates = toViewportSpace(center);
110 vec4 controlPointInViewportCoordinates = toViewportSpace(controlPoint);
111 float viewportSpaceDistance = distance(centerInViewportCoordinates, controlPointInViewportCoordinates);
112
113 // We add the inflation distance to the computed distance, and compute the ratio.
114 float inflationRatio = (viewportSpaceDistance + antialiasInflationDistance) / viewportSpaceDistance;
115
116 // v_antialias needs to be 0 for the outer edge and 1. for the inner edge.
117 // Since the controlPoint is equal to the position in the edge vertices, the value is always 0 for those.
118 // For the center point, the distance is always 0.5, so we normalize to 1. by multiplying by 2.
119 // By multplying by inflationRatio and dividing by (inflationRatio - 1),
120 // We make sure that the varying interpolates between 0 (outer edge), 1 (inner edge) and n > 1 (center).
121 v_antialias = distance(controlPoint, position) * 2. * inflationRatio / (inflationRatio - 1.);
122
123 // Now inflate the actual position. By using this formula instead of inflating position directly,
124 // we ensure that the center vertex is never inflated.
125 position = center + (position - center) * inflationRatio;
126 }
127
128 void main(void)
129 {
130 vec2 position = a_vertex.xy;
131 applyAntialiasingIfNeeded(position);
132
133 v_texCoord = position;
134 vec4 clampedPosition = clamp(vec4(position, 0., 1.), 0., 1.);
135 v_transformedTexCoord = (u_textureSpaceMatrix * clampedPosition).xy;
136 gl_Position = u_projectionMatrix * u_modelViewMatrix * vec4(position, 0., 1.);
137 }
138 );
139
140#define RECT_TEXTURE_DIRECTIVE \
141 GLSL_DIRECTIVE(ifdef ENABLE_Rect) \
142 GLSL_DIRECTIVE(define SamplerType sampler2DRect) \
143 GLSL_DIRECTIVE(define SamplerFunction texture2DRect) \
144 GLSL_DIRECTIVE(else) \
145 GLSL_DIRECTIVE(define SamplerType sampler2D) \
146 GLSL_DIRECTIVE(define SamplerFunction texture2D) \
147 GLSL_DIRECTIVE(endif)
148
149#define ANTIALIASING_TEX_COORD_DIRECTIVE \
150 GLSL_DIRECTIVE(if defined(ENABLE_Antialiasing) && defined(ENABLE_Texture)) \
151 GLSL_DIRECTIVE(define transformTexCoord fragmentTransformTexCoord) \
152 GLSL_DIRECTIVE(else) \
153 GLSL_DIRECTIVE(define transformTexCoord vertexTransformTexCoord) \
154 GLSL_DIRECTIVE(endif)
155
156#define ENABLE_APPLIER(Name) "#define ENABLE_"#Name"\n#define apply"#Name"IfNeeded apply"#Name"\n"
157#define DISABLE_APPLIER(Name) "#define apply"#Name"IfNeeded noop\n"
158#define BLUR_CONSTANTS \
159 GLSL_DIRECTIVE(define GAUSSIAN_KERNEL_HALF_WIDTH 11) \
160 GLSL_DIRECTIVE(define GAUSSIAN_KERNEL_STEP 0.2)
161
162
163// Common header for all versions. We define the matrices variables here to keep the precision
164// directives scope: the first one applies to the matrices variables and the next one to the
165// rest of them. The precision is only used in GLES.
166static const char* fragmentTemplateHeaderCommon =
167 RECT_TEXTURE_DIRECTIVE
168 ANTIALIASING_TEX_COORD_DIRECTIVE
169 BLUR_CONSTANTS
170#if USE(OPENGL_ES)
171 TEXTURE_SPACE_MATRIX_PRECISION_DIRECTIVE
172#endif
173#if USE(OPENGL_ES)
174 STRINGIFY(
175 precision TextureSpaceMatrixPrecision float;
176 )
177#endif
178 STRINGIFY(
179 uniform mat4 u_textureSpaceMatrix;
180 uniform mat4 u_textureColorSpaceMatrix;
181 )
182#if USE(OPENGL_ES)
183 STRINGIFY(
184 precision mediump float;
185 )
186#endif
187 ;
188
189// Input/output variables definition for both GLES and OpenGL < 3.2.
190static const char* fragmentTemplateLT320Vars =
191 STRINGIFY(
192 varying float v_antialias;
193 varying vec2 v_texCoord;
194 varying vec2 v_transformedTexCoord;
195 );
196
197#if !USE(OPENGL_ES)
198// Input/output variables definition for OpenGL >= 3.2.
199static const char* fragmentTemplateGE320Vars =
200 STRINGIFY(
201 in float v_antialias;
202 in vec2 v_texCoord;
203 in vec2 v_transformedTexCoord;
204 );
205#endif
206
207static const char* fragmentTemplateCommon =
208 STRINGIFY(
209 uniform SamplerType s_sampler;
210 uniform sampler2D s_contentTexture;
211 uniform float u_opacity;
212 uniform float u_filterAmount;
213 uniform vec2 u_blurRadius;
214 uniform vec2 u_shadowOffset;
215 uniform vec4 u_color;
216 uniform float u_gaussianKernel[GAUSSIAN_KERNEL_HALF_WIDTH];
217
218 void noop(inout vec4 dummyParameter) { }
219 void noop(inout vec4 dummyParameter, vec2 texCoord) { }
220 void noop(inout vec2 dummyParameter) { }
221
222 float antialias() { return smoothstep(0., 1., v_antialias); }
223
224 vec2 fragmentTransformTexCoord()
225 {
226 vec4 clampedPosition = clamp(vec4(v_texCoord, 0., 1.), 0., 1.);
227 return (u_textureSpaceMatrix * clampedPosition).xy;
228 }
229
230 vec2 vertexTransformTexCoord() { return v_transformedTexCoord; }
231
232 void applyManualRepeat(inout vec2 pos) { pos = fract(pos); }
233
234 void applyTexture(inout vec4 color, vec2 texCoord) { color = u_textureColorSpaceMatrix * SamplerFunction(s_sampler, texCoord); }
235 void applyOpacity(inout vec4 color) { color *= u_opacity; }
236 void applyAntialiasing(inout vec4 color) { color *= antialias(); }
237
238 void applyGrayscaleFilter(inout vec4 color)
239 {
240 float amount = 1.0 - u_filterAmount;
241 color = vec4((0.2126 + 0.7874 * amount) * color.r + (0.7152 - 0.7152 * amount) * color.g + (0.0722 - 0.0722 * amount) * color.b,
242 (0.2126 - 0.2126 * amount) * color.r + (0.7152 + 0.2848 * amount) * color.g + (0.0722 - 0.0722 * amount) * color.b,
243 (0.2126 - 0.2126 * amount) * color.r + (0.7152 - 0.7152 * amount) * color.g + (0.0722 + 0.9278 * amount) * color.b,
244 color.a);
245 }
246
247 void applySepiaFilter(inout vec4 color)
248 {
249 float amount = 1.0 - u_filterAmount;
250 color = vec4((0.393 + 0.607 * amount) * color.r + (0.769 - 0.769 * amount) * color.g + (0.189 - 0.189 * amount) * color.b,
251 (0.349 - 0.349 * amount) * color.r + (0.686 + 0.314 * amount) * color.g + (0.168 - 0.168 * amount) * color.b,
252 (0.272 - 0.272 * amount) * color.r + (0.534 - 0.534 * amount) * color.g + (0.131 + 0.869 * amount) * color.b,
253 color.a);
254 }
255
256 void applySaturateFilter(inout vec4 color)
257 {
258 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,
259 (0.213 - 0.213 * u_filterAmount) * color.r + (0.715 + 0.285 * u_filterAmount) * color.g + (0.072 - 0.072 * u_filterAmount) * color.b,
260 (0.213 - 0.213 * u_filterAmount) * color.r + (0.715 - 0.715 * u_filterAmount) * color.g + (0.072 + 0.928 * u_filterAmount) * color.b,
261 color.a);
262 }
263
264 void applyHueRotateFilter(inout vec4 color)
265 {
266 float pi = 3.14159265358979323846;
267 float c = cos(u_filterAmount * pi / 180.0);
268 float s = sin(u_filterAmount * pi / 180.0);
269 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),
270 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),
271 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),
272 color.a);
273 }
274
275 float invert(float n) { return (1.0 - n) * u_filterAmount + n * (1.0 - u_filterAmount); }
276 void applyInvertFilter(inout vec4 color)
277 {
278 color = vec4(invert(color.r), invert(color.g), invert(color.b), color.a);
279 }
280
281 void applyBrightnessFilter(inout vec4 color)
282 {
283 color = vec4(color.rgb * u_filterAmount, color.a);
284 }
285
286 float contrast(float n) { return (n - 0.5) * u_filterAmount + 0.5; }
287 void applyContrastFilter(inout vec4 color)
288 {
289 color = vec4(contrast(color.r), contrast(color.g), contrast(color.b), color.a);
290 }
291
292 void applyOpacityFilter(inout vec4 color)
293 {
294 color = vec4(color.r, color.g, color.b, color.a * u_filterAmount);
295 }
296
297 vec4 sampleColorAtRadius(float radius, vec2 texCoord)
298 {
299 vec2 coord = texCoord + radius * u_blurRadius;
300 return SamplerFunction(s_sampler, coord) * float(coord.x > 0. && coord.y > 0. && coord.x < 1. && coord.y < 1.);
301 }
302
303 float sampleAlphaAtRadius(float radius, vec2 texCoord)
304 {
305 vec2 coord = texCoord - u_shadowOffset + radius * u_blurRadius;
306 return SamplerFunction(s_sampler, coord).a * float(coord.x > 0. && coord.y > 0. && coord.x < 1. && coord.y < 1.);
307 }
308
309 void applyBlurFilter(inout vec4 color, vec2 texCoord)
310 {
311 vec4 total = sampleColorAtRadius(0., texCoord) * u_gaussianKernel[0];
312 for (int i = 1; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) {
313 total += sampleColorAtRadius(float(i) * GAUSSIAN_KERNEL_STEP, texCoord) * u_gaussianKernel[i];
314 total += sampleColorAtRadius(float(-1 * i) * GAUSSIAN_KERNEL_STEP, texCoord) * u_gaussianKernel[i];
315 }
316
317 color = total;
318 }
319
320 void applyAlphaBlur(inout vec4 color, vec2 texCoord)
321 {
322 float total = sampleAlphaAtRadius(0., texCoord) * u_gaussianKernel[0];
323 for (int i = 1; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) {
324 total += sampleAlphaAtRadius(float(i) * GAUSSIAN_KERNEL_STEP, texCoord) * u_gaussianKernel[i];
325 total += sampleAlphaAtRadius(float(-1 * i) * GAUSSIAN_KERNEL_STEP, texCoord) * u_gaussianKernel[i];
326 }
327
328 color *= total;
329 }
330
331 vec4 sourceOver(vec4 src, vec4 dst) { return src + dst * (1. - dst.a); }
332
333 void applyContentTexture(inout vec4 color, vec2 texCoord)
334 {
335 vec4 contentColor = texture2D(s_contentTexture, texCoord);
336 color = sourceOver(contentColor, color);
337 }
338
339 void applySolidColor(inout vec4 color) { color *= u_color; }
340
341 void main(void)
342 {
343 vec4 color = vec4(1., 1., 1., 1.);
344 vec2 texCoord = transformTexCoord();
345 applyManualRepeatIfNeeded(texCoord);
346 applyTextureIfNeeded(color, texCoord);
347 applySolidColorIfNeeded(color);
348 applyAntialiasingIfNeeded(color);
349 applyOpacityIfNeeded(color);
350 applyGrayscaleFilterIfNeeded(color);
351 applySepiaFilterIfNeeded(color);
352 applySaturateFilterIfNeeded(color);
353 applyHueRotateFilterIfNeeded(color);
354 applyInvertFilterIfNeeded(color);
355 applyBrightnessFilterIfNeeded(color);
356 applyContrastFilterIfNeeded(color);
357 applyOpacityFilterIfNeeded(color);
358 applyBlurFilterIfNeeded(color, texCoord);
359 applyAlphaBlurIfNeeded(color, texCoord);
360 applyContentTextureIfNeeded(color, texCoord);
361 gl_FragColor = color;
362 }
363 );
364
365Ref<TextureMapperShaderProgram> TextureMapperShaderProgram::create(TextureMapperShaderProgram::Options options)
366{
367#define SET_APPLIER_FROM_OPTIONS(Applier) \
368 optionsApplierBuilder.append(\
369 (options & TextureMapperShaderProgram::Applier) ? ENABLE_APPLIER(Applier) : DISABLE_APPLIER(Applier))
370
371 StringBuilder optionsApplierBuilder;
372 SET_APPLIER_FROM_OPTIONS(Texture);
373 SET_APPLIER_FROM_OPTIONS(Rect);
374 SET_APPLIER_FROM_OPTIONS(SolidColor);
375 SET_APPLIER_FROM_OPTIONS(Opacity);
376 SET_APPLIER_FROM_OPTIONS(Antialiasing);
377 SET_APPLIER_FROM_OPTIONS(GrayscaleFilter);
378 SET_APPLIER_FROM_OPTIONS(SepiaFilter);
379 SET_APPLIER_FROM_OPTIONS(SaturateFilter);
380 SET_APPLIER_FROM_OPTIONS(HueRotateFilter);
381 SET_APPLIER_FROM_OPTIONS(BrightnessFilter);
382 SET_APPLIER_FROM_OPTIONS(ContrastFilter);
383 SET_APPLIER_FROM_OPTIONS(InvertFilter);
384 SET_APPLIER_FROM_OPTIONS(OpacityFilter);
385 SET_APPLIER_FROM_OPTIONS(BlurFilter);
386 SET_APPLIER_FROM_OPTIONS(AlphaBlur);
387 SET_APPLIER_FROM_OPTIONS(ContentTexture);
388 SET_APPLIER_FROM_OPTIONS(ManualRepeat);
389
390 StringBuilder vertexShaderBuilder;
391
392 // OpenGL >= 3.2 requires a #version directive at the beginning of the code.
393#if !USE(OPENGL_ES)
394 unsigned glVersion = GLContext::current()->version();
395 if (glVersion >= 320)
396 vertexShaderBuilder.append(GLSL_DIRECTIVE(version 150));
397#endif
398
399 // Append the options.
400 vertexShaderBuilder.append(optionsApplierBuilder.toString());
401
402 // Append the appropriate input/output variable definitions.
403#if USE(OPENGL_ES)
404 vertexShaderBuilder.append(vertexTemplateLT320Vars);
405#else
406 if (glVersion >= 320)
407 vertexShaderBuilder.append(vertexTemplateGE320Vars);
408 else
409 vertexShaderBuilder.append(vertexTemplateLT320Vars);
410#endif
411
412 // Append the common code.
413 vertexShaderBuilder.append(vertexTemplateCommon);
414
415 StringBuilder fragmentShaderBuilder;
416
417 // OpenGL >= 3.2 requires a #version directive at the beginning of the code.
418#if !USE(OPENGL_ES)
419 if (glVersion >= 320)
420 fragmentShaderBuilder.append(GLSL_DIRECTIVE(version 150));
421#endif
422
423 // Append the options.
424 fragmentShaderBuilder.append(optionsApplierBuilder.toString());
425
426 // Append the common header.
427 fragmentShaderBuilder.append(fragmentTemplateHeaderCommon);
428
429 // Append the appropriate input/output variable definitions.
430#if USE(OPENGL_ES)
431 fragmentShaderBuilder.append(fragmentTemplateLT320Vars);
432#else
433 if (glVersion >= 320)
434 fragmentShaderBuilder.append(fragmentTemplateGE320Vars);
435 else
436 fragmentShaderBuilder.append(fragmentTemplateLT320Vars);
437#endif
438
439 // Append the common code.
440 fragmentShaderBuilder.append(fragmentTemplateCommon);
441
442 return adoptRef(*new TextureMapperShaderProgram(vertexShaderBuilder.toString(), fragmentShaderBuilder.toString()));
443}
444
445#if !LOG_DISABLED
446static CString getShaderLog(GLuint shader)
447{
448 GLint logLength = 0;
449 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
450 if (!logLength)
451 return { };
452
453 Vector<GLchar> info(logLength);
454 GLsizei infoLength = 0;
455 glGetShaderInfoLog(shader, logLength, &infoLength, info.data());
456
457 size_t stringLength = std::max(infoLength, 0);
458 return { info.data(), stringLength };
459}
460
461static CString getProgramLog(GLuint program)
462{
463 GLint logLength = 0;
464 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
465 if (!logLength)
466 return { };
467
468 Vector<GLchar> info(logLength);
469 GLsizei infoLength = 0;
470 glGetProgramInfoLog(program, logLength, &infoLength, info.data());
471
472 size_t stringLength = std::max(infoLength, 0);
473 return { info.data(), stringLength };
474}
475#endif
476
477TextureMapperShaderProgram::TextureMapperShaderProgram(const String& vertex, const String& fragment)
478{
479 m_vertexShader = glCreateShader(GL_VERTEX_SHADER);
480 {
481 CString vertexCString = vertex.utf8();
482 const char* data = vertexCString.data();
483 int length = vertexCString.length();
484 glShaderSource(m_vertexShader, 1, &data, &length);
485 }
486 glCompileShader(m_vertexShader);
487
488 m_fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
489 {
490 CString fragmentCString = fragment.utf8();
491 const char* data = fragmentCString.data();
492 int length = fragmentCString.length();
493 glShaderSource(m_fragmentShader, 1, &data, &length);
494 }
495 glCompileShader(m_fragmentShader);
496
497 m_id = glCreateProgram();
498 glAttachShader(m_id, m_vertexShader);
499 glAttachShader(m_id, m_fragmentShader);
500 glLinkProgram(m_id);
501
502 if (!compositingLogEnabled() || glGetError() == GL_NO_ERROR)
503 return;
504
505 LOG(Compositing, "Vertex shader log: %s\n", getShaderLog(m_vertexShader).data());
506 LOG(Compositing, "Fragment shader log: %s\n", getShaderLog(m_fragmentShader).data());
507 LOG(Compositing, "Program log: %s\n", getProgramLog(m_id).data());
508}
509
510TextureMapperShaderProgram::~TextureMapperShaderProgram()
511{
512 if (!m_id)
513 return;
514
515 glDetachShader(m_id, m_vertexShader);
516 glDeleteShader(m_vertexShader);
517 glDetachShader(m_id, m_fragmentShader);
518 glDeleteShader(m_fragmentShader);
519 glDeleteProgram(m_id);
520}
521
522void TextureMapperShaderProgram::setMatrix(GLuint location, const TransformationMatrix& matrix)
523{
524 auto floatMatrix = matrix.toColumnMajorFloatArray();
525 glUniformMatrix4fv(location, 1, false, floatMatrix.data());
526}
527
528GLuint TextureMapperShaderProgram::getLocation(const AtomicString& name, VariableType type)
529{
530 auto addResult = m_variables.ensure(name,
531 [this, &name, type] {
532 CString nameCString = name.string().utf8();
533 switch (type) {
534 case UniformVariable:
535 return glGetUniformLocation(m_id, nameCString.data());
536 case AttribVariable:
537 return glGetAttribLocation(m_id, nameCString.data());
538 }
539 ASSERT_NOT_REACHED();
540 return 0;
541 });
542 return addResult.iterator->value;
543}
544
545} // namespace WebCore
546
547#endif // USE(TEXTURE_MAPPER_GL)
548