[CSS Shaders] Implement transform parameter animations for CSS Custom Filters
authorachicu@adobe.com <achicu@adobe.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Sep 2012 23:33:41 +0000 (23:33 +0000)
committerachicu@adobe.com <achicu@adobe.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Sep 2012 23:33:41 +0000 (23:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=94980

Reviewed by Dean Jackson.

Based on patch from Joshua Netterfield <jnetterfield@rim.com>.

Source/WebCore:

According to Section 39.2 of Filter Effects 1.0 Editor's draft
(https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html), animations
of transforms should be handled following the CSS3 transform interpolation
rules. This patch implements this functionality.

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

* page/animation/CSSPropertyAnimation.cpp:
(WebCore::blendFunc):
(WebCore):
* platform/graphics/filters/CustomFilterNumberParameter.h:
(WebCore::CustomFilterNumberParameter::blend):
* platform/graphics/filters/CustomFilterOperation.cpp:
(WebCore::blendCustomFilterParameters):
(WebCore::CustomFilterOperation::blend):
* platform/graphics/filters/CustomFilterOperation.h:
(WebCore):
(WebCore::CustomFilterOperation::blendingNeedsRendererSize): Some filters need the box size, so that they could compute
Transforms. Right now only the CustomFilterOperation needs that, but I've implemented using this generic function.
(CustomFilterOperation):
* platform/graphics/filters/CustomFilterParameter.h:
(CustomFilterParameter):
* platform/graphics/filters/CustomFilterTransformParameter.h:
(CustomFilterTransformParameter):
(WebCore::CustomFilterTransformParameter::blend):
* platform/graphics/filters/FilterOperation.h:
(WebCore::FilterOperation::FilterOperation::blend):
(FilterOperation):
(WebCore::FilterOperation::FilterOperation::blendingNeedsRendererSize):
* platform/graphics/transforms/TransformOperations.cpp: Extracted the blending functions from CSSPropertyAnimation.cpp
so that they could be reused from other classes.
(WebCore::TransformOperations::blendByMatchingOperations):
(WebCore):
(WebCore::TransformOperations::blendByUsingMatrixInterpolation): Used when the TransformOperations do not match.
(WebCore::TransformOperations::blend): Uses when the caller doesn't know whether the TransformOperations match or not.
* platform/graphics/transforms/TransformOperations.h:
(TransformOperations):

LayoutTests:

Added test transform animations in css3/filters/custom/custom-filter-transform-animation.html.

* animations/resources/animation-test-helpers.js:
(customFilterParameterMatch): Extracted the parameter matching from filterParametersMatch and added function parameters matching.
This should work for all functions that have number parameters, ie. arrays, mat4 functions.

(filterParametersMatch):
* css3/filters/custom/custom-filter-transforms-animation-expected.txt: Added.
* css3/filters/custom/custom-filter-transforms-animation.html: Added.
* css3/filters/resources/custom-filter-parser.js: Fixed function parsing.
(TokenStream.prototype.skip):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@128380 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
LayoutTests/ChangeLog
LayoutTests/animations/resources/animation-test-helpers.js
LayoutTests/css3/filters/custom/custom-filter-transforms-animation-expected.txt [new file with mode: 0644]
LayoutTests/css3/filters/custom/custom-filter-transforms-animation.html [new file with mode: 0644]
LayoutTests/css3/filters/resources/custom-filter-parser.js
Source/WebCore/ChangeLog
Source/WebCore/page/animation/CSSPropertyAnimation.cpp
Source/WebCore/platform/graphics/filters/CustomFilterNumberParameter.h
Source/WebCore/platform/graphics/filters/CustomFilterOperation.cpp
Source/WebCore/platform/graphics/filters/CustomFilterOperation.h
Source/WebCore/platform/graphics/filters/CustomFilterParameter.h
Source/WebCore/platform/graphics/filters/CustomFilterTransformParameter.h
Source/WebCore/platform/graphics/filters/FilterOperation.h
Source/WebCore/platform/graphics/transforms/TransformOperations.cpp
Source/WebCore/platform/graphics/transforms/TransformOperations.h

index 54900f586a52a90836a2de0d0ee2b9e6bccda0e0..4a467cb06b66b8419729f6084814c4de3f840109 100644 (file)
@@ -1,3 +1,24 @@
+2012-09-12  Alexandru Chiculita  <achicu@adobe.com>
+
+        [CSS Shaders] Implement transform parameter animations for CSS Custom Filters
+        https://bugs.webkit.org/show_bug.cgi?id=94980
+
+        Reviewed by Dean Jackson.
+
+        Based on patch from Joshua Netterfield <jnetterfield@rim.com>.
+
+        Added test transform animations in css3/filters/custom/custom-filter-transform-animation.html.
+
+        * animations/resources/animation-test-helpers.js:
+        (customFilterParameterMatch): Extracted the parameter matching from filterParametersMatch and added function parameters matching.
+        This should work for all functions that have number parameters, ie. arrays, mat4 functions.
+        
+        (filterParametersMatch):
+        * css3/filters/custom/custom-filter-transforms-animation-expected.txt: Added.
+        * css3/filters/custom/custom-filter-transforms-animation.html: Added.
+        * css3/filters/resources/custom-filter-parser.js: Fixed function parsing.
+        (TokenStream.prototype.skip):
+
 2012-09-12  Chris Fleizach  <cfleizach@apple.com>
 
         AX: svg:image not accessible
index 42598ccaccffd7a13c32a3e208437453cbe5e5d6..4691862cc25bcea6fa33855e0be0586f86c5865f 100644 (file)
@@ -84,6 +84,45 @@ function getFilterParameters(s)
     return paramList;
 }
 
