[BlackBerry] Upstream LayerAnimation.{cpp, h}
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Mar 2012 02:56:42 +0000 (02:56 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Mar 2012 02:56:42 +0000 (02:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=80123

Patch by Robin Cao <robin.cao@torchmobile.com.cn> on 2012-03-27
Reviewed by Rob Buis.

Initial upstream, no new tests.

* platform/graphics/blackberry/LayerAnimation.cpp: Added.
(WebCore):
(WebCore::solveEpsilon):
(WebCore::solveCubicBezierFunction):
(WebCore::solveStepsFunction):
(WebCore::timingFunctionForAnimationValue):
(WebCore::progress):
(WebCore::fetchIntervalEndpoints):
(WebCore::LayerAnimation::apply):
(WebCore::LayerAnimation::blendTransform):
(WebCore::LayerAnimation::blendOpacity):
(WebCore::LayerAnimation::validateTransformLists):
* platform/graphics/blackberry/LayerAnimation.h: Added.
(WebCore):
(LayerAnimation):
(WebCore::LayerAnimation::create):
(WebCore::LayerAnimation::clone):
(WebCore::LayerAnimation::~LayerAnimation):
(WebCore::LayerAnimation::name):
(WebCore::LayerAnimation::setStartTime):
(WebCore::LayerAnimation::idFromAnimation):
(WebCore::LayerAnimation::isEqualToAnimation):
(WebCore::LayerAnimation::id):
(WebCore::LayerAnimation::property):
(WebCore::LayerAnimation::boxSize):
(WebCore::LayerAnimation::timeOffset):
(WebCore::LayerAnimation::startTime):
(WebCore::LayerAnimation::valueCount):
(WebCore::LayerAnimation::timingFunction):
(WebCore::LayerAnimation::duration):
(WebCore::LayerAnimation::iterationCount):
(WebCore::LayerAnimation::direction):
(WebCore::LayerAnimation::valueAt):
(WebCore::LayerAnimation::LayerAnimation):

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

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/blackberry/LayerAnimation.cpp [new file with mode: 0644]
Source/WebCore/platform/graphics/blackberry/LayerAnimation.h [new file with mode: 0644]

index eede911..9fa69c4 100644 (file)
@@ -1,3 +1,47 @@
+2012-03-27  Robin Cao  <robin.cao@torchmobile.com.cn>
+
+        [BlackBerry] Upstream LayerAnimation.{cpp, h}
+        https://bugs.webkit.org/show_bug.cgi?id=80123
+
+        Reviewed by Rob Buis.
+
+        Initial upstream, no new tests.
+
+        * platform/graphics/blackberry/LayerAnimation.cpp: Added.
+        (WebCore):
+        (WebCore::solveEpsilon):
+        (WebCore::solveCubicBezierFunction):
+        (WebCore::solveStepsFunction):
+        (WebCore::timingFunctionForAnimationValue):
+        (WebCore::progress):
+        (WebCore::fetchIntervalEndpoints):
+        (WebCore::LayerAnimation::apply):
+        (WebCore::LayerAnimation::blendTransform):
+        (WebCore::LayerAnimation::blendOpacity):
+        (WebCore::LayerAnimation::validateTransformLists):
+        * platform/graphics/blackberry/LayerAnimation.h: Added.
+        (WebCore):
+        (LayerAnimation):
+        (WebCore::LayerAnimation::create):
+        (WebCore::LayerAnimation::clone):
+        (WebCore::LayerAnimation::~LayerAnimation):
+        (WebCore::LayerAnimation::name):
+        (WebCore::LayerAnimation::setStartTime):
+        (WebCore::LayerAnimation::idFromAnimation):
+        (WebCore::LayerAnimation::isEqualToAnimation):
+        (WebCore::LayerAnimation::id):
+        (WebCore::LayerAnimation::property):
+        (WebCore::LayerAnimation::boxSize):
+        (WebCore::LayerAnimation::timeOffset):
+        (WebCore::LayerAnimation::startTime):
+        (WebCore::LayerAnimation::valueCount):
+        (WebCore::LayerAnimation::timingFunction):
+        (WebCore::LayerAnimation::duration):
+        (WebCore::LayerAnimation::iterationCount):
+        (WebCore::LayerAnimation::direction):
+        (WebCore::LayerAnimation::valueAt):
+        (WebCore::LayerAnimation::LayerAnimation):
+
 2012-03-27  Kenichi Ishibashi  <bashi@chromium.org>
 
         [Chromium] Uninitialized access in SimpleFontDataSkia::platformInit
 2012-03-27  Kenichi Ishibashi  <bashi@chromium.org>
 
         [Chromium] Uninitialized access in SimpleFontDataSkia::platformInit
diff --git a/Source/WebCore/platform/graphics/blackberry/LayerAnimation.cpp b/Source/WebCore/platform/graphics/blackberry/LayerAnimation.cpp
new file mode 100644 (file)
index 0000000..f46afba
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2011, 2012 Research In Motion Limited. All rights reserved.
+ * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LayerAnimation.h"
+
+#include "IdentityTransformOperation.h"
+#include "LayerCompositingThread.h"
+#include "TransformationMatrix.h"
+#include "UnitBezier.h"
+
+#include <algorithm>
+
+namespace WebCore {
+
+using namespace std;
+
+// FIXME: Some functions below are copied from AnimationBase and KeyframeAnimation.
+// We need to refactor these code to increase code reuse.
+// https://bugs.webkit.org/show_bug.cgi?id=82293
+
+// The epsilon value we pass to UnitBezier::solve given that the animation is going to run over |dur| seconds. The longer the
+// animation, the more precision we need in the timing function result to avoid ugly discontinuities.
+static inline double solveEpsilon(double duration)
+{
+    return 1.0 / (200.0 * duration);
+}
+
+static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration)
+{
+    // Convert from input time to parametric value in curve, then from
+    // that to output time.
+    UnitBezier bezier(p1x, p1y, p2x, p2y);
+    return bezier.solve(t, solveEpsilon(duration));
+}
+
+static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t)
+{
+    if (stepAtStart)
+        return min(1.0, (floor(numSteps * t) + 1) / numSteps);
+    return floor(numSteps * t) / numSteps;
+}
+
+static const TimingFunction* timingFunctionForAnimationValue(const AnimationValue* animValue, const LayerAnimation* anim)
+{
+    if (animValue->timingFunction())
+        return animValue->timingFunction();
+    if (anim->timingFunction())
+        return anim->timingFunction();
+
+    return CubicBezierTimingFunction::defaultTimingFunction();
+}
+
+static double progress(double elapsedTime, const LayerAnimation* layerAnimation, double scale, double offset, const TimingFunction* tf)
+{
+    double dur = layerAnimation->duration();
+    if (layerAnimation->iterationCount() > 0)
+        dur *= layerAnimation->iterationCount();
+
+    if (!layerAnimation->duration())
+        return 1.0;
+    if (layerAnimation->iterationCount() > 0 && elapsedTime >= dur)
+        return (layerAnimation->iterationCount() % 2) ? 1.0 : 0.0;
+
+    // Compute the fractional time, taking into account direction.
+    // There is no need to worry about iterations, we assume that we would have
+    // short circuited above if we were done.
+    double fractionalTime = elapsedTime / layerAnimation->duration();
+    int integralTime = static_cast<int>(fractionalTime);
+    fractionalTime -= integralTime;
+
+    if ((layerAnimation->direction() == Animation::AnimationDirectionAlternate) && (integralTime & 1))
+        fractionalTime = 1 - fractionalTime;
+
+    if (scale != 1 || offset)
+        fractionalTime = (fractionalTime - offset) * scale;
+
+    if (!tf)
+        tf = layerAnimation->timingFunction();
+
+    if (tf->isCubicBezierTimingFunction()) {
+        const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(tf);
+        return solveCubicBezierFunction(ctf->x1(), ctf->y1(), ctf->x2(), ctf->y2(), fractionalTime, layerAnimation->duration());
+    }
+
+    if (tf->isStepsTimingFunction()) {
+        const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(tf);
+        return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), fractionalTime);
+    }
+
+    return fractionalTime;
+}
+
+static void fetchIntervalEndpoints(double elapsedTime, const LayerAnimation* layerAnimation, const AnimationValue*& fromValue, const AnimationValue*& toValue, double& prog)
+{
+    // Find the first key.
+    if (layerAnimation->duration() && layerAnimation->iterationCount() != Animation::IterationCountInfinite)
+        elapsedTime = min(elapsedTime, layerAnimation->duration() * layerAnimation->iterationCount());
+
+    double fractionalTime = layerAnimation->duration() ? (elapsedTime / layerAnimation->duration()) : 1;
+
+    // FIXME: startTime can be before the current animation "frame" time. This is to sync with the frame time
+    // concept in AnimationTimeController. So we need to somehow sync the two. Until then, the possible
+    // error is small and will probably not be noticeable. Until we fix this, remove the assert.
+    // https://bugs.webkit.org/show_bug.cgi?id=52037
+    // ASSERT(fractionalTime >= 0);
+    if (fractionalTime < 0)
+        fractionalTime = 0;
+
+    // FIXME: share this code with AnimationBase::progress().
+    int iteration = static_cast<int>(fractionalTime);
+    if (layerAnimation->iterationCount() != Animation::IterationCountInfinite)
+        iteration = min(iteration, layerAnimation->iterationCount() - 1);
+
+    fractionalTime -= iteration;
+
+    bool reversing = (layerAnimation->direction() == Animation::AnimationDirectionAlternate) && (iteration & 1);
+    if (reversing)
+        fractionalTime = 1 - fractionalTime;
+
+    size_t numKeyframes = layerAnimation->valueCount();
+    if (!numKeyframes)
+        return;
+
+    ASSERT(!layerAnimation->valueAt(0)->keyTime());
+    ASSERT(layerAnimation->valueAt(layerAnimation->valueCount() - 1)->keyTime() == 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 AnimationValue* currKeyframe = layerAnimation->valueAt(i);
+
+        if (fractionalTime < currKeyframe->keyTime()) {
+            nextIndex = i;
+            break;
+        }
+
+        prevIndex = i;
+    }
+
+    double scale = 1;
+    double offset = 0;
+
+    if (prevIndex == -1)
+        prevIndex = 0;
+
+    if (nextIndex == -1)
+        nextIndex = layerAnimation->valueCount() - 1;
+
+    const AnimationValue* prevKeyframe = layerAnimation->valueAt(prevIndex);
+    const AnimationValue* nextKeyframe = layerAnimation->valueAt(nextIndex);
+
+    fromValue = prevKeyframe;
+    toValue = nextKeyframe;
+
+    offset = prevKeyframe->keyTime();
+    scale = 1.0 / (nextKeyframe->keyTime() - prevKeyframe->keyTime());
+
+    const TimingFunction* timingFunction = timingFunctionForAnimationValue(prevKeyframe, layerAnimation);
+
+    prog = progress(elapsedTime, layerAnimation, scale, offset, timingFunction);
+}
+
+void LayerAnimation::apply(LayerCompositingThread* layer, double elapsedTime)
+{
+    const AnimationValue* from = 0;
+    const AnimationValue* to = 0;
+    double progress = 0.0;
+
+    fetchIntervalEndpoints(elapsedTime, this, from, to, progress);
+
+    switch (property()) {
+    case AnimatedPropertyWebkitTransform:
+        layer->setTransform(blendTransform(static_cast<const TransformAnimationValue*>(from)->value(), static_cast<const TransformAnimationValue*>(to)->value(), progress));
+        break;
+    case AnimatedPropertyOpacity:
+        layer->setOpacity(blendOpacity(static_cast<const FloatAnimationValue*>(from)->value(), static_cast<const FloatAnimationValue*>(to)->value(), progress));
+        break;
+    case AnimatedPropertyBackgroundColor:
+    case AnimatedPropertyWebkitFilter:
+    case AnimatedPropertyInvalid:
+        ASSERT_NOT_REACHED();
+        break;
+    }
+}
+
+TransformationMatrix LayerAnimation::blendTransform(const TransformOperations* from, const TransformOperations* to, double progress) const
+{
+    TransformationMatrix t;
+
+    if (m_transformFunctionListValid) {
+        // A trick to avoid touching the refcount of shared TransformOperations on the wrong thread.
+        // Since TransforOperation is not ThreadSafeRefCounted, we are only allowed to touch the ref
+        // count of shared operations when the WebKit thread and compositing thread are in sync.
+        Vector<TransformOperation*> result;
+        Vector<RefPtr<TransformOperation> > owned;
+
+        unsigned fromSize = from->operations().size();
+        unsigned toSize = to->operations().size();
+        unsigned size = max(fromSize, toSize);
+        for (unsigned i = 0; i < size; i++) {
+            TransformOperation* fromOp = (i < fromSize) ? from->operations()[i].get() : 0;
+            TransformOperation* toOp = (i < toSize) ? to->operations()[i].get() : 0;
+            RefPtr<TransformOperation> blendedOp = toOp ? toOp->blend(fromOp, progress) : (fromOp ? fromOp->blend(0, progress, true) : PassRefPtr<TransformOperation>(0));
+            if (blendedOp) {
+                result.append(blendedOp.get());
+                owned.append(blendedOp);
+            } else {
+                RefPtr<TransformOperation> identityOp = IdentityTransformOperation::create();
+                owned.append(identityOp);
+                if (progress > 0.5)
+                    result.append(toOp ? toOp : identityOp.get());
+                else
+                    result.append(fromOp ? fromOp : identityOp.get());
+            }
+        }
+
+        IntSize sz = boxSize();
+        for (unsigned i = 0; i < result.size(); ++i)
+            result[i]->apply(t, sz);
+    } else {
+        // Convert the TransformOperations into matrices.
+        TransformationMatrix fromT;
+        from->apply(boxSize(), fromT);
+        to->apply(boxSize(), t);
+
+        t.blend(fromT, progress);
+    }
+
+    return t;
+}
+
+float LayerAnimation::blendOpacity(float from, float to, double progress) const
+{
+    float opacity = from + (to - from) * progress;
+
+    return max(0.0f, min(opacity, 1.0f));
+}
+
+void LayerAnimation::validateTransformLists()
+{
+    m_transformFunctionListValid = false;
+
+    if (m_values.size() < 2 || property() != AnimatedPropertyWebkitTransform)
+        return;
+
+    // Empty transforms match anything, so find the first non-empty entry as the reference.
+    size_t numKeyframes = m_values.size();
+    size_t firstNonEmptyTransformKeyframeIndex = numKeyframes;
+
+    for (size_t i = 0; i < numKeyframes; ++i) {
+        const TransformAnimationValue* currentKeyframe = static_cast<const TransformAnimationValue*>(m_values.at(i));
+        if (currentKeyframe->value()->size()) {
+            firstNonEmptyTransformKeyframeIndex = i;
+            break;
+        }
+    }
+
+    if (firstNonEmptyTransformKeyframeIndex == numKeyframes)
+        return;
+
+    const TransformOperations* firstVal = static_cast<const TransformAnimationValue*>(m_values.at(firstNonEmptyTransformKeyframeIndex))->value();
+
+    // See if the keyframes are valid.
+    for (size_t i = firstNonEmptyTransformKeyframeIndex + 1; i < numKeyframes; ++i) {
+        const TransformAnimationValue* currentKeyframe = static_cast<const TransformAnimationValue*>(m_values.at(i));
+        const TransformOperations* val = currentKeyframe->value();
+
+        // A null transform matches anything.
+        if (val->operations().isEmpty())
+            continue;
+
+        // If the sizes of the function lists don't match, the lists don't match.
+        if (firstVal->operations().size() != val->operations().size())
+            return;
+
+        // If the types of each function are not the same, the lists don't match.
+        for (size_t j = 0; j < firstVal->operations().size(); ++j) {
+            if (!firstVal->operations()[j]->isSameType(*val->operations()[j]))
+                return;
+        }
+    }
+
+    // Keyframes are valid.
+    m_transformFunctionListValid = true;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/blackberry/LayerAnimation.h b/Source/WebCore/platform/graphics/blackberry/LayerAnimation.h
new file mode 100644 (file)
index 0000000..1044770
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef LayerAnimation_h
+#define LayerAnimation_h
+
+#include "GraphicsLayer.h"
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+#define DEBUG_LAYER_ANIMATION 0
+
+namespace WebCore {
+
+class Animation;
+class LayerCompositingThread;
+class TransformationMatrix;
+
+// This class uses non-threadsafe refcounting in the WebCore::Animation and
+// WebCore::String members, so using threadsafe refcounting here would be a big
+// cover-up. Instead, you must be careful to use ref/deref this class only on
+// the WebKit thread, or when the WebKit and compositing threads are in sync.
+class LayerAnimation : public RefCounted<LayerAnimation> {
+public:
+    // WebKit thread only
+    // Because the refcounting done in constructor and destructor is not thread safe,
+    // the LayerAnimation must be created or destroyed on the WebKit thread, or when
+    // the WebKit and compositing threads are in sync.
+    // Also, the name property is using a String which has non-threadsafe refcounting.
+    // The setStartTime method is not threadsafe and must only be called on a newly
+    // created LayerAnimation before sending it off to the compositing thread.
+    static PassRefPtr<LayerAnimation> create(const KeyframeValueList& values, const IntSize& boxSize, const Animation* animation, const String& name, double timeOffset)
+    {
+        return adoptRef(new LayerAnimation(values, boxSize, animation, name, timeOffset));
+    }
+
+    PassRefPtr<LayerAnimation> clone(double timeOffset)
+    {
+        LayerAnimation* animation = new LayerAnimation(*this);
+        // The cloned animation should get a different timeOffset if it's paused.
+        animation->m_timeOffset = timeOffset;
+
+        return adoptRef(animation);
+    }
+
+    ~LayerAnimation()
+    {
+    }
+
+    const String& name() const { return m_name; }
+    void setStartTime(double time) { m_startTime = time; }
+
+    // These functions are thread safe (immutable state).
+    static int idFromAnimation(const Animation* animation) { return reinterpret_cast<int>(animation); }
+    bool isEqualToAnimation(const Animation* animation) const { return idFromAnimation(animation) == id(); }
+    int id() const { return m_id; }
+    AnimatedPropertyID property() const { return m_values.property(); }
+    IntSize boxSize() const { return m_boxSize; }
+    double timeOffset() const { return m_timeOffset; }
+    double startTime() const { return m_startTime; }
+    size_t valueCount() const { return m_values.size(); }
+
+    // NOTE: Don't try to ref a TimingFunction, that's not a threadsafe operation.
+    const TimingFunction* timingFunction() const { return m_timingFunction.get(); }
+    double duration() const { return m_duration; }
+    int iterationCount() const { return m_iterationCount; }
+    Animation::AnimationDirection direction() const { return m_direction; }
+
+    // NOTE: Don't try to clone() an AnimationValue, that's not a threadsafe operation since it mutates refcounts.
+    const AnimationValue* valueAt(size_t i) const { return m_values.at(i); }
+
+    TransformationMatrix blendTransform(const TransformOperations* from, const TransformOperations*, double progress) const;
+    float blendOpacity(float from, float to, double progress) const;
+    void apply(LayerCompositingThread*, double elapsedTime);
+
+private:
+    LayerAnimation(const KeyframeValueList& values, const IntSize& boxSize, const Animation* animation, const String& name, double timeOffset)
+        : m_id(reinterpret_cast<int>(animation))
+        , m_values(values)
+        , m_boxSize(boxSize)
+        , m_name(name)
+        , m_timeOffset(timeOffset)
+        , m_startTime(0)
+        , m_timingFunction(0)
+        , m_duration(animation->duration())
+        , m_iterationCount(animation->iterationCount())
+        , m_direction(animation->direction())
+    {
+        if (animation->isTimingFunctionSet())
+            m_timingFunction = animation->timingFunction();
+
+        validateTransformLists();
+    }
+
+    LayerAnimation(const LayerAnimation& other)
+        :  RefCounted<LayerAnimation>()
+        , m_id(other.m_id)
+        , m_values(other.m_values)
+        , m_boxSize(other.m_boxSize)
+        , m_name(other.m_name)
+        , m_timeOffset(other.m_timeOffset)
+        , m_startTime(other.m_startTime)
+        , m_transformFunctionListValid(other.m_transformFunctionListValid)
+        , m_timingFunction(other.m_timingFunction)
+        , m_duration(other.m_duration)
+        , m_iterationCount(other.m_iterationCount)
+        , m_direction(other.m_direction)
+    {
+    }
+
+    void validateTransformLists();
+
+    int m_id;
+
+    // NOTE: Don't expose the KeyframeValueList directly, since its copy
+    // constructor mutates refcounts and thus is not thread safe.
+    KeyframeValueList m_values;
+    IntSize m_boxSize;
+    String m_name;
+    double m_timeOffset;
+    double m_startTime;
+    bool m_transformFunctionListValid;
+
+    RefPtr<TimingFunction> m_timingFunction;
+    double m_duration;
+    int m_iterationCount;
+    Animation::AnimationDirection m_direction;
+};
+
+}
+
+#endif // LayerAnimation_h