Title: [115487] trunk
Revision
115487
Author
[email protected]
Date
2012-04-27 13:30:40 -0700 (Fri, 27 Apr 2012)

Log Message

[CSS Shaders] Implement CSS Animations and Transitions for CSS Shaders
https://bugs.webkit.org/show_bug.cgi?id=71406

Reviewed by Dean Jackson.

Source/WebCore:

I've implemented the blend function for the CustomFilterOperation. This should enable animations for CSS Shaders.
Currently, just floats are implemented. If any of the filter attributes like shader, mesh size or box mode are different,
the fallback is to use the "to" part of the animation instead. If other shader parameters do not match, it will merge the parameter values
between the "from" and "to" states.

Test: css3/filters/custom/custom-filter-animation.html

* platform/graphics/filters/CustomFilterNumberParameter.h:
(WebCore::CustomFilterNumberParameter::blend):
(CustomFilterNumberParameter):
(WebCore::CustomFilterNumberParameter::operator==):
* platform/graphics/filters/CustomFilterOperation.cpp:
(WebCore::equalCustomFilterParameters):
(WebCore):
(WebCore::checkCustomFilterParametersOrder):
(WebCore::blendCustomFilterParameters):
(WebCore::CustomFilterOperation::CustomFilterOperation):
(WebCore::CustomFilterOperation::blend):
* platform/graphics/filters/CustomFilterOperation.h:
(WebCore):
(CustomFilterOperation):
(WebCore::CustomFilterOperation::operator==):
(WebCore::CustomFilterOperation::operator!=):
* platform/graphics/filters/CustomFilterParameter.h:
(CustomFilterParameter):
(WebCore::CustomFilterParameter::isSameType):
(WebCore::CustomFilterParameter::operator==):
(WebCore::CustomFilterParameter::operator!=):
* platform/graphics/filters/CustomFilterProgram.h:
* rendering/style/StyleCustomFilterProgram.h:
(StyleCustomFilterProgram):
(WebCore::StyleCustomFilterProgram::cachedVertexShader):
(WebCore::StyleCustomFilterProgram::cachedFragmentShader):
(WebCore::StyleCustomFilterProgram::operator==):

LayoutTests:

* animations/resources/animation-test-helpers.js: Added a check for the "custom" function and used the parser in custom-filter-parser.js instead.
(getFilterParameters):
(filterParametersMatch):
* css3/filters/custom/custom-filter-animation-expected.txt: Added.
* css3/filters/custom/custom-filter-animation.html: Added.
* css3/filters/resources/custom-filter-parser.js: Added a simple parser for the "custom" function, so that multiple types can be checked correctly.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (115486 => 115487)


--- trunk/LayoutTests/ChangeLog	2012-04-27 20:29:31 UTC (rev 115486)
+++ trunk/LayoutTests/ChangeLog	2012-04-27 20:30:40 UTC (rev 115487)
@@ -1,3 +1,17 @@
+2012-04-27  Alexandru Chiculita  <[email protected]>
+
+        [CSS Shaders] Implement CSS Animations and Transitions for CSS Shaders
+        https://bugs.webkit.org/show_bug.cgi?id=71406
+
+        Reviewed by Dean Jackson.
+
+        * animations/resources/animation-test-helpers.js: Added a check for the "custom" function and used the parser in custom-filter-parser.js instead.
+        (getFilterParameters):
+        (filterParametersMatch):
+        * css3/filters/custom/custom-filter-animation-expected.txt: Added.
+        * css3/filters/custom/custom-filter-animation.html: Added.
+        * css3/filters/resources/custom-filter-parser.js: Added a simple parser for the "custom" function, so that multiple types can be checked correctly.
+
 2012-04-26  Sam Weinig  <[email protected]>
 
         Add support for the Blob constructor

Modified: trunk/LayoutTests/animations/resources/animation-test-helpers.js (115486 => 115487)