+function customFilterParameterMatch(param1, param2, tolerance)
+{
+    if (param1.type != "parameter") {
+        // Checking for shader uris and other keywords. They need to be exactly the same.
+        return (param1.type == param2.type && param1.value == param2.value);
+    }
+
+    if (param1.name != param2.name || param1.value.length != param2.value.length)
+        return false;
+
+    for (var j = 0; j < param1.value.length; ++j) {
+        var val1 = param1.value[j],
+            val2 = param2.value[j];
+        if (val1.type != val2.type)
+            return false;
+        switch (val1.type) {
+        case "function":
+            if (val1.name != val2.name)
+                return false;
+            for (var t = 0; t < val1.arguments.length; ++t) {
+                if (val1.arguments[t].type != "number" || val2.arguments[t].type != "number")
+                    return false;
+                if (!isCloseEnough(val1.arguments[t].value, val2.arguments[t].value, tolerance))
+                    return false;
+            }
+            break;
+        case "number":
+            if (!isCloseEnough(val1.value, val2.value, tolerance))
+                return false;
+            break;
+        default:
+            console.error("Unsupported parameter type ", val1.type);
+            return false;
+        }
+    }
+
+    return true;
+}
+
 function filterParametersMatch(paramList1, paramList2, tolerance)
 {
     if (paramList1.length != paramList2.length)
@@ -93,21 +132,8 @@ function filterParametersMatch(paramList1, paramList2, tolerance)
             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)
+            if (!customFilterParameterMatch(param1, param2, tolerance))
                 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);
