2010-08-29 Simon Fraser <simon.fraser@apple.com>
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 29 Aug 2010 23:16:21 +0000 (23:16 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 29 Aug 2010 23:16:21 +0000 (23:16 +0000)
        Reviewed by Darin Adler.

        When properties are missing from animation keyframes, interpolate between those keyframes that specify them
        https://bugs.webkit.org/show_bug.cgi?id=40794

        When a property does not appear in all keyframes of a keyframe animation, we currently use the
        value of that property from the unanimated style. That forces the author to use additional
        keyframes for properties that need to "skip a keyframe", which is laborious.

        With this change, properties are interpolated between the keyframes in which they appear.
        This is equivalent to splitting each property out into its own set of keyframes.

        Tests: animations/missing-keyframe-properties-repeating.html
               animations/missing-keyframe-properties-timing-function.html
               animations/missing-keyframe-properties.html

        * css/CSSStyleSelector.h:
        * css/CSSStyleSelector.cpp:
        (WebCore::CSSStyleSelector::styleForKeyframe): Pass a KeyframeValue in so that we can
        collect which properties are represented per keyframe.
        (WebCore::CSSStyleSelector::keyframeStylesForAnimation): Keyframes are inserted into a
        KeyframeList by object now, rather than by key and style.

        * page/animation/AnimationBase.cpp:
        (WebCore::AnimationBase::progress): Use AnimationDirectionAlternate for readability.

        * page/animation/KeyframeAnimation.cpp:
        (WebCore::KeyframeAnimation::fetchIntervalEndpointsForProperty): Renamed from getKeyframeAnimationInterval.
        Use fractionalTime etc to match AnimationBase::progress(), and do lookups per-property.
        Simplify the code that finds the relevant keyframe to do less work.
        (WebCore::KeyframeAnimation::animate): Call fetchIntervalEndpointsForProperty() for each property, rather than just once for the
        entire keyframe.
        (WebCore::KeyframeAnimation::getAnimatedStyle): Ditto.
        (WebCore::KeyframeAnimation::hasAnimationForProperty): FIXME comment.

        * page/animation/KeyframeAnimation.h: Rename getKeyframeAnimationInterval() to fetchIntervalEndpointsForProperty().

        * rendering/RenderLayerBacking.cpp:
        (WebCore::RenderLayerBacking::startAnimation): For hardware animations, only insert values
        for keyframes which contain the property.

        (WebCore::KeyframeList::KeyframeList): insert() takes a KeyframeValue now.
        * rendering/style/KeyframeList.cpp:
        (WebCore::KeyframeList::operator==):
        (WebCore::KeyframeList::insert): Fix insert/replace logic, and ensure we maintain the
        m_properties hash.

        * rendering/style/KeyframeList.h:
        (WebCore::KeyframeValue::KeyframeValue): Make members private, with accessors.
        Add a m_properties HashSet for the properties animated in this keyframe.

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

18 files changed:
LayoutTests/ChangeLog
LayoutTests/animations/missing-from-to-transforms.html
LayoutTests/animations/missing-from-to.html
LayoutTests/animations/missing-keyframe-properties-expected.txt [new file with mode: 0644]
LayoutTests/animations/missing-keyframe-properties-repeating-expected.txt [new file with mode: 0644]
LayoutTests/animations/missing-keyframe-properties-repeating.html [new file with mode: 0644]
LayoutTests/animations/missing-keyframe-properties-timing-function-expected.txt [new file with mode: 0644]
LayoutTests/animations/missing-keyframe-properties-timing-function.html [new file with mode: 0644]
LayoutTests/animations/missing-keyframe-properties.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/css/CSSStyleSelector.cpp
WebCore/css/CSSStyleSelector.h
WebCore/page/animation/AnimationBase.cpp
WebCore/page/animation/KeyframeAnimation.cpp
WebCore/page/animation/KeyframeAnimation.h
WebCore/rendering/RenderLayerBacking.cpp
WebCore/rendering/style/KeyframeList.cpp
WebCore/rendering/style/KeyframeList.h

index be91f67..0dbf053 100644 (file)
@@ -1,3 +1,21 @@
+2010-08-29  Simon Fraser  <simon.fraser@apple.com>
+
+        Reviewed by Darin Adler.
+
+        When properties are missing from animation keyframes, interpolate between those keyframes that specify them
+        https://bugs.webkit.org/show_bug.cgi?id=40794
+
+        Testcases for keyframes with missing properties; repeating and non-repeating.
+
+        * animations/missing-from-to-transforms.html:
+        * animations/missing-from-to.html:
+        * animations/missing-keyframe-properties-expected.txt: Added.
+        * animations/missing-keyframe-properties-repeating-expected.txt: Added.
+        * animations/missing-keyframe-properties-repeating.html: Added.
+        * animations/missing-keyframe-properties-timing-function-expected.txt: Added.
+        * animations/missing-keyframe-properties-timing-function.html: Added.
+        * animations/missing-keyframe-properties.html: Added.
+
 2010-08-28  Darin Adler  <darin@apple.com>
 
         Reviewed by Sam Weinig.
index 110a5a2..44f958b 100644 (file)
@@ -89,7 +89,7 @@
     ];
 
     runAnimationTest(expectedValues, function() {
-      if (layoutTestController) {
+      if (window.layoutTestController) {
           if (layoutTestController.pauseAnimationAtTimeOnElementWithId("anim5", 0.1, "box5"))
               result += "FAIL - box5 animation was running<br>";
           else 
index 9b92a49..a28a530 100644 (file)
@@ -88,7 +88,7 @@
     ];
 
     runAnimationTest(expectedValues, function() {
-      if (layoutTestController) {
+      if (window.layoutTestController) {
           if (layoutTestController.pauseAnimationAtTimeOnElementWithId("anim5", 0.1, "box5"))
               result += "FAIL - box5 animation was running<br>";
           else 
diff --git a/LayoutTests/animations/missing-keyframe-properties-expected.txt b/LayoutTests/animations/missing-keyframe-properties-expected.txt
new file mode 100644 (file)
index 0000000..7cdc21c
--- /dev/null
@@ -0,0 +1,8 @@
+PASS - "left" property for "box1" element at 1s saw something close to: 200
+PASS - "webkitTransform.4" property for "box2" element at 1s saw something close to: 200
+PASS - "opacity" property for "box1" element at 0.5s saw something close to: 0.5
+PASS - "opacity" property for "box1" element at 1s saw something close to: 0
+PASS - "left" property for "box1" element at 1.5s saw something close to: 300
+PASS - "webkitTransform.4" property for "box2" element at 1.5s saw something close to: 300
+PASS - "opacity" property for "box1" element at 1.5s saw something close to: 0.5
+
diff --git a/LayoutTests/animations/missing-keyframe-properties-repeating-expected.txt b/LayoutTests/animations/missing-keyframe-properties-repeating-expected.txt
new file mode 100644 (file)
index 0000000..29c62b8
--- /dev/null
@@ -0,0 +1,4 @@
+PASS - "left" property for "box1" element at 1s saw something close to: 100
+PASS - "left" property for "box1" element at 2s saw something close to: 0
+PASS - "left" property for "box1" element at 3s saw something close to: 100
+
diff --git a/LayoutTests/animations/missing-keyframe-properties-repeating.html b/LayoutTests/animations/missing-keyframe-properties-repeating.html
new file mode 100644 (file)
index 0000000..c4c299e
--- /dev/null
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style type="text/css" media="screen">
+    #box1 {
+      position: relative;
+      height: 100px;
+      width: 100px;
+      left: 0;
+      background-color: blue;
+      margin: 0;
+      -webkit-animation-duration: 2s;
+      -webkit-animation-timing-function: linear;
+      -webkit-animation-iteration-count: 2;
+      -webkit-animation-direction: alternate;
+      -webkit-animation-name: move;
+    }
+    @-webkit-keyframes move {
+        from {
+          opacity: 1;
+        }
+        50% {
+          opacity: 1;
+          left: 100px;
+        }
+        to {
+          opacity: 1;
+        }
+    }
+    
+    /* Test reversing animation with missing values */
+
+  </style>
+  <script src="animation-test-helpers.js" type="text/javascript" charset="utf-8"></script>
+  <script type="text/javascript" charset="utf-8">
+    
+    const expectedValues = [
+      // [animation-name, time, element-id, property, expected-value, tolerance]
+      ["move", 1, "box1", "left", 100, 15],
+      ["move", 2, "box1", "left", 0, 15],
+      ["move", 3, "box1", "left", 100, 15],
+    ];
+    
+    runAnimationTest(expectedValues);
+  </script>
+</head>
+<body>
+<div id="box1"></div>
+<div id="result">
+</div>
+</body>
+</html>
diff --git a/LayoutTests/animations/missing-keyframe-properties-timing-function-expected.txt b/LayoutTests/animations/missing-keyframe-properties-timing-function-expected.txt
new file mode 100644 (file)
index 0000000..1accc47
--- /dev/null
@@ -0,0 +1,5 @@
+PASS - "left" property for "box1" element at 0.5s saw something close to: 132
+PASS - "webkitTransform.4" property for "box2" element at 0.5s saw something close to: 132
+PASS - "left" property for "box1" element at 1.5s saw something close to: 300
+PASS - "webkitTransform.4" property for "box2" element at 1.5s saw something close to: 300
+
diff --git a/LayoutTests/animations/missing-keyframe-properties-timing-function.html b/LayoutTests/animations/missing-keyframe-properties-timing-function.html
new file mode 100644 (file)
index 0000000..af467a6
--- /dev/null
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style type="text/css" media="screen">
+    .box {
+      position: relative;
+      margin: 10px;
+      left: 0;
+      width: 100px;
+      height: 100px;
+      background-color: blue;
+      -webkit-animation-duration: 2s;
+      -webkit-animation-timing-function: ease-in;
+    }
+    
+    #box1 {
+      -webkit-animation-name: move;
+    }
+    @-webkit-keyframes move {
+        from {
+          opacity: 0;
+          left: 100px;
+        }
+        25% {
+          opacity: 0.25;
+        }
+        50% {
+          left: 200px;
+          -webkit-animation-timing-function: linear;
+        }
+        to {
+          left: 400px;
+        }
+    }
+
+    #box2 {
+      -webkit-animation-name: move2;
+    }
+    @-webkit-keyframes move2 {
+        from {
+          opacity: 0;
+          -webkit-transform: translateX(100px);
+        }
+        25% {
+          opacity: 0.25;
+        }
+        50% {
+          -webkit-transform: translateX(200px);
+          -webkit-animation-timing-function: linear;
+        }
+        to {
+          -webkit-transform: translateX(400px);
+        }
+    }
+
+  </style>
+  <script src="animation-test-helpers.js" type="text/javascript" charset="utf-8"></script>
+  <script type="text/javascript" charset="utf-8">
+    
+    const expectedValues = [
+      // [animation-name, time, element-id, property, expected-value, tolerance]
+      ["move", 0.5, "box1", "left", 132, 15],
+      ["move2", 0.5, "box2", "webkitTransform.4", 132, 15],
+      ["move", 1.5, "box1", "left", 300, 15],
+      ["move2", 1.5, "box2", "webkitTransform.4", 300, 15],
+    ];
+    
+    runAnimationTest(expectedValues);
+  </script>
+</head>
+<body>
+<div id="box1" class="box"></div>
+<div id="box2" class="box"></div>
+<div id="result">
+</div>
+</body>
+</html>
diff --git a/LayoutTests/animations/missing-keyframe-properties.html b/LayoutTests/animations/missing-keyframe-properties.html
new file mode 100644 (file)
index 0000000..d4a6963
--- /dev/null
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style type="text/css" media="screen">
+    .box {
+      position: relative;
+      margin: 10px;
+      left: 0;
+      width: 100px;
+      height: 100px;
+      background-color: blue;
+      -webkit-animation-duration: 2s;
+      -webkit-animation-timing-function: linear;
+    }
+    
+    #box1 {
+      -webkit-animation-name: move;
+    }
+    @-webkit-keyframes move {
+        from {
+          left: 0px;
+        }
+        50% {
+          opacity: 0;
+        }
+        to {
+          left: 400px;
+        }
+    }
+
+    #box2 {
+      -webkit-animation-name: move2;
+    }
+    @-webkit-keyframes move2 {
+        from {
+          -webkit-transform: translateX(0);
+        }
+        50% {
+          opacity: 0;
+        }
+        to {
+          -webkit-transform: translateX(400px);
+        }
+    }
+
+  </style>
+  <script src="animation-test-helpers.js" type="text/javascript" charset="utf-8"></script>
+  <script type="text/javascript" charset="utf-8">
+    
+    const expectedValues = [
+      // [animation-name, time, element-id, property, expected-value, tolerance]
+      ["move", 1, "box1", "left", 200, 15],
+      ["move2", 1, "box2", "webkitTransform.4", 200, 15],
+      ["move", 0.5, "box1", "opacity", 0.5, 0.1],
+      ["move", 1, "box1", "opacity", 0, 0.1],
+      ["move", 1.5, "box1", "left", 300, 15],
+      ["move2", 1.5, "box2", "webkitTransform.4", 300, 15],
+      ["move", 1.5, "box1", "opacity", 0.5, 0.1],
+    ];
+    
+    runAnimationTest(expectedValues);
+  </script>
+</head>
+<body>
+<div id="box1" class="box"></div>
+<div id="box2" class="box"></div>
+<div id="result">
+</div>
+</body>
+</html>
index 2dcaaf8..88b2c03 100644 (file)
@@ -1,3 +1,57 @@
+2010-08-29  Simon Fraser  <simon.fraser@apple.com>
+
+        Reviewed by Darin Adler.
+
+        When properties are missing from animation keyframes, interpolate between those keyframes that specify them
+        https://bugs.webkit.org/show_bug.cgi?id=40794
+        
+        When a property does not appear in all keyframes of a keyframe animation, we currently use the
+        value of that property from the unanimated style. That forces the author to use additional
+        keyframes for properties that need to "skip a keyframe", which is laborious.
+        
+        With this change, properties are interpolated between the keyframes in which they appear.
+        This is equivalent to splitting each property out into its own set of keyframes.
+
+        Tests: animations/missing-keyframe-properties-repeating.html
+               animations/missing-keyframe-properties-timing-function.html
+               animations/missing-keyframe-properties.html
+
+        * css/CSSStyleSelector.h:
+        * css/CSSStyleSelector.cpp:
+        (WebCore::CSSStyleSelector::styleForKeyframe): Pass a KeyframeValue in so that we can
+        collect which properties are represented per keyframe.
+        (WebCore::CSSStyleSelector::keyframeStylesForAnimation): Keyframes are inserted into a
+        KeyframeList by object now, rather than by key and style.
+
+        * page/animation/AnimationBase.cpp:
+        (WebCore::AnimationBase::progress): Use AnimationDirectionAlternate for readability.
+
+        * page/animation/KeyframeAnimation.cpp:
+        (WebCore::KeyframeAnimation::fetchIntervalEndpointsForProperty): Renamed from getKeyframeAnimationInterval.
+        Use fractionalTime etc to match AnimationBase::progress(), and do lookups per-property.
+        Simplify the code that finds the relevant keyframe to do less work.
+        (WebCore::KeyframeAnimation::animate): Call fetchIntervalEndpointsForProperty() for each property, rather than just once for the
+        entire keyframe.
+        (WebCore::KeyframeAnimation::getAnimatedStyle): Ditto.
+        (WebCore::KeyframeAnimation::hasAnimationForProperty): FIXME comment.
+
+        * page/animation/KeyframeAnimation.h: Rename getKeyframeAnimationInterval() to fetchIntervalEndpointsForProperty().
+
+        * rendering/RenderLayerBacking.cpp:
+        (WebCore::RenderLayerBacking::startAnimation): For hardware animations, only insert values
+        for keyframes which contain the property.
+
+        (WebCore::KeyframeList::KeyframeList): insert() takes a KeyframeValue now.
+        * rendering/style/KeyframeList.cpp:
+        (WebCore::KeyframeList::operator==):
+        (WebCore::KeyframeList::insert): Fix insert/replace logic, and ensure we maintain the
+        m_properties hash.
+
+        * rendering/style/KeyframeList.h:
+        (WebCore::KeyframeValue::KeyframeValue): Make members private, with accessors.
+        Add a m_properties HashSet for the properties animated in this keyframe.
+
+
 2010-08-29  Csaba Osztrogon√°c  <ossy@webkit.org>
 
         Reviewed by Darin Adler.
index 0ddf4f7..a070c65 100644 (file)
@@ -1373,7 +1373,7 @@ PassRefPtr<RenderStyle> CSSStyleSelector::styleForElement(Element* e, RenderStyl
     return m_style.release();
 }
 
-PassRefPtr<RenderStyle> CSSStyleSelector::styleForKeyframe(const RenderStyle* elementStyle, const WebKitCSSKeyframeRule* keyframeRule, KeyframeList& list)
+PassRefPtr<RenderStyle> CSSStyleSelector::styleForKeyframe(const RenderStyle* elementStyle, const WebKitCSSKeyframeRule* keyframeRule, KeyframeValue& keyframe)
 {
     if (keyframeRule->style())
         addMatchedDeclaration(keyframeRule->style());
@@ -1410,7 +1410,7 @@ PassRefPtr<RenderStyle> CSSStyleSelector::styleForKeyframe(const RenderStyle* el
     // Start loading images referenced by this style.
     loadPendingImages();
 
-    // Add all the animating properties to the list
+    // Add all the animating properties to the keyframe.
     if (keyframeRule->style()) {
         CSSMutableStyleDeclaration::const_iterator end = keyframeRule->style()->end();
         for (CSSMutableStyleDeclaration::const_iterator it = keyframeRule->style()->begin(); it != end; ++it) {
@@ -1418,7 +1418,7 @@ PassRefPtr<RenderStyle> CSSStyleSelector::styleForKeyframe(const RenderStyle* el
             // Timing-function within keyframes is special, because it is not animated; it just
             // describes the timing function between this keyframe and the next.
             if (property != CSSPropertyWebkitAnimationTimingFunction)
-                list.addProperty(property);
+                keyframe.addProperty(property);
         }
     }
 
@@ -1439,7 +1439,6 @@ void CSSStyleSelector::keyframeStylesForAnimation(Element* e, const RenderStyle*
         return;
         
     const WebKitCSSKeyframesRule* rule = m_keyframesRuleMap.find(list.animationName().impl()).get()->second.get();
-    RefPtr<RenderStyle> keyframeStyle;
     
     // Construct and populate the style for each keyframe
     for (unsigned i = 0; i < rule->length(); ++i) {
@@ -1448,34 +1447,36 @@ void CSSStyleSelector::keyframeStylesForAnimation(Element* e, const RenderStyle*
         initForStyleResolve(e);
         
         const WebKitCSSKeyframeRule* keyframeRule = rule->item(i);
-        
-        keyframeStyle = styleForKeyframe(elementStyle, keyframeRule, list);
+
+        KeyframeValue keyframe(0, 0);
+        keyframe.setStyle(styleForKeyframe(elementStyle, keyframeRule, keyframe));
 
         // Add this keyframe style to all the indicated key times
         Vector<float> keys;
         keyframeRule->getKeys(keys);
         for (size_t keyIndex = 0; keyIndex < keys.size(); ++keyIndex) {
-            float key = keys[keyIndex];
-            list.insert(key, keyframeStyle.get());
+            keyframe.setKey(keys[keyIndex]);
+            list.insert(keyframe);
         }
-        keyframeStyle.release();
     }
     
     // If the 0% keyframe is missing, create it (but only if there is at least one other keyframe)
     int initialListSize = list.size();
     if (initialListSize > 0 && list[0].key() != 0) {
-        RefPtr<WebKitCSSKeyframeRule> keyframe = WebKitCSSKeyframeRule::create();
-        keyframe->setKeyText("0%");
-        keyframeStyle = styleForKeyframe(elementStyle, keyframe.get(), list);
-        list.insert(0, keyframeStyle.release());
+        RefPtr<WebKitCSSKeyframeRule> keyframeRule = WebKitCSSKeyframeRule::create();
+        keyframeRule->setKeyText("0%");
+        KeyframeValue keyframe(0, 0);
+        keyframe.setStyle(styleForKeyframe(elementStyle, keyframeRule.get(), keyframe));
+        list.insert(keyframe);
     }
 
     // If the 100% keyframe is missing, create it (but only if there is at least one other keyframe)
     if (initialListSize > 0 && (list[list.size() - 1].key() != 1)) {
-        RefPtr<WebKitCSSKeyframeRule> keyframe = WebKitCSSKeyframeRule::create();
-        keyframe->setKeyText("100%");
-        keyframeStyle = styleForKeyframe(elementStyle, keyframe.get(), list);
-        list.insert(1, keyframeStyle.release());
+        RefPtr<WebKitCSSKeyframeRule> keyframeRule = WebKitCSSKeyframeRule::create();
+        keyframeRule->setKeyText("100%");
+        KeyframeValue keyframe(1, 0);
+        keyframe.setStyle(styleForKeyframe(elementStyle, keyframeRule.get(), keyframe));
+        list.insert(keyframe);
     }
 }
 
index 7038b44..28d4488 100644 (file)
@@ -59,6 +59,7 @@ class Frame;
 class FrameView;
 class KURL;
 class KeyframeList;
+class KeyframeValue;
 class MediaQueryEvaluator;
 class Node;
 class Settings;
@@ -114,7 +115,7 @@ public:
 
         RenderStyle* style() const { return m_style.get(); }
 
-        PassRefPtr<RenderStyle> styleForKeyframe(const RenderStyle*, const WebKitCSSKeyframeRule*, KeyframeList&);
+        PassRefPtr<RenderStyle> styleForKeyframe(const RenderStyle*, const WebKitCSSKeyframeRule*, KeyframeValue&);
 
     public:
         // These methods will give back the set of rules that matched for a given element (or a pseudo-element).
index 4f304e2..d2e86bd 100644 (file)
@@ -1242,7 +1242,7 @@ double AnimationBase::progress(double scale, double offset, const TimingFunction
     int integralTime = static_cast<int>(fractionalTime);
     fractionalTime -= integralTime;
 
-    if (m_animation->direction() && (integralTime & 1))
+    if ((m_animation->direction() == Animation::AnimationDirectionAlternate) && (integralTime & 1))
         fractionalTime = 1 - fractionalTime;
 
     if (scale != 1 || offset)
index 2f5a73e..01ec2f1 100644 (file)
@@ -62,42 +62,67 @@ KeyframeAnimation::~KeyframeAnimation()
         endAnimation();
 }
 
-void KeyframeAnimation::getKeyframeAnimationInterval(const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& prog) const
+void KeyframeAnimation::fetchIntervalEndpointsForProperty(int property, const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& prog) const
 {
     // Find the first key
     double elapsedTime = getElapsedTime();
 
-    double t = m_animation->duration() ? (elapsedTime / m_animation->duration()) : 1;
+    double fractionalTime = m_animation->duration() ? (elapsedTime / m_animation->duration()) : 1;
 
-    ASSERT(t >= 0);
-    if (t < 0)
-        t = 0;
+    ASSERT(fractionalTime >= 0);
+    if (fractionalTime < 0)
+        fractionalTime = 0;
 
-    int i = static_cast<int>(t);
-    t -= i;
-    if (m_animation->direction() && (i & 1))
-        t = 1 - t;
+    // FIXME: share this code with AnimationBase::progress().
+    int iteration = static_cast<int>(fractionalTime);
+    fractionalTime -= iteration;
+    
+    bool reversing = (m_animation->direction() == Animation::AnimationDirectionAlternate) && (iteration & 1);
+    if (reversing)
+        fractionalTime = 1 - fractionalTime;
 
-    double scale = 1;
-    double offset = 0;
     size_t numKeyframes = m_keyframes.size();
+    if (!numKeyframes)
+        return;
+    
+    ASSERT(!m_keyframes[0].key());
+    ASSERT(m_keyframes[m_keyframes.size() - 1].key() == 1);
+    
+    int prevIndex = -1;
+    int nextIndex = -1;
+    
+    // FIXME: with a lot of keys, this linear search will be slow. We could binary search.
     for (size_t i = 0; i < numKeyframes; ++i) {
-        const KeyframeValue& currentKeyframe = m_keyframes[i];
-        if (t < currentKeyframe.key()) {
-            // The first key should always be 0, so we should never succeed on the first key
-            if (!fromStyle)
-                break;
-            scale = 1.0 / (currentKeyframe.key() - offset);
-            toStyle = currentKeyframe.style();
+        const KeyframeValue& currKeyFrame = m_keyframes[i];
+
+        if (!currKeyFrame.containsProperty(property))
+            continue;
+
+        if (fractionalTime < currKeyFrame.key()) {
+            nextIndex = i;
             break;
         }
-
-        offset = currentKeyframe.key();
-        fromStyle = currentKeyframe.style();
+        
+        prevIndex = i;
     }
 
-    if (!fromStyle || !toStyle)
-        return;
+    double scale = 1;
+    double offset = 0;
+
+    if (prevIndex == -1)
+        prevIndex = 0;
+
+    if (nextIndex == -1)
+        nextIndex = m_keyframes.size() - 1;
+
+    const KeyframeValue& prevKeyframe = m_keyframes[prevIndex];
+    const KeyframeValue& nextKeyframe = m_keyframes[nextIndex];
+
+    fromStyle = prevKeyframe.style();
+    toStyle = nextKeyframe.style();
+    
+    offset = prevKeyframe.key();
+    scale = 1.0 / (nextKeyframe.key() - prevKeyframe.key());
 
     const TimingFunction* timingFunction = 0;
     if (fromStyle->animations() && fromStyle->animations()->size() > 0) {
@@ -132,18 +157,9 @@ void KeyframeAnimation::animate(CompositeAnimation*, RenderObject*, const Render
     // through to the style blend so that we get the fromStyle.
     if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards())
         return;
-
-    // FIXME: we need to be more efficient about determining which keyframes we are animating between.
-    // We should cache the last pair or something.
     
-    // Get the from/to styles and progress between
-    const RenderStyle* fromStyle = 0;
-    const RenderStyle* toStyle = 0;
-    double progress;
-    getKeyframeAnimationInterval(fromStyle, toStyle, progress);
-
-    // If either style is 0 we have an invalid case, just stop the animation.
-    if (!fromStyle || !toStyle) {
+    // If we have no keyframes, don't animate.
+    if (!m_keyframes.size()) {
         updateStateMachine(AnimationStateInputEndAnimation, -1);
         return;
     }
@@ -153,9 +169,19 @@ void KeyframeAnimation::animate(CompositeAnimation*, RenderObject*, const Render
     if (!animatedStyle)
         animatedStyle = RenderStyle::clone(targetStyle);
 
+    // FIXME: we need to be more efficient about determining which keyframes we are animating between.
+    // We should cache the last pair or something.
     HashSet<int>::const_iterator endProperties = m_keyframes.endProperties();
     for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) {
-        bool needsAnim = blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress);
+        int property = *it;
+
+        // Get the from/to styles and progress between
+        const RenderStyle* fromStyle = 0;
+        const RenderStyle* toStyle = 0;
+        double progress;
+        fetchIntervalEndpointsForProperty(property, fromStyle, toStyle, progress);
+    
+        bool needsAnim = blendProperties(this, property, animatedStyle.get(), fromStyle, toStyle, progress);
         if (needsAnim)
             setAnimating();
         else {
@@ -176,26 +202,29 @@ void KeyframeAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle)
     if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards())
         return;
 
-    // Get the from/to styles and progress between
-    const RenderStyle* fromStyle = 0;
-    const RenderStyle* toStyle = 0;
-    double progress;
-    getKeyframeAnimationInterval(fromStyle, toStyle, progress);
-
-    // If either style is 0 we have an invalid case
-    if (!fromStyle || !toStyle)
+    if (!m_keyframes.size())
         return;
 
     if (!animatedStyle)
         animatedStyle = RenderStyle::clone(m_object->style());
 
     HashSet<int>::const_iterator endProperties = m_keyframes.endProperties();
-    for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it)
-        blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress);
+    for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) {
+        int property = *it;
+
+        // Get the from/to styles and progress between
+        const RenderStyle* fromStyle = 0;
+        const RenderStyle* toStyle = 0;
+        double progress;
+        fetchIntervalEndpointsForProperty(property, fromStyle, toStyle, progress);
+
+        blendProperties(this, property, animatedStyle.get(), fromStyle, toStyle, progress);
+    }
 }
 
 bool KeyframeAnimation::hasAnimationForProperty(int property) const
 {
+    // FIXME: why not just m_keyframes.containsProperty()?
     HashSet<int>::const_iterator end = m_keyframes.endProperties();
     for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) {
         if (*it == property)
index fab0ae8..a187f35 100644 (file)
@@ -82,8 +82,8 @@ private:
     KeyframeAnimation(const Animation* animation, RenderObject*, int index, CompositeAnimation*, RenderStyle* unanimatedStyle);
     virtual ~KeyframeAnimation();
     
-    // Get the styles surrounding the current animation time and the progress between them
-    void getKeyframeAnimationInterval(const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& progress) const;
+    // Get the styles for the given property surrounding the current animation time and the progress between them.
+    void fetchIntervalEndpointsForProperty(int property, const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& progress) const;
 
     // The keyframes that we are blending.
     KeyframeList m_keyframes;
index 43d1945..0ba907b 100644 (file)
@@ -1126,10 +1126,10 @@ bool RenderLayerBacking::startAnimation(double timeOffset, const Animation* anim
         // get timing function
         const TimingFunction* tf = keyframeStyle->hasAnimations() ? &((*keyframeStyle->animations()).animation(0)->timingFunction()) : 0;
         
-        if (hasTransform)
+        if (currentKeyframe.containsProperty(AnimatedPropertyWebkitTransform))
             transformVector.insert(new TransformAnimationValue(key, &(keyframeStyle->transform()), tf));
         
-        if (hasOpacity)
+        if (currentKeyframe.containsProperty(AnimatedPropertyOpacity))
             opacityVector.insert(new FloatAnimationValue(key, keyframeStyle->opacity(), tf));
     }
 
index 41fbbe2..bafa426 100644 (file)
@@ -43,10 +43,10 @@ bool KeyframeList::operator==(const KeyframeList& o) const
 
     Vector<KeyframeValue>::const_iterator it2 = o.m_keyframes.begin();
     for (Vector<KeyframeValue>::const_iterator it1 = m_keyframes.begin(); it1 != m_keyframes.end(); ++it1) {
-        if (it1->m_key != it2->m_key)
+        if (it1->key() != it2->key())
             return false;
-        const RenderStyle& style1 = *it1->m_style;
-        const RenderStyle& style2 = *it2->m_style;
+        const RenderStyle& style1 = *it1->style();
+        const RenderStyle& style2 = *it2->style();
         if (style1 != style2)
             return false;
         ++it2;
@@ -55,34 +55,43 @@ bool KeyframeList::operator==(const KeyframeList& o) const
     return true;
 }
 
-void KeyframeList::insert(float key, PassRefPtr<RenderStyle> style)
+void KeyframeList::insert(const KeyframeValue& keyframe)
 {
-    if (key < 0 || key > 1)
+    if (keyframe.key() < 0 || keyframe.key() > 1)
         return;
 
-    int index = -1;
-    
+    bool inserted = false;
+    bool replaced = false;
     for (size_t i = 0; i < m_keyframes.size(); ++i) {
-        if (m_keyframes[i].m_key == key) {
-            index = (int) i;
+        if (m_keyframes[i].key() == keyframe.key()) {
+            m_keyframes[i] = keyframe;
+            replaced = true;
             break;
         }
-        if (m_keyframes[i].m_key > key) {
+
+        if (m_keyframes[i].key() > keyframe.key()) {
             // insert before
-            m_keyframes.insert(i, KeyframeValue());
-            index = (int) i;
+            m_keyframes.insert(i, keyframe);
+            inserted = true;
             break;
         }
     }
     
-    if (index < 0) {
-        // append
-        index = (int) m_keyframes.size();
-        m_keyframes.append(KeyframeValue());
+    if (!replaced && !inserted)
+        m_keyframes.append(keyframe);
+
+    if (replaced) {
+        // We have to rebuild the properties list from scratch.
+        m_properties.clear();
+        for (Vector<KeyframeValue>::const_iterator it = m_keyframes.begin(); it != m_keyframes.end(); ++it) {
+            const KeyframeValue& currKeyframe = *it;
+            for (HashSet<int>::const_iterator it = currKeyframe.properties().begin(); it != currKeyframe.properties().end(); ++it)
+                m_properties.add(*it);
+        }
+    } else {
+        for (HashSet<int>::const_iterator it = keyframe.properties().begin(); it != keyframe.properties().end(); ++it)
+            m_properties.add(*it);
     }
-    
-    m_keyframes[index].m_key = key;
-    m_keyframes[index].m_style = style;
 }
 
 } // namespace WebCore
index 44a2bdf..64170ce 100644 (file)
@@ -37,15 +37,25 @@ class RenderStyle;
 
 class KeyframeValue {
 public:
-    KeyframeValue()
-        : m_key(-1)
+    KeyframeValue(float key, PassRefPtr<RenderStyle> style)
+        : m_key(key)
+        , m_style(style)
     {
     }
 
+    void addProperty(int prop) { m_properties.add(prop); }
+    bool containsProperty(int prop) const { return m_properties.contains(prop); }
+    const HashSet<int>& properties() const { return m_properties; }
+
     float key() const { return m_key; }
+    void setKey(float key) { m_key = key; }
+
     const RenderStyle* style() const { return m_style.get(); }
+    void setStyle(PassRefPtr<RenderStyle> style) { m_style = style; }
 
+private:
     float m_key;
+    HashSet<int> m_properties; // The properties specified in this keyframe.
     RefPtr<RenderStyle> m_style;
 };
 
@@ -55,8 +65,8 @@ public:
         : m_animationName(animationName)
         , m_renderer(renderer)
     {
-        insert(0, 0);
-        insert(1, 0);
+        insert(KeyframeValue(0, 0));
+        insert(KeyframeValue(1, 0));
     }
     ~KeyframeList();
         
@@ -65,7 +75,7 @@ public:
     
     const AtomicString& animationName() const { return m_animationName; }
     
-    void insert(float key, PassRefPtr<RenderStyle> style);
+    void insert(const KeyframeValue& keyframe);
     
     void addProperty(int prop) { m_properties.add(prop); }
     bool containsProperty(int prop) const { return m_properties.contains(prop); }
@@ -79,8 +89,8 @@ public:
 
 private:
     AtomicString m_animationName;
-    Vector<KeyframeValue> m_keyframes;
-    HashSet<int> m_properties;       // the properties being animated
+    Vector<KeyframeValue> m_keyframes; // kept sorted by key
+    HashSet<int> m_properties; // the properties being animated
     RenderObject* m_renderer;
 };