--- trunk/LayoutTests/animations/resources/animation-test-helpers.js	2012-04-27 20:29:31 UTC (rev 115486)
+++ trunk/LayoutTests/animations/resources/animation-test-helpers.js	2012-04-27 20:30:40 UTC (rev 115487)
@@ -62,7 +62,15 @@
 // Return an array of numeric filter params in 0-1.
 function getFilterParameters(s)
 {
-    var filterParams = s.match(/\((.+)\)/)[1];
+    var filterResult = s.match(/(\w+)\((.+)\)/);
+    if (!filterResult)
+        throw new Error("There's no filter in \"" + s + "\"");
+    var filterParams = filterResult[2];
+    if (filterResult[1] == "custom") {
+        if (!window.getCustomFilterParameters)
+            throw new Error("getCustomFilterParameters not found. Did you include custom-filter-parser.js?");
+        return getCustomFilterParameters(filterParams);
+    }
     var paramList = filterParams.split(' '); // FIXME: the spec may allow comma separation at some point.
     
     // Normalize percentage values.
@@ -80,9 +88,29 @@
 {
     if (paramList1.length != paramList2.length)
         return false;
-
     for (var i = 0; i < paramList1.length; ++i) {
-        var match = isCloseEnough(paramList1[i], paramList2[i], tolerance);
+        var param1 = paramList1[i], 
+            param2 = paramList2[i];
+        if (typeof param1 == "object") {
+            // This is a custom filter parameter.
+            if (param1.type != "parameter") {
+                // Checking for shader uris and other keywords. They need to be exactly the same.
+                if (param1.type != param2.type
+                    || param1.value != param2.value)
+                    return false;
+                continue;
+            }
+            if (param1.name != param2.name
+                || param1.value.length != param2.value.length)
+                return false;
+            // For now we only support floats.
+            for (var j = 0; j < param1.value.length; ++j) {
+                if (!isCloseEnough(param1.value[j].value, param2.value[j].value, tolerance))
+                    return false;
+            }
+            continue;
+        }
+        var match = isCloseEnough(param1, param2, tolerance);
         if (!match)
             return false;
     }

Added: trunk/LayoutTests/css3/filters/custom/custom-filter-animation-expected.txt (0 => 115487)


--- trunk/LayoutTests/css3/filters/custom/custom-filter-animation-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/css3/filters/custom/custom-filter-animation-expected.txt	2012-04-27 20:30:40 UTC (rev 115487)
@@ -0,0 +1,11 @@
+       
+PASS - "webkitFilter" property for "custom-from-none-box" element at 1s saw something close to: custom(url(vertex-rotate.vs) none, 1 1 filter-box, rotateBy 30, offset 0)
+PASS - "webkitFilter" property for "custom-to-none-box" element at 1s saw something close to: custom(url(vertex-rotate.vs) none, 1 1 filter-box, rotateBy 30, offset 0)
+PASS - "webkitFilter" property for "custom-from-no-params-box" element at 1s saw something close to: custom(url(vertex-rotate.vs) none, 1 1 filter-box, rotateBy 30, offset 0)
+PASS - "webkitFilter" property for "custom-to-no-params-box" element at 1s saw something close to: custom(url(vertex-rotate.vs) none, 1 1 filter-box, rotateBy 30, offset 0)
+PASS - "webkitFilter" property for "custom-mix-attributes-box" element at 1s saw something close to: custom(url(vertex-rotate.vs) none, 5 5 border-box, rotateBy 30, offset 0)
+PASS - "webkitFilter" property for "custom-from-diff-params-box" element at 1s saw something close to: custom(url(vertex-rotate.vs) none, 1 1 filter-box, param 10, rotateBy 60, offset 10, another_param 5)
+PASS - "webkitFilter" property for "custom-to-diff-params-box" element at 1s saw something close to: custom(url(vertex-rotate.vs) none, 1 1 filter-box, param 10, rotateBy 60, offset 10, another_param 5)
+PASS - "webkitFilter" property for "custom-mix-params-box" element at 1s saw something close to: custom(url(vertex-rotate.vs) none, 1 1 filter-box, param 10, another_param 5, first_param 6, rotateBy 60, offset 10, last_param 4)
+PASS - "webkitFilter" property for "custom-mix-numbers-box" element at 1s saw something close to: custom(url(vertex-rotate.vs) none, 1 1 filter-box, rotateBy 45, a 5.5, b 11 16.5, c 22 27.5 33, d 38.5 44 49.5)
+

Added: trunk/LayoutTests/css3/filters/custom/custom-filter-animation.html (0 => 115487)


--- trunk/LayoutTests/css3/filters/custom/custom-filter-animation.html	                        (rev 0)
+++ trunk/LayoutTests/css3/filters/custom/custom-filter-animation.html	2012-04-27 20:30:40 UTC (rev 115487)
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+    <script>
+        if (window.layoutTestController) {
+            window.layoutTestController.overridePreference("WebKitCSSCustomFilterEnabled", "1");
+            window.layoutTestController.overridePreference("WebKitWebGLEnabled", "1");
+        }
+    </script>
+    <style>
+        .box {
+            height: 100px;
+            width: 100px;
+            margin: 10px;
+            background-color: blue;
+            display: inline-block;
+        }
+    
+        #custom-from-none-box {
+            -webkit-animation: custom-from-none-anim 2s linear;
+        }
+    
+        #custom-to-none-box {
+            -webkit-animation: custom-to-none-anim 2s linear;
+        }
+    
+        #custom-from-no-params-box {
+            -webkit-animation: custom-from-no-params-anim 2s linear;
+        }
+    
+        #custom-to-no-params-box {
+            -webkit-animation: custom-to-no-params-anim 2s linear;
+        }
+        
+        #custom-mix-attributes-box {
+            -webkit-animation: custom-mix-attributes-anim 2s linear;
+        }
+    
+        #custom-from-diff-params-box {
+            -webkit-animation: custom-from-diff-params-anim 2s linear;
+        }
+    
+        #custom-to-diff-params-box {
+            -webkit-animation: custom-to-diff-params-anim 2s linear;
+        }
+    
+        #custom-mix-params-box {
+            -webkit-animation: custom-mix-params-anim 2s linear;
+        }
+        
+        #custom-mix-numbers-box {
+            -webkit-animation: custom-mix-numbers-anim 2s linear;
+        }
+    
+        @-webkit-keyframes custom-from-none-anim {
+            from { -webkit-filter: none; }
+            to   { -webkit-filter: custom(url(../resources/vertex-rotate.vs), rotateBy 30, offset 0); }
+        }
+
+        @-webkit-keyframes custom-to-none-anim {
+            from { -webkit-filter: custom(url(../resources/vertex-rotate.vs), rotateBy 30, offset 0); }
+            to   { -webkit-filter: none; }
+        }
+    
+        @-webkit-keyframes custom-from-no-params-anim {
+            from { -webkit-filter: custom(url(../resources/vertex-rotate.vs)); }
+            to   { -webkit-filter: custom(url(../resources/vertex-rotate.vs), rotateBy 30, offset 0); }
+        }
+
+        @-webkit-keyframes custom-to-no-params-anim {
+            from { -webkit-filter: custom(url(../resources/vertex-rotate.vs), rotateBy 30, offset 0); }
+            to   { -webkit-filter: custom(url(../resources/vertex-rotate.vs)); }
+        }
+        
+        @-webkit-keyframes custom-mix-attributes-anim {
+            from { -webkit-filter: custom(url(../resources/vertex-offset.vs), rotateBy_a 30, offset_a 0); }
+            to   { -webkit-filter: custom(url(../resources/vertex-rotate.vs), 5 5 border-box, rotateBy 30, offset 0); }
+        }
+    
+        @-webkit-keyframes custom-from-diff-params-anim {
+            from { -webkit-filter: custom(url(../resources/vertex-rotate.vs), param 10, rotateBy 30, offset 0, another_param 5); }
+            to   { -webkit-filter: custom(url(../resources/vertex-rotate.vs), rotateBy 90, offset 20); }
+        }
+
+        @-webkit-keyframes custom-to-diff-params-anim {
+            from { -webkit-filter: custom(url(../resources/vertex-rotate.vs), rotateBy 90, offset 20); }
+            to   { -webkit-filter: custom(url(../resources/vertex-rotate.vs), param 10, rotateBy 30, offset 0, another_param 5); }
+        }
+    
+        @-webkit-keyframes custom-mix-params-anim {
+            from { -webkit-filter: custom(url(../resources/vertex-rotate.vs), first_param 6, rotateBy 90, offset 20, last_param 4); }
+            to   { -webkit-filter: custom(url(../resources/vertex-rotate.vs), param 10, rotateBy 30, offset 0, another_param 5); }
+        }
+        
+        @-webkit-keyframes custom-mix-numbers-anim {
+            from { -webkit-filter: custom(url(../resources/vertex-rotate.vs), rotateBy 0, a 1, b 2 3, c 4 5 6, d 7 8 9); }
+            to   { -webkit-filter: custom(url(../resources/vertex-rotate.vs), rotateBy 90, a 10, b 20 30, c 40 50 60, d 70 80 90); }
+        }
+    </style>
+    <script src=""
+    <script src=""
+    <script type="text/_javascript_">
+        const expectedValues = [
+            // [animation-name, time, element-id, property, expected-value, tolerance]
+            ["custom-from-none-anim",  1, "custom-from-none-box", "webkitFilter", 'custom(url(vertex-rotate.vs) none, 1 1 filter-box, rotateBy 30, offset 0)', 0],
+            ["custom-to-none-anim",  1, "custom-to-none-box", "webkitFilter", 'custom(url(vertex-rotate.vs) none, 1 1 filter-box, rotateBy 30, offset 0)', 0],
+
+            ["custom-from-no-params-anim",  1, "custom-from-no-params-box", "webkitFilter", 'custom(url(vertex-rotate.vs) none, 1 1 filter-box, rotateBy 30, offset 0)', 0],
+            ["custom-to-no-params-anim",  1, "custom-to-no-params-box", "webkitFilter", 'custom(url(vertex-rotate.vs) none, 1 1 filter-box, rotateBy 30, offset 0)', 0],
+            
+            ["custom-mix-attributes-anim",  1, "custom-mix-attributes-box", "webkitFilter", 'custom(url(vertex-rotate.vs) none, 5 5 border-box, rotateBy 30, offset 0)', 0],
+
+            // FIXME: CSS Shaders do not get good FPS in debug mode, so the tolerance needs to be higher.
+            // https://bugs.webkit.org/show_bug.cgi?id=85086
+            ["custom-from-diff-params-anim",  1, "custom-from-diff-params-box", "webkitFilter", 'custom(url(vertex-rotate.vs) none, 1 1 filter-box, param 10, rotateBy 60, offset 10, another_param 5)', 5],
+            ["custom-to-diff-params-anim",  1, "custom-to-diff-params-box", "webkitFilter", 'custom(url(vertex-rotate.vs) none, 1 1 filter-box, param 10, rotateBy 60, offset 10, another_param 5)', 5],
+
+            ["custom-mix-params-anim",  1, "custom-mix-params-box", "webkitFilter", 'custom(url(vertex-rotate.vs) none, 1 1 filter-box, param 10, another_param 5, first_param 6, rotateBy 60, offset 10, last_param 4)', 5],
+            ["custom-mix-numbers-anim",  1, "custom-mix-numbers-box", "webkitFilter", 'custom(url(vertex-rotate.vs) none, 1 1 filter-box, rotateBy 45, a 5.5, b 11 16.5, c 22 27.5 33, d 38.5 44 49.5)', 10]
+        ];
+        runAnimationTest(expectedValues);
+    </script>
+</head>
+<body>
+    <div class="box" id="custom-from-none-box"></div>
+    <div class="box" id="custom-to-none-box"></div>
+    <div class="box" id="custom-from-no-params-box"></div>
+    <div class="box" id="custom-to-no-params-box"></div>
+    <div class="box" id="custom-mix-attributes-box"></div>
+    <div class="box" id="custom-from-diff-params-box"></div>
+    <div class="box" id="custom-to-diff-params-box"></div>
+    <div class="box" id="custom-mix-params-box"></div>
+    <div class="box" id="custom-mix-numbers-box"></div>
+
+    <div id="result">
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/css3/filters/resources/custom-filter-parser.js (0 => 115487)


