1/*
2 * Copyright (C) 2012 Adobe Systems Incorporated. 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
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above
9 *    copyright notice, this list of conditions and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above
12 *    copyright notice, this list of conditions and the following
13 *    disclaimer in the documentation and/or other materials
14 *    provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER âAS ISâ AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
26 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include "config.h"
31
32#include "core/platform/graphics/filters/custom/CustomFilterValidatedProgram.h"
33
34#include "core/platform/NotImplemented.h"
35#include "core/platform/graphics/ANGLEWebKitBridge.h"
36#include "core/platform/graphics/filters/custom/CustomFilterConstants.h"
37#include "core/platform/graphics/filters/custom/CustomFilterGlobalContext.h"
38#include "core/platform/graphics/filters/custom/CustomFilterProgramInfo.h"
39#include "wtf/HashMap.h"
40#include "wtf/text/StringBuilder.h"
41#include "wtf/text/StringHash.h"
42#include "wtf/Vector.h"
43
44namespace WebCore {
45
46#define SHADER(Src) (#Src)
47
48typedef HashMap<String, ShDataType> SymbolNameToTypeMap;
49
50static SymbolNameToTypeMap* builtInAttributeNameToTypeMap()
51{
52    static SymbolNameToTypeMap* nameToTypeMap = 0;
53    if (!nameToTypeMap) {
54        nameToTypeMap = new SymbolNameToTypeMap;
55        nameToTypeMap->set("a_meshCoord", SH_FLOAT_VEC2);
56        nameToTypeMap->set("a_position", SH_FLOAT_VEC4);
57        nameToTypeMap->set("a_texCoord", SH_FLOAT_VEC2);
58        nameToTypeMap->set("a_triangleCoord", SH_FLOAT_VEC3);
59    }
60    return nameToTypeMap;
61}
62
63static SymbolNameToTypeMap* builtInUniformNameToTypeMap()
64{
65    static SymbolNameToTypeMap* nameToTypeMap = 0;
66    if (!nameToTypeMap) {
67        nameToTypeMap = new SymbolNameToTypeMap;
68        nameToTypeMap->set("u_meshBox", SH_FLOAT_VEC4);
69        nameToTypeMap->set("u_meshSize", SH_FLOAT_VEC2);
70        nameToTypeMap->set("u_projectionMatrix", SH_FLOAT_MAT4);
71        nameToTypeMap->set("u_textureSize", SH_FLOAT_VEC2);
72        nameToTypeMap->set("u_tileSize", SH_FLOAT_VEC2);
73    }
74    return nameToTypeMap;
75}
76
77static bool validateSymbols(const Vector<ANGLEShaderSymbol>& symbols, CustomFilterMeshType meshType)
78{
79    for (size_t i = 0; i < symbols.size(); ++i) {
80        const ANGLEShaderSymbol& symbol = symbols[i];
81        switch (symbol.symbolType) {
82        case SHADER_SYMBOL_TYPE_ATTRIBUTE: {
83            SymbolNameToTypeMap* attributeNameToTypeMap = builtInAttributeNameToTypeMap();
84            SymbolNameToTypeMap::iterator builtInAttribute = attributeNameToTypeMap->find(symbol.name);
85            if (builtInAttribute == attributeNameToTypeMap->end()) {
86                // The author defined a custom attribute.
87                // FIXME: Report the validation error.
88                // https://bugs.webkit.org/show_bug.cgi?id=74416
89                return false;
90            }
91            if (meshType == MeshTypeAttached && symbol.name == "a_triangleCoord") {
92                // a_triangleCoord is only available for detached meshes.
93                // FIXME: Report the validation error.
94                // https://bugs.webkit.org/show_bug.cgi?id=74416
95                return false;
96            }
97            if (symbol.dataType != builtInAttribute->value) {
98                // The author defined one of the built-in attributes with the wrong type.
99                // FIXME: Report the validation error.
100                // https://bugs.webkit.org/show_bug.cgi?id=74416
101                return false;
102            }
103            break;
104        }
105        case SHADER_SYMBOL_TYPE_UNIFORM: {
106            if (symbol.isSampler()) {
107                // FIXME: For now, we restrict shaders with any sampler defined.
108                // When we implement texture parameters, we will allow shaders whose samplers are bound to valid textures.
109                // We must not allow OpenGL to give unbound samplers a default value of 0 because that references the element texture,
110                // which should be inaccessible to the author's shader code.
111                // https://bugs.webkit.org/show_bug.cgi?id=96230
112                return false;
113            }
114
115            SymbolNameToTypeMap* uniformNameToTypeMap = builtInUniformNameToTypeMap();
116            SymbolNameToTypeMap::iterator builtInUniform = uniformNameToTypeMap->find(symbol.name);
117            if (builtInUniform != uniformNameToTypeMap->end() && (symbol.isArray || symbol.dataType != builtInUniform->value)) {
118                // The author defined one of the built-in uniforms with the wrong type.
119                // FIXME: Report the validation error.
120                // https://bugs.webkit.org/show_bug.cgi?id=74416
121                return false;
122            }
123            break;
124        }
125        default:
126            ASSERT_NOT_REACHED();
127            break;
128        }
129    }
130
131    return true;
132}
133
134String CustomFilterValidatedProgram::defaultVertexShaderString()
135{
136    DEFINE_STATIC_LOCAL(String, vertexShaderString, (SHADER(
137        attribute mediump vec4 a_position;
138        uniform mediump mat4 u_projectionMatrix;
139
140        void main()
141        {
142            gl_Position = u_projectionMatrix * a_position;
143        }
144    )));
145    return vertexShaderString;
146}
147
148String CustomFilterValidatedProgram::defaultFragmentShaderString()
149{
150    DEFINE_STATIC_LOCAL(String, fragmentShaderString, (SHADER(
151        void main()
152        {
153        }
154    )));
155    return fragmentShaderString;
156}
157
158CustomFilterValidatedProgram::CustomFilterValidatedProgram(CustomFilterGlobalContext* globalContext, const CustomFilterProgramInfo& programInfo)
159    : m_globalContext(globalContext)
160    , m_programInfo(programInfo)
161    , m_isInitialized(false)
162{
163    platformInit();
164
165    String originalVertexShader = programInfo.vertexShaderString();
166    if (originalVertexShader.isNull())
167        originalVertexShader = defaultVertexShaderString();
168
169    String originalFragmentShader = programInfo.fragmentShaderString();
170    if (originalFragmentShader.isNull())
171        originalFragmentShader = defaultFragmentShaderString();
172
173    // Shaders referenced from the CSS mix function use a different validator than regular WebGL shaders. See core/platform/graphics/filters/custom/CustomFilterGlobalContext.h for more details.
174    bool blendsElementTexture = (programInfo.programType() == PROGRAM_TYPE_BLENDS_ELEMENT_TEXTURE);
175    ANGLEWebKitBridge* validator = blendsElementTexture ? m_globalContext->mixShaderValidator() : m_globalContext->webglShaderValidator();
176    String vertexShaderLog, fragmentShaderLog;
177    Vector<ANGLEShaderSymbol> symbols;
178    bool vertexShaderValid = validator->compileShaderSource(originalVertexShader.utf8().data(), SHADER_TYPE_VERTEX, m_validatedVertexShader, vertexShaderLog, symbols);
179    bool fragmentShaderValid = validator->compileShaderSource(originalFragmentShader.utf8().data(), SHADER_TYPE_FRAGMENT, m_validatedFragmentShader, fragmentShaderLog, symbols);
180    if (!vertexShaderValid || !fragmentShaderValid) {
181        // FIXME: Report the validation errors.
182        // https://bugs.webkit.org/show_bug.cgi?id=74416
183        return;
184    }
185
186    if (!validateSymbols(symbols, m_programInfo.meshType())) {
187        // FIXME: Report validation errors.
188        // https://bugs.webkit.org/show_bug.cgi?id=74416
189        return;
190    }
191
192    // We need to add texture access, blending, and compositing code to shaders that are referenced from the CSS mix function.
193    if (blendsElementTexture) {
194        rewriteMixVertexShader(symbols);
195        rewriteMixFragmentShader();
196    }
197
198    m_isInitialized = true;
199}
200
201PassRefPtr<CustomFilterCompiledProgram> CustomFilterValidatedProgram::compiledProgram()
202{
203    ASSERT(m_isInitialized && m_globalContext && !m_validatedVertexShader.isNull() && !m_validatedFragmentShader.isNull());
204    if (!m_compiledProgram) {
205        m_compiledProgram = CustomFilterCompiledProgram::create(m_globalContext->context(), m_validatedVertexShader, m_validatedFragmentShader, m_programInfo.programType());
206        ASSERT(m_compiledProgram->isInitialized());
207        ASSERT(m_compiledProgram->samplerLocation() != -1 || !needsInputTexture());
208    }
209    return m_compiledProgram;
210}
211
212bool CustomFilterValidatedProgram::needsInputTexture() const
213{
214    return m_programInfo.programType() == PROGRAM_TYPE_BLENDS_ELEMENT_TEXTURE
215        && m_programInfo.mixSettings().compositeOperator != CompositeClear
216        && m_programInfo.mixSettings().compositeOperator != CompositeCopy;
217}
218
219void CustomFilterValidatedProgram::rewriteMixVertexShader(const Vector<ANGLEShaderSymbol>& symbols)
220{
221    ASSERT(m_programInfo.programType() == PROGRAM_TYPE_BLENDS_ELEMENT_TEXTURE);
222
223    // If the author defined a_texCoord, we can use it to shuttle the texture coordinate to the fragment shader.
224    // Note that vertex attributes are read-only in GLSL, so the author could not have changed a_texCoord's value.
225    // Also, note that we would have already rejected the shader if the author defined a_texCoord with the wrong type.
226    bool texCoordAttributeDefined = false;
227    for (size_t i = 0; i < symbols.size(); ++i) {
228        if (symbols[i].name == "a_texCoord")
229            texCoordAttributeDefined = true;
230    }
231
232    if (!texCoordAttributeDefined)
233        m_validatedVertexShader.append("attribute mediump vec2 a_texCoord;");
234
235    // During validation, ANGLE renamed the author's "main" function to "css_main".
236    // We write our own "main" function and call "css_main" from it.
237    // This makes rewriting easy and ensures that our code runs after all author code.
238    m_validatedVertexShader.append(SHADER(
239        varying mediump vec2 css_v_texCoord;
240
241        void main()
242        {
243            css_main();
244            css_v_texCoord = a_texCoord;
245        }
246    ));
247}
248
249void CustomFilterValidatedProgram::rewriteMixFragmentShader()
250{
251    ASSERT(m_programInfo.programType() == PROGRAM_TYPE_BLENDS_ELEMENT_TEXTURE);
252
253    StringBuilder builder;
254    // ANGLE considered these symbols as built-ins during validation under the SH_CSS_SHADERS_SPEC flag.
255    // Now, we have to define these symbols in order to make this shader valid GLSL.
256    // We define these symbols before the author's shader code, which makes them accessible to author code.
257    builder.append(SHADER(
258        mediump vec4 css_MixColor = vec4(0.0);
259        mediump mat4 css_ColorMatrix = mat4(1.0);
260    ));
261    builder.append(m_validatedFragmentShader);
262    builder.append(blendFunctionString(m_programInfo.mixSettings().blendMode));
263    builder.append(compositeFunctionString(m_programInfo.mixSettings().compositeOperator));
264    // We define symbols like "css_u_texture" after the author's shader code, which makes them inaccessible to author code.
265    // In particular, "css_u_texture" represents the DOM element texture, so it's important to keep it inaccessible to
266    // author code for security reasons.
267    builder.append(SHADER(
268        uniform sampler2D css_u_texture;
269        varying mediump vec2 css_v_texCoord;
270
271        void main()
272        {
273            css_main();
274            mediump vec4 originalColor = texture2D(css_u_texture, css_v_texCoord);
275            mediump vec4 multipliedColor = clamp(css_ColorMatrix * originalColor, 0.0, 1.0);
276            mediump vec4 clampedMixColor = clamp(css_MixColor, 0.0, 1.0);
277            mediump vec3 blendedColor = css_BlendColor(multipliedColor.rgb, clampedMixColor.rgb);
278            mediump vec3 weightedColor = (1.0 - multipliedColor.a) * clampedMixColor.rgb + multipliedColor.a * blendedColor;
279            gl_FragColor = css_Composite(multipliedColor.rgb, multipliedColor.a, weightedColor.rgb, clampedMixColor.a);
280        }
281    ));
282    m_validatedFragmentShader = builder.toString();
283}
284
285String CustomFilterValidatedProgram::blendFunctionString(BlendMode blendMode)
286{
287    // Implemented using the same symbol names as the Compositing and Blending spec:
288    // https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blendingnormal
289    // Cs: is the source color in css_BlendColor() and the source color component in css_BlendComponent()
290    // Cb: is the backdrop color in css_BlendColor() and the backdrop color component in css_BlendComponent()
291    const char* blendColorExpression = "vec3(css_BlendComponent(Cb.r, Cs.r), css_BlendComponent(Cb.g, Cs.g), css_BlendComponent(Cb.b, Cs.b))";
292    const char* blendComponentExpression = "Co = 0.0;";
293    bool needsLuminosityHelperFunctions = false;
294    bool needsSaturationHelperFunctions = false;
295    String blendFunctionString;
296    switch (blendMode) {
297    case BlendModeNormal:
298        blendColorExpression = "Cs";
299        break;
300    case BlendModeMultiply:
301        blendColorExpression = "Cs * Cb";
302        break;
303    case BlendModeScreen:
304        blendColorExpression = "Cb + Cs - (Cb * Cs)";
305        break;
306    case BlendModeDarken:
307        blendColorExpression = "min(Cb, Cs)";
308        break;
309    case BlendModeLighten:
310        blendColorExpression = "max(Cb, Cs)";
311        break;
312    case BlendModeDifference:
313        blendColorExpression = "abs(Cb - Cs)";
314        break;
315    case BlendModeExclusion:
316        blendColorExpression = "Cb + Cs - 2.0 * Cb * Cs";
317        break;
318    case BlendModeOverlay:
319        /*
320            Co = HardLight(Cs, Cb)
321               = if(Cb <= 0.5)
322                     Multiply(Cs, 2 x Cb)
323                 else
324                     Screen(Cs, 2 x Cb - 1)
325               = if(Cb <= 0.5)
326                     Cs x (2 x Cb)
327                 else
328                     Cs + (2 x Cb - 1) - (Cs x (2 x Cb - 1))
329        */
330        blendComponentExpression = SHADER(
331            if (Cb <= 0.5)
332                Co = Cs * (2.0 * Cb);
333            else
334                Co = Cs + (2.0 * Cb - 1.0) - (Cs * (2.0 * Cb - 1.0));
335        );
336        break;
337    case BlendModeColorDodge:
338        /*
339            Co = if(Cs < 1)
340                     min(1, Cb / (1 - Cs))
341                 else
342                     1
343        */
344        blendComponentExpression = SHADER(
345            if (Cs < 1.0)
346                Co = min(1.0, Cb / (1.0 - Cs));
347            else
348                Co = 1.0;
349        );
350        break;
351    case BlendModeColorBurn:
352        /*
353            Co = if(Cs > 0)
354                     1 - min(1, (1 - Cb) / Cs)
355                 else
356                     0
357        */
358        blendComponentExpression = SHADER(
359            if (Cs > 0.0)
360                Co = 1.0 - min(1.0, (1.0 - Cb) / Cs);
361            else
362                Co = 0.0;
363        );
364        break;
365    case BlendModeHardLight:
366        /*
367            Co = if(Cs <= 0.5)
368                     Multiply(Cb, 2 x Cs)
369                 else
370                     Screen(Cb, 2 x Cs -1)
371               = if(Cs <= 0.5)
372                     Cb x (2 x Cs)
373                 else
374                     Cb + (2 x Cs - 1) - (Cb x (2 x Cs - 1))
375        */
376        blendComponentExpression = SHADER(
377            if (Cs <= 0.5)
378                Co = Cb * (2.0 * Cs);
379            else
380                Co = Cb + (2.0 * Cs - 1.0) - (Cb * (2.0 * Cs - 1.0));
381        );
382        break;
383    case BlendModeSoftLight:
384        /*
385            Co = if(Cs <= 0.5)
386                     Cb - (1 - 2 x Cs) x Cb x (1 - Cb)
387                 else
388                     Cb + (2 x Cs - 1) x (D(Cb) - Cb)
389
390            with
391
392            D(Cb) = if(Cb <= 0.25)
393                        (16 * Cb - 12) x Cb + 4) x Cb
394                    else
395                        sqrt(Cb)
396        */
397        blendComponentExpression = SHADER(
398            mediump float D;
399            if (Cb <= 0.25)
400                D = ((16.0 * Cb - 12.0) * Cb + 4.0) * Cb;
401            else
402                D = sqrt(Cb);
403
404            if (Cs <= 0.5)
405                Co = Cb - (1.0 - 2.0 * Cs) * Cb * (1.0 - Cb);
406            else
407                Co = Cb + (2.0 * Cs - 1.0) * (D - Cb);
408        );
409        break;
410    case BlendModeColor:
411        needsLuminosityHelperFunctions = true;
412        blendColorExpression = "css_SetLum(Cs, css_Lum(Cb))";
413        break;
414    case BlendModeLuminosity:
415        needsLuminosityHelperFunctions = true;
416        blendColorExpression = "css_SetLum(Cb, css_Lum(Cs))";
417        break;
418    case BlendModeHue:
419        needsLuminosityHelperFunctions = true;
420        needsSaturationHelperFunctions = true;
421        blendColorExpression = "css_SetLum(css_SetSat(Cs, css_Sat(Cb)), css_Lum(Cb))";
422        break;
423    case BlendModeSaturation:
424        needsLuminosityHelperFunctions = true;
425        needsSaturationHelperFunctions = true;
426        blendColorExpression = "css_SetLum(css_SetSat(Cb, css_Sat(Cs)), css_Lum(Cb))";
427        break;
428    default:
429        ASSERT_NOT_REACHED();
430    }
431
432    if (needsLuminosityHelperFunctions) {
433        blendFunctionString.append(SHADER(
434            mediump float css_Lum(mediump vec3 C)
435            {
436                return 0.3 * C.r + 0.59 * C.g + 0.11 * C.b;
437            }
438            mediump vec3 css_ClipColor(mediump vec3 C)
439            {
440                mediump float L = css_Lum(C);
441                mediump float n = min(min(C.r, C.g), C.b);
442                mediump float x = max(max(C.r, C.g), C.b);
443                if (n < 0.0)
444                    C = L + (((C - L) * L) / (L - n));
445                if (x > 1.0)
446                    C = L + (((C - L) * (1.0 - L) / (x - L)));
447                return C;
448            }
449            mediump vec3 css_SetLum(mediump vec3 C, mediump float l)
450            {
451                C += l - css_Lum(C);
452                return css_ClipColor(C);
453            }
454        ));
455    }
456
457    if (needsSaturationHelperFunctions) {
458        blendFunctionString.append(SHADER(
459            mediump float css_Sat(mediump vec3 C)
460            {
461                mediump float cMin = min(min(C.r, C.g), C.b);
462                mediump float cMax = max(max(C.r, C.g), C.b);
463                return cMax - cMin;
464            }
465            void css_SetSatHelper(inout mediump float cMin, inout mediump float cMid, inout mediump float cMax, mediump float s)
466            {
467                if (cMax > cMin) {
468                    cMid = (((cMid - cMin) * s) / (cMax - cMin));
469                    cMax = s;
470                } else
471                    cMid = cMax = 0.0;
472                cMin = 0.0;
473            }
474            mediump vec3 css_SetSat(mediump vec3 C, mediump float s)
475            {
476                if (C.r <= C.g) {
477                    if (C.g <= C.b)
478                        css_SetSatHelper(C.r, C.g, C.b, s);
479                    else {
480                        if (C.r <= C.b)
481                            css_SetSatHelper(C.r, C.b, C.g, s);
482                        else
483                            css_SetSatHelper(C.b, C.r, C.g, s);
484                    }
485                } else {
486                    if (C.r <= C.b)
487                        css_SetSatHelper(C.g, C.r, C.b, s);
488                    else {
489                        if (C.g <= C.b)
490                            css_SetSatHelper(C.g, C.b, C.r, s);
491                        else
492                            css_SetSatHelper(C.b, C.g, C.r, s);
493                    }
494                }
495                return C;
496            }
497        ));
498    }
499
500    blendFunctionString.append(String::format(SHADER(
501        mediump float css_BlendComponent(mediump float Cb, mediump float Cs)
502        {
503            mediump float Co;
504            %s
505            return Co;
506        }
507        mediump vec3 css_BlendColor(mediump vec3 Cb, mediump vec3 Cs)
508        {
509            return %s;
510        }
511    ), blendComponentExpression, blendColorExpression));
512
513    return blendFunctionString;
514}
515
516String CustomFilterValidatedProgram::compositeFunctionString(CompositeOperator compositeOperator)
517{
518    // Use the same symbol names as the Compositing and Blending spec:
519    // https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blendingnormal
520    // Cs: is the source color
521    // Cb: is the backdrop color
522    // as: is the source alpha
523    // ab: is the backdrop alpha
524    // Fa: is defined by the Porter Duff operator in use
525    // Fb: is defined by the Porter Duff operator in use
526    const char* Fa = 0;
527    const char* Fb = 0;
528    switch (compositeOperator) {
529    case CompositeSourceAtop:
530        Fa = "ab";
531        Fb = "1.0 - as";
532        break;
533    case CompositeClear:
534        Fa = "0.0";
535        Fb = "0.0";
536        break;
537    case CompositeCopy:
538        Fa = "1.0";
539        Fb = "0.0";
540        break;
541    case CompositeSourceOver:
542        Fa = "1.0";
543        Fb = "1.0 - as";
544        break;
545    case CompositeSourceIn:
546        Fa = "ab";
547        Fb = "0.0";
548        break;
549    case CompositeSourceOut:
550        Fa = "1.0 - ab";
551        Fb = "0.0";
552        break;
553    case CompositeDestinationOver:
554        Fa = "1.0 - ab";
555        Fb = "1.0";
556        break;
557    case CompositeDestinationIn:
558        Fa = "0.0";
559        Fb = "as";
560        break;
561    case CompositeDestinationOut:
562        Fa = "0.0";
563        Fb = "1.0 - as";
564        break;
565    case CompositeDestinationAtop:
566        Fa = "1.0 - ab";
567        Fb = "as";
568        break;
569    case CompositeXOR:
570        Fa = "1.0 - ab";
571        Fb = "1.0 - as";
572        break;
573    case CompositePlusLighter:
574        notImplemented();
575        return String();
576    default:
577        // The CSS parser should not have accepted any other composite operators.
578        ASSERT_NOT_REACHED();
579        return String();
580    }
581
582    ASSERT(Fa && Fb);
583    // Use the general formula for compositing, lifted from the spec:
584    // https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#generalformula
585    return String::format(SHADER(
586        mediump vec4 css_Composite(mediump vec3 Cb, mediump float ab, mediump vec3 Cs, mediump float as)
587        {
588            mediump float Fa = %s;
589            mediump float Fb = %s;
590            return vec4(as * Fa * Cs + ab * Fb * Cb, as * Fa + ab * Fb);
591        }
592    ), Fa, Fb);
593}
594
595CustomFilterValidatedProgram::~CustomFilterValidatedProgram()
596{
597    platformDestroy();
598
599    if (m_globalContext)
600        m_globalContext->removeValidatedProgram(this);
601}
602
603CustomFilterProgramInfo CustomFilterValidatedProgram::validatedProgramInfo() const
604{
605    ASSERT(m_isInitialized);
606    return CustomFilterProgramInfo(m_validatedVertexShader, m_validatedFragmentShader, m_programInfo.programType(), m_programInfo.mixSettings(), m_programInfo.meshType());
607}
608
609void CustomFilterValidatedProgram::platformInit()
610{
611}
612
613void CustomFilterValidatedProgram::platformDestroy()
614{
615}
616
617} // namespace WebCore
618
619