diff --git a/LayoutTests/css3/filters/custom/custom-filter-transforms-animation-expected.txt b/LayoutTests/css3/filters/custom/custom-filter-transforms-animation-expected.txt
new file mode 100644 (file)
index 0000000..8a3056c
--- /dev/null
@@ -0,0 +1,9 @@
+     
+PASS - "webkitFilter" property for "custom-mix-from-none-box" element at 1s saw something close to: custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform matrix(30, 0, 0, 30, 0, 0))
+PASS - "webkitFilter" property for "custom-mix-to-none-box" element at 1s saw something close to: custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform matrix(30, 0, 0, 30, 0, 0))
+PASS - "webkitFilter" property for "custom-mix-single-transform-box" element at 1s saw something close to: custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform matrix(15, 0, 0, 15, 0, 0))
+PASS - "webkitFilter" property for "custom-mix-different-transforms-box" element at 1s saw something close to: custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform matrix(100, 0, 0, 100, 0, 0))
+PASS - "webkitFilter" property for "custom-mix-multi-transform-box" element at 1s saw something close to: custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform1 matrix(15, 0, 0, 15, 0, 0), transform2 matrix(100, 0, 0, 100, 0, 0))
+PASS - "webkitFilter" property for "custom-mix-different-transforms-count1-box" element at 1s saw something close to: custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform matrix(50, 0, 0, 50, 0, 0))
+PASS - "webkitFilter" property for "custom-mix-different-transforms-count2-box" element at 1s saw something close to: custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform matrix(50, 0, 0, 50, 0, 0))
+
diff --git a/LayoutTests/css3/filters/custom/custom-filter-transforms-animation.html b/LayoutTests/css3/filters/custom/custom-filter-transforms-animation.html
new file mode 100644 (file)
index 0000000..92e3efa
--- /dev/null
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+    <script>
+        if (window.testRunner) {
+            window.testRunner.overridePreference("WebKitCSSCustomFilterEnabled", "1");
+            window.testRunner.overridePreference("WebKitWebGLEnabled", "1");
+        }
+    </script>
+    <style>
+        .box {
+            height: 100px;
+            width: 100px;
+            margin: 10px;
+            background-color: blue;
+            display: inline-block;
+        }
+
+        #custom-mix-from-none-box {
+            -webkit-animation: custom-mix-from-none-anim 2s linear;
+        }
+
+        #custom-mix-to-none-box {
+            -webkit-animation: custom-mix-to-none-anim 2s linear;
+        }
+
+        #custom-mix-single-transform-box {
+            -webkit-animation: custom-mix-single-transform-anim 2s linear;
+        }
+
+        #custom-mix-different-transforms-box {
+            -webkit-animation: custom-mix-different-transforms-anim 2s linear;
+        }
+
+        #custom-mix-multi-transform-box {
+            -webkit-animation: custom-mix-multi-transform-anim 2s linear;
+        }
+
+        #custom-mix-different-transforms-count1-box {
+            -webkit-animation: custom-mix-different-transforms-count1-anim 2s linear;
+        }
+
+        #custom-mix-different-transforms-count2-box {
+            -webkit-animation: custom-mix-different-transforms-count2-anim 2s linear;
+        }
+
+        @-webkit-keyframes custom-mix-from-none-anim {
+            from { -webkit-filter: custom(url(no-shader-needed.vs)); }
+            to   { -webkit-filter: custom(url(no-shader-needed.vs), transform scale(30)); }
+        }
+
+        @-webkit-keyframes custom-mix-to-none-anim {
+            from { -webkit-filter: custom(url(no-shader-needed.vs), transform scale(30)); }
+            to   { -webkit-filter: custom(url(no-shader-needed.vs)); }
+        }
+    
+        @-webkit-keyframes custom-mix-single-transform-anim {
+            from { -webkit-filter: custom(url(no-shader-needed.vs), transform scale(0)); }
+            to   { -webkit-filter: custom(url(no-shader-needed.vs), transform scale(30)); }
+        }
+
+        @-webkit-keyframes custom-mix-different-transforms-anim {
+            from { -webkit-filter: custom(url(no-shader-needed.vs), transform scale(100) rotate(0deg)); }
+            to   { -webkit-filter: custom(url(no-shader-needed.vs), transform rotate(0deg) scale(100)); }
+        }
+
+        @-webkit-keyframes custom-mix-multi-transform-anim {
+            from { -webkit-filter: custom(url(no-shader-needed.vs), transform1 scale(0), transform2 scale(100) rotate(0deg)); }
+            to   { -webkit-filter: custom(url(no-shader-needed.vs), transform1 scale(30), transform2 rotate(0deg) scale(100)); }
+        }
+
+        @-webkit-keyframes custom-mix-different-transforms-count1-anim {
+            from { -webkit-filter: custom(url(no-shader-needed.vs), transform scale(1)); }
+            to   { -webkit-filter: custom(url(no-shader-needed.vs), transform rotate(0deg) scale(100)); }
+        }
+
+        @-webkit-keyframes custom-mix-different-transforms-count2-anim {
+            from { -webkit-filter: custom(url(no-shader-needed.vs), transform rotate(0deg) scale(100)); }
+            to   { -webkit-filter: custom(url(no-shader-needed.vs), transform scale(1)); }
+        }
+    </style>
+    <script src="../resources/custom-filter-parser.js"></script>
+    <script src="../../../animations/resources/animation-test-helpers.js"></script>
+    <script type="text/javascript">
+        const expectedValues = [
+            // [animation-name, time, element-id, property, expected-value, tolerance]
+            ["custom-mix-from-none-anim",  1, "custom-mix-from-none-box", "webkitFilter", 'custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform matrix(30, 0, 0, 30, 0, 0))', 2],
+            ["custom-mix-to-none-anim",  1, "custom-mix-to-none-box", "webkitFilter", 'custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform matrix(30, 0, 0, 30, 0, 0))', 2],
+            ["custom-mix-single-transform-anim",  1, "custom-mix-single-transform-box", "webkitFilter", 'custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform matrix(15, 0, 0, 15, 0, 0))', 2],
+            ["custom-mix-different-transforms-anim",  1, "custom-mix-different-transforms-box", "webkitFilter", 'custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform matrix(100, 0, 0, 100, 0, 0))', 0],
+            ["custom-mix-multi-transform-anim",  1, "custom-mix-multi-transform-box", "webkitFilter", 'custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform1 matrix(15, 0, 0, 15, 0, 0), transform2 matrix(100, 0, 0, 100, 0, 0))', 2],
+            ["custom-mix-different-transforms-count1-anim",  1, "custom-mix-different-transforms-count1-box", "webkitFilter", 'custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform matrix(50, 0, 0, 50, 0, 0))', 2],
+            ["custom-mix-different-transforms-count2-anim",  1, "custom-mix-different-transforms-count2-box", "webkitFilter", 'custom(url(no-shader-needed.vs) none, 1 1 filter-box, transform matrix(50, 0, 0, 50, 0, 0))', 2]
+        ];
+        runAnimationTest(expectedValues);
+    </script>
+</head>
+<body>
+    <div class="box" id="custom-mix-from-none-box"></div>
+    <div class="box" id="custom-mix-to-none-box"></div>
+    <div class="box" id="custom-mix-single-transform-box"></div>
+    <div class="box" id="custom-mix-different-transforms-box"></div>
+    <div class="box" id="custom-mix-multi-transform-box"></div>
+    <div class="box" id="custom-mix-different-transforms-count1-box"></div>
+    <div class="box" id="custom-mix-different-transforms-count2-box"></div>
+    <div id="result">
+    </div>
+</body>
+</html>
index 8a30b62591f4089b2c8930bc9ef9ca9b0a3e7ad8..b8e2481877af3d00bbf508b5d687ec239d4c993f 100644 (file)
@@ -86,7 +86,7 @@ TokenStream.prototype.ahead = function()
 // 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)) {