--- trunk/LayoutTests/css3/filters/resources/custom-filter-parser.js	                        (rev 0)
+++ trunk/LayoutTests/css3/filters/resources/custom-filter-parser.js	2012-04-27 20:30:40 UTC (rev 115487)
@@ -0,0 +1,202 @@
+// This is a helper script designed to parse the computed syntax of the custom filter. 
+// Note that it is generic enough so that it can be used for other properties in the future.
+
+function Token(type) 
+{
+    this.type = type;
+}
+
+// Checks if the type of the token is in the list of arguments passed to the function.
+// It also accepts arrays and other type checking functions.
+Token.prototype.isA = function()
+{
+    for (var i = 0; i < arguments.length; ++i) {
+        var type = arguments[i];
+        if ((typeof type == "object" && type.length && this.isA.apply(this, type))
+            || (typeof type == "function" && type(this))
+            || type == this.type)
+            return true;
+    }
+    return false;
+}
+
+// Creates a new token object and copies all the properties in "value" to the new token.
+function createToken(type, value) {
+    var token = new Token(type);
+    if (value) {
+        for (var i in value)
+            if (value.hasOwnProperty(i))
+                token[i] = value[i];
+    }
+    return token;
+}
+
+// Tokenizes the string into Tokens of types [urls, floats, integers, keywords, ",", "(", ")" and "."].
+function tokenizeString(s)
+{
+    var tokenizer = new RegExp([
+        "url\\(\\s*(.*?)\\s*\\)", // url() - 1
+        "((?:[\\+-]?)\\d*\\.\\d+)", // floats - 2
+        "((?:[\\+-]?)\\d+)", // integers - 3
+        "([\\w-][\\w\\d-]*)", // keywords - 4
+        "([,\\.\\(\\)])" // punctuation - 5
+    ].join("|"), "g");
+
+    var match, tokens = [];
+    while (match = tokenizer.exec(s)) {
+        if (match[1] !== undefined)
+            tokens.push(createToken("url", {value: match[1]}));
+        else if (match[2] !== undefined)
+            tokens.push(createToken("float", {value: parseFloat(match[2])}));
+        else if (match[3] !== undefined)
+            tokens.push(createToken("integer", {value: parseInt(match[3])}));
+        else if (match[4] !== undefined)
+            tokens.push(createToken("keyword", {value: match[4]}));
+        else if (match[5] !== undefined)
+            tokens.push(createToken(match[5]));
+    }
+    return tokens;
+}
+
+// Checks if the token is a number. Can be used in combination with the "Token.isA" method.
+function number(token)
+{
+    return token.type == "float" || token.type == "integer";
+}
+
+// Helper class to iterate on the token stream. It will add an "end"
+// token at the end to make it easier to use the "ahead" function
+// without checking if it's the last token in the stream.
+function TokenStream(tokens) {
+    this.tokens = tokens;
+    this.tokens.push(new Token("end"));
+    this.tokenIndex = 0;
+}
+
+TokenStream.prototype.current = function() 
+{ 
+    return this.tokens[this.tokenIndex]; 
+}
+
+TokenStream.prototype.ahead = function() 
+{
+    return this.tokens[this.tokenIndex + 1]; 
+}
+
+// Skips the current token only if it matches the "typeMatcher". Otherwise it throws an error.
+TokenStream.prototype.skip = function(typeMatcher)
+{
+    if (!typeMatcher || this.ahead().isA(typeMatcher)) {
+        var token = this.current();
+        ++this.tokenIndex;
+        return token;
+    }
+    throw new Error("Cannot use " + JSON.stringify(typeMatcher) + " to skip over " + JSON.stringify(this.ahead()));
+}
+
+// Skips the current token, only if it matches the "typeMatcher". Makes it easy to skip over comma tokens.
+TokenStream.prototype.skipIfNeeded = function(typeMatcher)
+{
+    if (this.ahead() && this.ahead().isA(typeMatcher))
+        ++this.tokenIndex;
+}
+
+// Creates a new "function" node. It expects that the current token is the function name.
+function parseFunction(m)
+{
+    var functionObject = {
+        type: "function",
+        name: m.skip().value
+    };
+    m.skip("(");
+    functionObject.arguments = parseList(m, [")", "end"]);
+    m.skip(")");
+    return functionObject;
+}
+
+// Creates a new "parameter" node. It expects that the current token is the parameter name.
+// It consumes everything before the following comma.
+function parseParameter(m)
+{
+    var functionObject = {
+        type: "parameter",
+        name: m.skip().value
+    };
+    functionObject.value = parseList(m, [",", "end"]);
+    m.skipIfNeeded(",");
+    return functionObject;
+}
+
+// Consumes a list of tokens before reaching the "endToken" and returns an array with all the parsed items.
+// Makes the following assumptions:
+// - if a keyword is followed by "(" then it is a start of function
+// - if a keyword is not followed by "," it is a parameter
+// Keywords that do not match either of the previous rules and tokens like number and url are just cloned.
+function parseList(m, endToken)
+{
+    var result = [], token;
+    while ((token = m.current()) && !token.isA(endToken)) {
+        if (token.isA("keyword")) {
+            if (m.ahead().isA("("))
+                result.push(parseFunction(m));
+            else if (m.ahead().isA(",", endToken)) {
+                result.push({
+                    type: "keyword", 
+                    value: m.skip().value
+                });
+            } else
+                result.push(parseParameter(m));
+        } else if (token.isA(number)) {
+            result.push({
+                type: "number",
+                value: m.skip().value
+            });
+        } else if (token.isA("url")) {
+            result.push({
+                type: "url",
+                value: m.skip().value
+            });
+        } else if (token.isA(","))
+            m.skip();
+        else 
+            throw "Unexpected token " + JSON.stringify(token) + " in a list.";
+    }
+    return result;
+}
+
+function tokensToValues(tokens)
+{
+    var m = new TokenStream(tokens);
+    return parseList(m, "end");
+}
+
+// Extracts a parameters array from the parameters string of the custom filter function.
+function parseCustomFilterParameters(s)
+{
+    return tokensToValues(tokenizeString(s));
+}
+
+// Need to remove the base URL to avoid having local paths in the expected results.
+function removeBaseURL(src) {
+    var urlRegexp = /url\(([^\)]*)\)/g;
+    return src.replace(urlRegexp, function(match, url) {
+        return "url(" + url.substr(url.lastIndexOf("/") + 1) + ")";
+    });
+}
+
+// Parses the parameters of the custom filter function and returns it using the following order:
+// - If parameters have different types (ie. url, number, named parameter), the alphabetical order of the "type" is used.
+// - If parameters are of type "parameter" the alphabetical order of the parameter names is used.
+// - If parameters are of other types, then the value is used to order them alphabetical.
+// The order is important to make it easy to compare two custom filters, that have exactly the same parameters, 
+// but with potentially different "named parameter" values.
+function getCustomFilterParameters(s) 
+{
+    return parseCustomFilterParameters(removeBaseURL(s)).sort(function(a, b) { 
+        if (a.type != b.type) 
+            return a.type.localeCompare(b.type);
+        if (a.type == "parameter")
+            return a.name.localeCompare(b.name);
+        return a.value.toString().localeCompare(b.value.toString());
+    });
+}

