1/*
2 * Copyright (c) 2010, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32
33#if ENABLE(ACCELERATED_2D_CANVAS)
34
35#include "Shader.h"
36
37#include "AffineTransform.h"
38#include "GraphicsContext3D.h"
39
40#include <wtf/text/CString.h>
41#include <wtf/text/StringBuilder.h>
42
43namespace WebCore {
44
45// static
46void Shader::affineTo3x3(const AffineTransform& transform, float mat[9])
47{
48    mat[0] = transform.a();
49    mat[1] = transform.b();
50    mat[2] = 0.0f;
51    mat[3] = transform.c();
52    mat[4] = transform.d();
53    mat[5] = 0.0f;
54    mat[6] = transform.e();
55    mat[7] = transform.f();
56    mat[8] = 1.0f;
57}
58
59// static
60void Shader::affineTo4x4(const AffineTransform& transform, float mat[16])
61{
62    mat[0] = transform.a();
63    mat[1] = transform.b();
64    mat[2] = 0.0f;
65    mat[3] = 0.0f;
66    mat[4] = transform.c();
67    mat[5] = transform.d();
68    mat[6] = 0.0f;
69    mat[7] = 0.0f;
70    mat[8] = 0.0f;
71    mat[9] = 0.0f;
72    mat[10] = 1.0f;
73    mat[11] = 0.0f;
74    mat[12] = transform.e();
75    mat[13] = transform.f();
76    mat[14] = 0.0f;
77    mat[15] = 1.0f;
78}
79
80// static
81unsigned Shader::loadShader(GraphicsContext3D* context, unsigned type, const String& shaderSource)
82{
83    unsigned shader = context->createShader(type);
84    if (!shader)
85        return 0;
86
87    context->shaderSource(shader, shaderSource);
88    context->compileShader(shader);
89    int compileStatus = 0;
90    context->getShaderiv(shader, GraphicsContext3D::COMPILE_STATUS, &compileStatus);
91    if (!compileStatus) {
92        String infoLog = context->getShaderInfoLog(shader);
93        LOG_ERROR("%s", infoLog.utf8().data());
94        context->deleteShader(shader);
95        return 0;
96    }
97    return shader;
98}
99
100// static
101unsigned Shader::loadProgram(GraphicsContext3D* context, const String& vertexShaderSource, const String& fragmentShaderSource)
102{
103    unsigned vertexShader = loadShader(context, GraphicsContext3D::VERTEX_SHADER, vertexShaderSource);
104    if (!vertexShader)
105        return 0;
106    unsigned fragmentShader = loadShader(context, GraphicsContext3D::FRAGMENT_SHADER, fragmentShaderSource);
107    if (!fragmentShader)
108        return 0;
109    unsigned program = context->createProgram();
110    if (!program)
111        return 0;
112    context->attachShader(program, vertexShader);
113    context->attachShader(program, fragmentShader);
114    context->linkProgram(program);
115    int linkStatus = 0;
116    context->getProgramiv(program, GraphicsContext3D::LINK_STATUS, &linkStatus);
117    if (!linkStatus)
118        context->deleteProgram(program);
119    context->deleteShader(vertexShader);
120    context->deleteShader(fragmentShader);
121    return program;
122}
123
124Shader::Shader(GraphicsContext3D* context, unsigned program)
125    : m_context(context)
126    , m_program(program)
127{
128}
129
130Shader::~Shader()
131{
132    m_context->deleteProgram(m_program);
133}
134
135// static
136String Shader::generateVertex(Shader::VertexType vertexType, Shader::FillType fillType)
137{
138    StringBuilder builder;
139    switch (vertexType) {
140    case TwoDimensional:
141        builder.append(
142            "uniform mat3 matrix;\n"
143            "attribute vec2 position;\n");
144        break;
145    case LoopBlinnInterior:
146        builder.append(
147            "uniform mat4 worldViewProjection;\n"
148            "attribute vec2 position;\n");
149        break;
150    case LoopBlinnExterior:
151        builder.append(
152            "uniform mat4 worldViewProjection;\n"
153            "attribute vec2 position;\n"
154            "attribute vec3 klm;\n"
155            "varying vec3 v_klm;\n");
156        break;
157    }
158
159    if (fillType == TextureFill) {
160        builder.append(
161            "uniform mat3 texMatrix;\n"
162            "varying vec3 texCoord;\n");
163    }
164
165    builder.append(
166        "void main() {\n");
167
168    if (vertexType == TwoDimensional) {
169        builder.append(
170            "gl_Position = vec4(matrix * vec3(position, 1.0), 1.0);\n");
171    } else {
172        builder.append(
173            "gl_Position = worldViewProjection * vec4(position, 0.0, 1.0);\n");
174        if (vertexType == LoopBlinnExterior) {
175            builder.append(
176                "v_klm = klm;\n");
177        }
178    }
179
180    if (fillType == TextureFill) {
181        builder.append(
182            "texCoord = texMatrix * vec3(position, 1.0);\n");
183    }
184
185    builder.append(
186        "}\n");
187
188    return builder.toString();
189}
190
191// static
192String Shader::generateFragment(Shader::VertexType vertexType, Shader::FillType fillType, Shader::AntialiasType antialiasType)
193{
194    StringBuilder builder;
195    builder.append(
196        "#ifdef GL_ES\n"
197        "precision mediump float;\n"
198        "#endif\n");
199
200    if (vertexType == LoopBlinnExterior) {
201        if (antialiasType == Antialiased) {
202            builder.append(
203                "#extension GL_OES_standard_derivatives : enable\n");
204        }
205        builder.append(
206            "varying vec3 v_klm;\n");
207    }
208
209    switch (fillType) {
210    case SolidFill:
211        builder.append(
212            "uniform vec4 color;\n");
213        break;
214    case TextureFill:
215        builder.append(
216            "uniform sampler2D sampler;\n"
217            "uniform float globalAlpha;\n"
218            "varying vec3 texCoord;\n");
219        break;
220    }
221
222    builder.append(
223        "void main() {\n");
224
225    if (vertexType != LoopBlinnExterior) {
226        builder.append(
227            "float alpha = 1.0;\n");
228    } else {
229        if (antialiasType == Antialiased) {
230            builder.append(
231                "  // Gradients\n"
232                "  vec3 px = dFdx(v_klm);\n"
233                "  vec3 py = dFdy(v_klm);\n"
234                "\n"
235                "  // Chain rule\n"
236                "  float k2 = v_klm.x * v_klm.x;\n"
237                "  float c = k2 * v_klm.x - v_klm.y * v_klm.z;\n"
238                "  float k23 = 3.0 * k2;\n"
239                "  float cx = k23 * px.x - v_klm.z * px.y - v_klm.y * px.z;\n"
240                "  float cy = k23 * py.x - v_klm.z * py.y - v_klm.y * py.z;\n"
241                "\n"
242                "  // Signed distance\n"
243                "  float sd = c / sqrt(cx * cx + cy * cy);\n"
244                "\n"
245                "  // Linear alpha\n"
246                "  // FIXME: figure out why this needs to be\n"
247                "  // negated compared to the HLSL version, and also why\n"
248                "  // we need an adjustment by +1.0 for it to look good.\n"
249                "  // float alpha = clamp(0.5 - sd, 0.0, 1.0);\n"
250                "  float alpha = clamp(sd + 0.5, 0.0, 1.0);\n");
251        } else {
252            builder.append(
253                "  float t = v_klm.x * v_klm.x * v_klm.x - v_klm.y * v_klm.z;\n"
254                "  float alpha = clamp(sign(t), 0.0, 1.0);\n");
255        }
256    }
257
258    switch (fillType) {
259    case SolidFill:
260        builder.append(
261            "gl_FragColor = color * alpha;\n");
262        break;
263    case TextureFill:
264        builder.append(
265            "gl_FragColor = texture2D(sampler, texCoord.xy) * alpha * globalAlpha;\n");
266        break;
267    }
268
269    builder.append(
270        "}\n");
271
272    return builder.toString();
273}
274
275} // namespace WebCore
276
277#endif
278