+    if (!typeMatcher || this.current().isA(typeMatcher)) {
         var token = this.current();
         ++this.tokenIndex;
         return token;
index 967f490bddc39d6cc7aee262be54752b6b164546..31e1797cb5f7db8b1c37dd4789943561713c2a5d 100644 (file)
@@ -1,3 +1,50 @@
+2012-09-12  Alexandru Chiculita  <achicu@adobe.com>
+
+        [CSS Shaders] Implement transform parameter animations for CSS Custom Filters
+        https://bugs.webkit.org/show_bug.cgi?id=94980
+
+        Reviewed by Dean Jackson.
+
+        Based on patch from Joshua Netterfield <jnetterfield@rim.com>.
+
+        According to Section 39.2 of Filter Effects 1.0 Editor's draft
+        (https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html), animations
+        of transforms should be handled following the CSS3 transform interpolation
+        rules. This patch implements this functionality.
+
+        Test: css3/filters/custom/custom-filter-transforms-animation.html
+
+        * page/animation/CSSPropertyAnimation.cpp:
+        (WebCore::blendFunc):
+        (WebCore):
+        * platform/graphics/filters/CustomFilterNumberParameter.h:
+        (WebCore::CustomFilterNumberParameter::blend):
+        * platform/graphics/filters/CustomFilterOperation.cpp:
+        (WebCore::blendCustomFilterParameters):
+        (WebCore::CustomFilterOperation::blend):
+        * platform/graphics/filters/CustomFilterOperation.h:
+        (WebCore):
+        (WebCore::CustomFilterOperation::blendingNeedsRendererSize): Some filters need the box size, so that they could compute
+        Transforms. Right now only the CustomFilterOperation needs that, but I've implemented using this generic function.
+        (CustomFilterOperation):
+        * platform/graphics/filters/CustomFilterParameter.h:
+        (CustomFilterParameter):
+        * platform/graphics/filters/CustomFilterTransformParameter.h:
+        (CustomFilterTransformParameter):
+        (WebCore::CustomFilterTransformParameter::blend):
+        * platform/graphics/filters/FilterOperation.h:
+        (WebCore::FilterOperation::FilterOperation::blend):
+        (FilterOperation):
+        (WebCore::FilterOperation::FilterOperation::blendingNeedsRendererSize):
+        * platform/graphics/transforms/TransformOperations.cpp: Extracted the blending functions from CSSPropertyAnimation.cpp
+        so that they could be reused from other classes.
+        (WebCore::TransformOperations::blendByMatchingOperations):
+        (WebCore):
+        (WebCore::TransformOperations::blendByUsingMatrixInterpolation): Used when the TransformOperations do not match.
+        (WebCore::TransformOperations::blend): Uses when the caller doesn't know whether the TransformOperations match or not.
+        * platform/graphics/transforms/TransformOperations.h:
+        (TransformOperations):
+
 2012-09-12  Alec Flett  <alecflett@chromium.org>
 
         IndexedDB: Use ScriptValue instead of SerializedScriptValue when possible
index f442755a0e04864c6071cb6042c6e7bd72afd01f..eadc831a8cbf83f8da8bb41d4666e14a5a35a22d 100644 (file)
@@ -120,44 +120,22 @@ static inline PassOwnPtr<ShadowData> blendFunc(const AnimationBase* anim, const
 
 static inline TransformOperations blendFunc(const AnimationBase* anim, const TransformOperations& from, const TransformOperations& to, double progress)
 {
-    TransformOperations result;
-
-    // If we have a transform function list, use that to do a per-function animation. Otherwise do a Matrix animation
-    if (anim->isTransformFunctionListValid()) {
-        unsigned fromSize = from.operations().size();
-        unsigned toSize = to.operations().size();
-        unsigned size = max(fromSize, toSize);
-        for (unsigned i = 0; i < size; i++) {
-            RefPtr<TransformOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : 0;
-            RefPtr<TransformOperation> toOp = (i < toSize) ? to.operations()[i].get() : 0;
-            RefPtr<TransformOperation> blendedOp = toOp ? toOp->blend(fromOp.get(), progress) : (fromOp ? fromOp->blend(0, progress, true) : PassRefPtr<TransformOperation>(0));
-            if (blendedOp)
-                result.operations().append(blendedOp);
-            else {
-                RefPtr<TransformOperation> identityOp = IdentityTransformOperation::create();
-                if (progress > 0.5)
-                    result.operations().append(toOp ? toOp : identityOp);
-                else
-                    result.operations().append(fromOp ? fromOp : identityOp);
-            }
-        }
-    } else {
-        // Convert the TransformOperations into matrices
-        LayoutSize size = anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : LayoutSize();
-        TransformationMatrix fromT;
-        TransformationMatrix toT;
-        from.apply(size, fromT);
-        to.apply(size, toT);
-
-        toT.blend(fromT, progress);
+    if (anim->isTransformFunctionListValid())
+        return to.blendByMatchingOperations(from, progress);
+    return to.blendByUsingMatrixInterpolation(from, progress, anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : LayoutSize());
+}
 
-        // Append the result
-        result.operations().append(Matrix3DTransformOperation::create(toT));
+#if ENABLE(CSS_FILTERS)
+static inline PassRefPtr<FilterOperation> blendFunc(const AnimationBase* anim, FilterOperation* fromOp, FilterOperation* toOp, double progress, bool blendToPassthrough = false)
+{
+    ASSERT(toOp);
+    if (toOp->blendingNeedsRendererSize()) {
+        LayoutSize size = anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : LayoutSize();
+        return toOp->blend(fromOp, progress, size, blendToPassthrough);
     }
-    return result;
+    return toOp->blend(fromOp, progress, blendToPassthrough);
 }
 
-#if ENABLE(CSS_FILTERS)
 static inline FilterOperations blendFunc(const AnimationBase* anim, const FilterOperations& from, const FilterOperations& to, double progress)
 {
     FilterOperations result;
@@ -170,7 +148,7 @@ static inline FilterOperations blendFunc(const AnimationBase* anim, const Filter
         for (size_t i = 0; i < size; i++) {
             RefPtr<FilterOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : 0;
             RefPtr<FilterOperation> toOp = (i < toSize) ? to.operations()[i].get() : 0;
-            RefPtr<FilterOperation> blendedOp = toOp ? toOp->blend(fromOp.get(), progress) : (fromOp ? fromOp->blend(0, progress, true) : PassRefPtr<FilterOperation>(0));
+            RefPtr<FilterOperation> blendedOp = toOp ? blendFunc(anim, fromOp.get(), toOp.get(), progress) : (fromOp ? blendFunc(anim, 0, fromOp.get(), progress, true) : 0);
             if (blendedOp)
                 result.operations().append(blendedOp);
             else {
index 2770b4ced6cf583034e071279f80a4586c7f762e..423ef547ec48a2e3b4a930ea8c20f9032bddc934 100644 (file)
@@ -48,7 +48,7 @@ public:
 
     void addValue(double value) { m_data.append(value); }
     
-    virtual PassRefPtr<CustomFilterParameter> blend(const CustomFilterParameter* from, double progress)
+    virtual PassRefPtr<CustomFilterParameter> blend(const CustomFilterParameter* from, double progress, const LayoutSize&)
     {
         if (!from || !isSameType(*from))
             return this;
index f5c81724f787d57bc00f8a9426efb880fc92b5e2..ed57b7374d463297819b71e65a818ef97e929580 100644 (file)
@@ -64,7 +64,8 @@ static bool checkCustomFilterParametersOrder(const CustomFilterParameterList& pa
 }
 #endif
 
-void blendCustomFilterParameters(const CustomFilterParameterList& fromList, const CustomFilterParameterList& toList, double progress, CustomFilterParameterList& resultList)
+void blendCustomFilterParameters(const CustomFilterParameterList& fromList, const CustomFilterParameterList& toList, 
+                                 double progress, const LayoutSize& size, CustomFilterParameterList& resultList)
 {
     // This method expects both lists to be sorted by parameter name and the result list is also sorted.
     ASSERT(checkCustomFilterParametersOrder(fromList));
@@ -74,7 +75,7 @@ void blendCustomFilterParameters(const CustomFilterParameterList& fromList, cons
         CustomFilterParameter* paramFrom = fromList.at(fromListIndex).get();
         CustomFilterParameter* paramTo = toList.at(toListIndex).get();
         if (paramFrom->name() == paramTo->name()) {
-            resultList.append(paramTo->blend(paramFrom, progress));
+            resultList.append(paramTo->blend(paramFrom, progress, size));
             ++fromListIndex;
             ++toListIndex;
             continue;
@@ -111,7 +112,7 @@ CustomFilterOperation::~CustomFilterOperation()
 {
 }
 
-PassRefPtr<FilterOperation> CustomFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough)
+PassRefPtr<FilterOperation> CustomFilterOperation::blend(const FilterOperation* from, double progress, const LayoutSize& size, bool blendToPassthrough)
 {
     // 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
@@ -128,7 +129,7 @@ PassRefPtr<FilterOperation> CustomFilterOperation::blend(const FilterOperation*
         return this;
     
     CustomFilterParameterList animatedParameters;
-    blendCustomFilterParameters(fromOp->m_parameters, m_parameters, progress, animatedParameters);
+    blendCustomFilterParameters(fromOp->m_parameters, m_parameters, progress, size, animatedParameters);
     return CustomFilterOperation::create(m_program, animatedParameters, m_meshRows, m_meshColumns, m_meshBoxType, m_meshType);
 }
 
index b4e8b29360b857898091cced1bc5780bcf79b374..788b3f01626b3f4af0520dd8c6f25af44a2540a3 100644 (file)
@@ -33,6 +33,7 @@
 #if ENABLE(CSS_SHADERS)
 #include "CustomFilterProgram.h"
 #include "FilterOperation.h"
+#include "LayoutTypes.h"
 
 #include <wtf/text/WTFString.h>
 
@@ -44,7 +45,8 @@ 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);
+void blendCustomFilterParameters(const CustomFilterParameterList& listFrom, const CustomFilterParameterList& listTo, 
+                                 double progress, const LayoutSize&, CustomFilterParameterList& resultList);
 
 class CustomFilterOperation : public FilterOperation {
 public:
@@ -79,8 +81,9 @@ public:
     
     virtual bool affectsOpacity() const { return true; }
     virtual bool movesPixels() const { return true; }
+    virtual bool blendingNeedsRendererSize() const { return true; }
     
-    virtual PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false);
+    virtual PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, const LayoutSize&, bool blendToPassthrough = false);
 private:
     virtual bool operator==(const FilterOperation& o) const
     {
index 82980d6c780f080cc7d703faa98c62b6f5d3ea84..81e5f83c2f91d97af89d79dac5698988f98e5cbd 100644 (file)
@@ -56,7 +56,7 @@ public:
     
     bool isSameType(const CustomFilterParameter& other) const { return parameterType() == other.parameterType(); }
     
-    virtual PassRefPtr<CustomFilterParameter> blend(const CustomFilterParameter*, double progress) = 0;
+    virtual PassRefPtr<CustomFilterParameter> blend(const CustomFilterParameter*, double progress, const LayoutSize&) = 0;
     virtual bool operator==(const CustomFilterParameter&) const = 0;
     bool operator!=(const CustomFilterParameter& o) const { return !(*this == o); }
 protected:
index bd66cec6c1c4a4a82fb9ae7c330573d7d7e7eb97..5bd6611a08b4a2c06182feefb8cd39cd3c27b787 100644 (file)
@@ -46,14 +46,26 @@ public:
     {
         return adoptRef(new CustomFilterTransformParameter(name));
     }
-    
-    virtual PassRefPtr<CustomFilterParameter> blend(const CustomFilterParameter*, double)
+
+    virtual PassRefPtr<CustomFilterParameter> blend(const CustomFilterParameter* fromParameter, double progress, const LayoutSize& size)
     {
-        // FIXME: Implement animations support.
-        // https://bugs.webkit.org/show_bug.cgi?id=94980
-        return this;
+        if (!fromParameter || !isSameType(*fromParameter))
+            return this;
+
+        const CustomFilterTransformParameter* fromTransformParameter = static_cast<const CustomFilterTransformParameter*>(fromParameter);
+        const TransformOperations& from = fromTransformParameter->operations();
+        const TransformOperations& to = operations();
+        if (from == to)
+            return this;
+       
+        RefPtr<CustomFilterTransformParameter> result = CustomFilterTransformParameter::create(name());
+        if (from.size() && to.size())
+            result->setOperations(to.blend(from, progress, size));
+        else
+            result->setOperations(progress > 0.5 ? to : from);
+        return result;
     }
-    
+
     virtual bool operator==(const CustomFilterParameter& o) const
     {
         if (!isSameType(o))
index e72600c47e364db6256b379f3ba463cd398d2443..6d2503ac39f208abab0dc2573889fe00ede661ab 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(CSS_FILTERS)
 
 #include "Color.h"
+#include "LayoutTypes.h"
 #include "Length.h"
 #include <wtf/OwnPtr.h>
 #include <wtf/PassOwnPtr.h>
@@ -78,7 +79,17 @@ public:
     virtual bool operator==(const FilterOperation&) const = 0;
     bool operator!=(const FilterOperation& o) const { return !(*this == o); }
 
-    virtual PassRefPtr<FilterOperation> blend(const FilterOperation* /*from*/, double /*progress*/, bool /*blendToPassthrough*/ = false) { return 0; }
+    virtual PassRefPtr<FilterOperation> blend(const FilterOperation* /*from*/, double /*progress*/, bool /*blendToPassthrough*/ = false)
+    { 
+        ASSERT(!blendingNeedsRendererSize());
+        return 0; 
+    }
+
+    virtual PassRefPtr<FilterOperation> blend(const FilterOperation* /*from*/, double /*progress*/, const LayoutSize&, bool /*blendToPassthrough*/ = false)
+    { 
+        ASSERT(blendingNeedsRendererSize());
+        return 0; 
+    }
 
     virtual OperationType getOperationType() const { return m_type; }
     virtual bool isSameType(const FilterOperation& o) const { return o.getOperationType() == m_type; }
@@ -89,6 +100,8 @@ public:
     virtual bool affectsOpacity() const { return false; }
     // True if the the value of one pixel can affect the value of another pixel under this operation, such as blur.
     virtual bool movesPixels() const { return false; }
+    // True if the filter needs the size of the box in order to calculate the animations.
+    virtual bool blendingNeedsRendererSize() const { return false; }
 
 protected:
     FilterOperation(OperationType type)
index c942a61692cf1ff2a2edd089541cbad5a0a3c67d..0e2a1db3320147a22ba1f40ca6fcc062f6addf59 100644 (file)
 #include "TransformOperations.h"
 
 #include "IdentityTransformOperation.h"
+#include "Matrix3DTransformOperation.h"
+#include <algorithm>
+
+using namespace std;
 
 namespace WebCore {
 
@@ -61,4 +65,58 @@ bool TransformOperations::operationsMatch(const TransformOperations& other) cons
     return true;
 }
 
+TransformOperations TransformOperations::blendByMatchingOperations(const TransformOperations& from, const double& progress) const
+{
+    TransformOperations result;
+
+    unsigned fromSize = from.operations().size();
+    unsigned toSize = operations().size();
+    unsigned size = max(fromSize, toSize);
+    for (unsigned i = 0; i < size; i++) {
+        RefPtr<TransformOperation> fromOperation = (i < fromSize) ? from.operations()[i].get() : 0;
+        RefPtr<TransformOperation> toOperation = (i < toSize) ? operations()[i].get() : 0;
+        RefPtr<TransformOperation> blendedOperation = toOperation ? toOperation->blend(fromOperation.get(), progress) : (fromOperation ? fromOperation->blend(0, progress, true) : 0);
+        if (blendedOperation)
+            result.operations().append(blendedOperation);
+        else {
+            RefPtr<TransformOperation> identityOperation = IdentityTransformOperation::create();
+            if (progress > 0.5)
+                result.operations().append(toOperation ? toOperation : identityOperation);
+            else
+                result.operations().append(fromOperation ? fromOperation : identityOperation);
+        }
+    }
+
+    return result;
+}
+
+TransformOperations TransformOperations::blendByUsingMatrixInterpolation(const TransformOperations& from, double progress, const LayoutSize& size) const
+{
+    TransformOperations result;
+
+    // Convert the TransformOperations into matrices
+    TransformationMatrix fromTransform;
+    TransformationMatrix toTransform;
+    from.apply(size, fromTransform);
+    apply(size, toTransform);
+
+    toTransform.blend(fromTransform, progress);
+
+    // Append the result
+    result.operations().append(Matrix3DTransformOperation::create(toTransform));
+
+    return result;
+}
+
+TransformOperations TransformOperations::blend(const TransformOperations& from, double progress, const LayoutSize& size) const
+{
+    if (from == *this)
+        return *this;
+
+    if (from.size() && from.operationsMatch(*this))
+        return blendByMatchingOperations(from, progress);
+
+    return blendByUsingMatrixInterpolation(from, progress, size);
+}
+
 } // namespace WebCore
index e236e12d920bec55621437ee839954b9ab6d3f9f..b7e091b25796bbbb666c7a9039327dc811e338a2 100644 (file)
@@ -25,6 +25,7 @@
 #ifndef TransformOperations_h
 #define TransformOperations_h
 
+#include "LayoutTypes.h"
 #include "TransformOperation.h"
 #include <wtf/RefPtr.h>
 #include <wtf/Vector.h>
@@ -71,6 +72,10 @@ public:
     size_t size() const { return m_operations.size(); }
     const TransformOperation* at(size_t index) const { return index < m_operations.size() ? m_operations.at(index).get() : 0; }
 
+    TransformOperations blendByMatchingOperations(const TransformOperations& from, const double& progress) const;
+    TransformOperations blendByUsingMatrixInterpolation(const TransformOperations& from, double progress, const LayoutSize&) const;
+    TransformOperations blend(const TransformOperations& from, double progress, const LayoutSize&) const;
+
 private:
     Vector<RefPtr<TransformOperation> > m_operations;
 };