Modified: trunk/Source/WebCore/ChangeLog (115486 => 115487)


--- trunk/Source/WebCore/ChangeLog	2012-04-27 20:29:31 UTC (rev 115486)
+++ trunk/Source/WebCore/ChangeLog	2012-04-27 20:30:40 UTC (rev 115487)
@@ -1,3 +1,45 @@
+2012-04-27  Alexandru Chiculita  <[email protected]>
+
+        [CSS Shaders] Implement CSS Animations and Transitions for CSS Shaders
+        https://bugs.webkit.org/show_bug.cgi?id=71406
+
+        Reviewed by Dean Jackson.
+
+        I've implemented the blend function for the CustomFilterOperation. This should enable animations for CSS Shaders.
+        Currently, just floats are implemented. If any of the filter attributes like shader, mesh size or box mode are different, 
+        the fallback is to use the "to" part of the animation instead. If other shader parameters do not match, it will merge the parameter values
+        between the "from" and "to" states.
+
+        Test: css3/filters/custom/custom-filter-animation.html
+
+        * platform/graphics/filters/CustomFilterNumberParameter.h:
+        (WebCore::CustomFilterNumberParameter::blend):
+        (CustomFilterNumberParameter):
+        (WebCore::CustomFilterNumberParameter::operator==):
+        * platform/graphics/filters/CustomFilterOperation.cpp:
+        (WebCore::equalCustomFilterParameters):
+        (WebCore):
+        (WebCore::checkCustomFilterParametersOrder):
+        (WebCore::blendCustomFilterParameters):
+        (WebCore::CustomFilterOperation::CustomFilterOperation):
+        (WebCore::CustomFilterOperation::blend):
+        * platform/graphics/filters/CustomFilterOperation.h:
+        (WebCore):
+        (CustomFilterOperation):
+        (WebCore::CustomFilterOperation::operator==):
+        (WebCore::CustomFilterOperation::operator!=):
+        * platform/graphics/filters/CustomFilterParameter.h:
+        (CustomFilterParameter):
+        (WebCore::CustomFilterParameter::isSameType):
+        (WebCore::CustomFilterParameter::operator==):
+        (WebCore::CustomFilterParameter::operator!=):
+        * platform/graphics/filters/CustomFilterProgram.h:
+        * rendering/style/StyleCustomFilterProgram.h:
+        (StyleCustomFilterProgram):
+        (WebCore::StyleCustomFilterProgram::cachedVertexShader):
+        (WebCore::StyleCustomFilterProgram::cachedFragmentShader):
+        (WebCore::StyleCustomFilterProgram::operator==):
+
 2012-04-27  Chris Rogers  <[email protected]>
 
         Re-factor scheduling logic from AudioBufferSourceNode into AudioScheduledSourceNode

Modified: trunk/Source/WebCore/platform/graphics/filters/CustomFilterNumberParameter.h (115486 => 115487)


--- trunk/Source/WebCore/platform/graphics/filters/CustomFilterNumberParameter.h	2012-04-27 20:29:31 UTC (rev 115486)
+++ trunk/Source/WebCore/platform/graphics/filters/CustomFilterNumberParameter.h	2012-04-27 20:30:40 UTC (rev 115487)
@@ -48,6 +48,27 @@
 
     void addValue(double value) { m_data.append(value); }
     
+    virtual PassRefPtr<CustomFilterParameter> blend(const CustomFilterParameter* from, double progress)
+    {
+        if (!from || !isSameType(*from))
+            return this;
+        const CustomFilterNumberParameter* fromNumber = static_cast<const CustomFilterNumberParameter*>(from);
+        if (size() != fromNumber->size())
+            return this;
+        RefPtr<CustomFilterNumberParameter> result = CustomFilterNumberParameter::create(name());
+        for (size_t i = 0; i < size(); ++i)
+            result->addValue(WebCore::blend(fromNumber->valueAt(i), valueAt(i), progress));
+        return result.release();
+    }
+    
+    virtual bool operator==(const CustomFilterParameter& o) const
+    {
+        if (!isSameType(o))
+            return false;
+        const CustomFilterNumberParameter* other = static_cast<const CustomFilterNumberParameter*>(&o);
+        return m_data == other->m_data;
+    }
+    
 private:
     CustomFilterNumberParameter(const String& name)
         : CustomFilterParameter(NUMBER, name)

Modified: trunk/Source/WebCore/platform/graphics/filters/CustomFilterOperation.cpp (115486 => 115487)


--- trunk/Source/WebCore/platform/graphics/filters/CustomFilterOperation.cpp	2012-04-27 20:29:31 UTC (rev 115486)
+++ trunk/Source/WebCore/platform/graphics/filters/CustomFilterOperation.cpp	2012-04-27 20:30:40 UTC (rev 115487)
@@ -40,6 +40,60 @@
 
 namespace WebCore {
 
+bool customFilterParametersEqual(const CustomFilterParameterList& listA, const CustomFilterParameterList& listB)
+{
+    if (listA.size() != listB.size())
+        return false;
+    for (size_t i = 0; i < listA.size(); ++i) {
+        if (listA.at(i).get() != listB.at(i).get() 
+            && *listA.at(i).get() != *listB.at(i).get())
+            return false;
+    }
+    return true;
+}
+
+#if !ASSERT_DISABLED
+static bool checkCustomFilterParametersOrder(const CustomFilterParameterList& parameters)
+{
+    for (unsigned i = 1; i < parameters.size(); ++i) {
+        // Break for equal or not-sorted parameters.
+        if (!codePointCompareLessThan(parameters.at(i - 1)->name(), parameters.at(i)->name()))
+            return false;
+    }
+    return true;
+}
+#endif
+
+void blendCustomFilterParameters(const CustomFilterParameterList& fromList, const CustomFilterParameterList& toList, double progress, CustomFilterParameterList& resultList)
+{
+    // This method expects both lists to be sorted by parameter name and the result list is also sorted.
+    ASSERT(checkCustomFilterParametersOrder(fromList));
+    ASSERT(checkCustomFilterParametersOrder(toList));
+    size_t fromListIndex = 0, toListIndex = 0;
+    while (fromListIndex < fromList.size() && toListIndex < toList.size()) {
+        CustomFilterParameter* paramFrom = fromList.at(fromListIndex).get();
+        CustomFilterParameter* paramTo = toList.at(toListIndex).get();
+        if (paramFrom->name() == paramTo->name()) {
+            resultList.append(paramTo->blend(paramFrom, progress));
+            ++fromListIndex;
+            ++toListIndex;
+            continue;
+        }
+        if (codePointCompareLessThan(paramFrom->name(), paramTo->name())) {
+            resultList.append(paramFrom);
+            ++fromListIndex;
+            continue;
+        }
+        resultList.append(paramTo);
+        ++toListIndex;
+    }
+    for (; fromListIndex < fromList.size(); ++fromListIndex)
+        resultList.append(fromList.at(fromListIndex));
+    for (; toListIndex < toList.size(); ++toListIndex)
+        resultList.append(toList.at(toListIndex));
+    ASSERT(checkCustomFilterParametersOrder(resultList));
+}
+
 CustomFilterOperation::CustomFilterOperation(PassRefPtr<CustomFilterProgram> program, const CustomFilterParameterList& sortedParameters, unsigned meshRows, unsigned meshColumns, MeshBoxType meshBoxType, MeshType meshType)
     : FilterOperation(CUSTOM)
     , m_program(program)
@@ -50,24 +104,33 @@
     , m_meshType(meshType)
 {
     // Make sure that the parameters are alwyas sorted by name. We use that to merge two CustomFilterOperations in animations.
-    ASSERT(hasSortedParameterList());
+    ASSERT(checkCustomFilterParametersOrder(m_parameters));
 }
     
 CustomFilterOperation::~CustomFilterOperation()
 {
 }
 
-#ifndef NDEBUG
-bool CustomFilterOperation::hasSortedParameterList()
+PassRefPtr<FilterOperation> CustomFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough)
 {
-    for (unsigned i = 1; i < m_parameters.size(); ++i) {
-        // Break for equal or not-sorted parameters.
-        if (!codePointCompareLessThan(m_parameters.at(i - i)->name(), m_parameters.at(i)->name()))
-            return false;
-    }
-    return true;
+    // FIXME: There's no way to decide what is the "passthrough filter" for shaders using the current CSS Syntax.
+    // https://bugs.webkit.org/show_bug.cgi?id=84903
+    // https://www.w3.org/Bugs/Public/show_bug.cgi?id=16861
+    if (blendToPassthrough || !from || !from->isSameType(*this))
+        return this;
+    
+    const CustomFilterOperation* fromOp = static_cast<const CustomFilterOperation*>(from);
+    if (*m_program.get() != *fromOp->m_program.get()
+        || m_meshRows != fromOp->m_meshRows
+        || m_meshColumns != fromOp->m_meshColumns
+        || m_meshBoxType != fromOp->m_meshBoxType
+        || m_meshType != fromOp->m_meshType)
+        return this;
+    
+    CustomFilterParameterList animatedParameters;
+    blendCustomFilterParameters(fromOp->m_parameters, m_parameters, progress, animatedParameters);
+    return CustomFilterOperation::create(m_program, animatedParameters, m_meshRows, m_meshColumns, m_meshBoxType, m_meshType);
 }
-#endif
 
 } // namespace WebCore
 

Modified: trunk/Source/WebCore/platform/graphics/filters/CustomFilterOperation.h (115486 => 115487)


--- trunk/Source/WebCore/platform/graphics/filters/CustomFilterOperation.h	2012-04-27 20:29:31 UTC (rev 115486)
+++ trunk/Source/WebCore/platform/graphics/filters/CustomFilterOperation.h	2012-04-27 20:30:40 UTC (rev 115487)
@@ -43,6 +43,9 @@
 class CustomFilterParameter;
 typedef Vector<RefPtr<CustomFilterParameter> > CustomFilterParameterList;
 
+bool customFilterParametersEqual(const CustomFilterParameterList&, const CustomFilterParameterList&);
+void blendCustomFilterParameters(const CustomFilterParameterList& listFrom, const CustomFilterParameterList& listTo, double progress, CustomFilterParameterList& resultList);
+
 class CustomFilterOperation : public FilterOperation {
 public:
     enum MeshBoxType {
@@ -83,6 +86,8 @@
     
     virtual bool affectsOpacity() const { return true; }
     virtual bool movesPixels() const { return true; }
+    
+    virtual PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false);
 private:
     virtual bool operator==(const FilterOperation& o) const
     {
@@ -90,18 +95,15 @@
             return false;
 
         const CustomFilterOperation* other = static_cast<const CustomFilterOperation*>(&o);
-        return m_program.get() == other->m_program.get()
+        return *m_program.get() == *other->m_program.get()
                && m_meshRows == other->m_meshRows
                && m_meshColumns == other->m_meshColumns
                && m_meshBoxType == other->m_meshBoxType
-               && m_meshType == other->m_meshType;
+               && m_meshType == other->m_meshType
+               && customFilterParametersEqual(m_parameters, other->m_parameters);
     }
     
     CustomFilterOperation(PassRefPtr<CustomFilterProgram>, const CustomFilterParameterList&, unsigned meshRows, unsigned meshColumns, MeshBoxType, MeshType);
-    
-#ifndef NDEBUG
-    bool hasSortedParameterList();
-#endif
 
     RefPtr<CustomFilterProgram> m_program;
     CustomFilterParameterList m_parameters;

Modified: trunk/Source/WebCore/platform/graphics/filters/CustomFilterParameter.h (115486 => 115487)


--- trunk/Source/WebCore/platform/graphics/filters/CustomFilterParameter.h	2012-04-27 20:29:31 UTC (rev 115486)
+++ trunk/Source/WebCore/platform/graphics/filters/CustomFilterParameter.h	2012-04-27 20:30:40 UTC (rev 115487)
@@ -52,7 +52,12 @@
     
     ParameterType parameterType() const { return m_type; }
     const String& name() const { return m_name; }
-
+    
+    bool isSameType(const CustomFilterParameter& other) const { return parameterType() == other.parameterType(); }
+    
+    virtual PassRefPtr<CustomFilterParameter> blend(const CustomFilterParameter*, double progress) = 0;
+    virtual bool operator==(const CustomFilterParameter&) const = 0;
+    bool operator!=(const CustomFilterParameter& o) const { return !(*this == o); }
 protected:
     CustomFilterParameter(ParameterType type, const String& name)
         : m_name(name)

Modified: trunk/Source/WebCore/platform/graphics/filters/CustomFilterProgram.h (115486 => 115487)


--- trunk/Source/WebCore/platform/graphics/filters/CustomFilterProgram.h	2012-04-27 20:29:31 UTC (rev 115486)
+++ trunk/Source/WebCore/platform/graphics/filters/CustomFilterProgram.h	2012-04-27 20:30:40 UTC (rev 115487)
@@ -57,6 +57,10 @@
     PassRefPtr<CustomFilterShader> createShaderWithContext(GraphicsContext3D*);
 #endif
 
+    // StyleCustomFilterProgram has the only implementation for the following method. That means, it casts to StyleCustomFilterProgram
+    // withouth checking the type. If you add another implementation, also add a mechanism to check for the correct type.
+    virtual bool operator==(const CustomFilterProgram&) const = 0;
+    bool operator!=(const CustomFilterProgram& o) const { return !(*this == o); }
 protected:
     // StyleCustomFilterProgram can notify the clients that the cached resources are
     // loaded and it is ready to create CustomFilterShader objects.

Modified: trunk/Source/WebCore/rendering/style/StyleCustomFilterProgram.h (115486 => 115487)


--- trunk/Source/WebCore/rendering/style/StyleCustomFilterProgram.h	2012-04-27 20:29:31 UTC (rev 115486)
+++ trunk/Source/WebCore/rendering/style/StyleCustomFilterProgram.h	2012-04-27 20:30:40 UTC (rev 115487)
@@ -113,6 +113,16 @@
         if (isLoaded())
             notifyClients();
     }
+    
+    CachedShader* cachedVertexShader() const { return m_vertexShader ? m_vertexShader->cachedShader() : 0; }
+    CachedShader* cachedFragmentShader() const { return m_fragmentShader ? m_fragmentShader->cachedShader() : 0; }
+    
+    virtual bool operator==(const CustomFilterProgram& o) const 
+    {
+        // The following cast is ugly, but StyleCustomFilterProgram is the single implementation of CustomFilterProgram.
+        const StyleCustomFilterProgram* other = static_cast<const StyleCustomFilterProgram*>(&o);
+        return cachedVertexShader() == other->cachedVertexShader() && cachedFragmentShader() == other->cachedFragmentShader();
+    }
 
 private:
     StyleCustomFilterProgram(PassRefPtr<StyleShader> vertexShader, PassRefPtr<StyleShader> fragmentShader)
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to