Changeset 109297 in webkit


Ignore:
Timestamp:
Feb 29, 2012 7:34:31 PM (12 years ago)
Author:
commit-queue@webkit.org
Message:

[chromium] Add impl-thread support for animation-timing-function
https://bugs.webkit.org/show_bug.cgi?id=79819

Patch by Ian Vollick <vollick@chromium.org> on 2012-02-29
Reviewed by James Robinson.

Source/WebCore:

  • WebCore.gypi:
  • platform/graphics/chromium/cc/CCKeyframedAnimationCurve.cpp:

(WebCore::CCKeyframe::CCKeyframe):
(WebCore):
(WebCore::CCKeyframe::~CCKeyframe):
(WebCore::CCKeyframe::time):
(WebCore::CCKeyframe::timingFunction):
(WebCore::CCFloatKeyframe::create):
(WebCore::CCFloatKeyframe::CCFloatKeyframe):
(WebCore::CCFloatKeyframe::~CCFloatKeyframe):
(WebCore::CCFloatKeyframe::value):
(WebCore::CCFloatKeyframe::clone):
(WebCore::CCTransformKeyframe::create):
(WebCore::CCTransformKeyframe::CCTransformKeyframe):
(WebCore::CCTransformKeyframe::~CCTransformKeyframe):
(WebCore::CCTransformKeyframe::value):
(WebCore::CCTransformKeyframe::clone):
(WebCore::CCKeyframedFloatAnimationCurve::create):
(WebCore::CCKeyframedFloatAnimationCurve::CCKeyframedFloatAnimationCurve):
(WebCore::CCKeyframedFloatAnimationCurve::addKeyframe):
(WebCore::CCKeyframedFloatAnimationCurve::duration):
(WebCore::CCKeyframedFloatAnimationCurve::clone):
(WebCore::CCKeyframedFloatAnimationCurve::getValue):
(WebCore::CCKeyframedTransformAnimationCurve::create):
(WebCore::CCKeyframedTransformAnimationCurve::CCKeyframedTransformAnimationCurve):
(WebCore::CCKeyframedTransformAnimationCurve::~CCKeyframedTransformAnimationCurve):
(WebCore::CCKeyframedTransformAnimationCurve::addKeyframe):
(WebCore::CCKeyframedTransformAnimationCurve::duration):
(WebCore::CCKeyframedTransformAnimationCurve::clone):
(WebCore::CCKeyframedTransformAnimationCurve::getValue):

  • platform/graphics/chromium/cc/CCKeyframedAnimationCurve.h:

(CCKeyframe):
(CCFloatKeyframe):
(WebCore):
(CCTransformKeyframe):
(CCKeyframedFloatAnimationCurve):
(CCKeyframedTransformAnimationCurve):

  • platform/graphics/chromium/cc/CCLayerAnimationController.cpp:
  • platform/graphics/chromium/cc/CCTimingFunction.cpp: Added.

(WebCore):
(WebCore::CCTimingFunction::CCTimingFunction):
(WebCore::CCTimingFunction::~CCTimingFunction):
(WebCore::CCTimingFunction::duration):
(WebCore::CCCubicBezierTimingFunction::create):
(WebCore::CCCubicBezierTimingFunction::CCCubicBezierTimingFunction):
(WebCore::CCCubicBezierTimingFunction::~CCCubicBezierTimingFunction):
(WebCore::CCCubicBezierTimingFunction::getValue):
(WebCore::CCCubicBezierTimingFunction::clone):
(WebCore::CCEaseTimingFunction::create):
(WebCore::CCEaseInTimingFunction::create):
(WebCore::CCEaseOutTimingFunction::create):
(WebCore::CCEaseInOutTimingFunction::create):

  • platform/graphics/chromium/cc/CCTimingFunction.h: Added.

(WebCore):
(CCTimingFunction):
(CCCubicBezierTimingFunction):
(CCEaseTimingFunction):
(CCEaseInTimingFunction):
(CCEaseOutTimingFunction):
(CCEaseInOutTimingFunction):

Source/WebKit/chromium:

  • tests/CCKeyframedAnimationCurveTest.cpp:

(WebCore::TEST):
(WebCore):

Location:
trunk/Source
Files:
2 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r109290 r109297  
     12012-02-29  Ian Vollick  <vollick@chromium.org>
     2
     3        [chromium] Add impl-thread support for animation-timing-function
     4        https://bugs.webkit.org/show_bug.cgi?id=79819
     5
     6        Reviewed by James Robinson.
     7
     8        * WebCore.gypi:
     9        * platform/graphics/chromium/cc/CCKeyframedAnimationCurve.cpp:
     10        (WebCore::CCKeyframe::CCKeyframe):
     11        (WebCore):
     12        (WebCore::CCKeyframe::~CCKeyframe):
     13        (WebCore::CCKeyframe::time):
     14        (WebCore::CCKeyframe::timingFunction):
     15        (WebCore::CCFloatKeyframe::create):
     16        (WebCore::CCFloatKeyframe::CCFloatKeyframe):
     17        (WebCore::CCFloatKeyframe::~CCFloatKeyframe):
     18        (WebCore::CCFloatKeyframe::value):
     19        (WebCore::CCFloatKeyframe::clone):
     20        (WebCore::CCTransformKeyframe::create):
     21        (WebCore::CCTransformKeyframe::CCTransformKeyframe):
     22        (WebCore::CCTransformKeyframe::~CCTransformKeyframe):
     23        (WebCore::CCTransformKeyframe::value):
     24        (WebCore::CCTransformKeyframe::clone):
     25        (WebCore::CCKeyframedFloatAnimationCurve::create):
     26        (WebCore::CCKeyframedFloatAnimationCurve::CCKeyframedFloatAnimationCurve):
     27        (WebCore::CCKeyframedFloatAnimationCurve::addKeyframe):
     28        (WebCore::CCKeyframedFloatAnimationCurve::duration):
     29        (WebCore::CCKeyframedFloatAnimationCurve::clone):
     30        (WebCore::CCKeyframedFloatAnimationCurve::getValue):
     31        (WebCore::CCKeyframedTransformAnimationCurve::create):
     32        (WebCore::CCKeyframedTransformAnimationCurve::CCKeyframedTransformAnimationCurve):
     33        (WebCore::CCKeyframedTransformAnimationCurve::~CCKeyframedTransformAnimationCurve):
     34        (WebCore::CCKeyframedTransformAnimationCurve::addKeyframe):
     35        (WebCore::CCKeyframedTransformAnimationCurve::duration):
     36        (WebCore::CCKeyframedTransformAnimationCurve::clone):
     37        (WebCore::CCKeyframedTransformAnimationCurve::getValue):
     38        * platform/graphics/chromium/cc/CCKeyframedAnimationCurve.h:
     39        (CCKeyframe):
     40        (CCFloatKeyframe):
     41        (WebCore):
     42        (CCTransformKeyframe):
     43        (CCKeyframedFloatAnimationCurve):
     44        (CCKeyframedTransformAnimationCurve):
     45        * platform/graphics/chromium/cc/CCLayerAnimationController.cpp:
     46        * platform/graphics/chromium/cc/CCTimingFunction.cpp: Added.
     47        (WebCore):
     48        (WebCore::CCTimingFunction::CCTimingFunction):
     49        (WebCore::CCTimingFunction::~CCTimingFunction):
     50        (WebCore::CCTimingFunction::duration):
     51        (WebCore::CCCubicBezierTimingFunction::create):
     52        (WebCore::CCCubicBezierTimingFunction::CCCubicBezierTimingFunction):
     53        (WebCore::CCCubicBezierTimingFunction::~CCCubicBezierTimingFunction):
     54        (WebCore::CCCubicBezierTimingFunction::getValue):
     55        (WebCore::CCCubicBezierTimingFunction::clone):
     56        (WebCore::CCEaseTimingFunction::create):
     57        (WebCore::CCEaseInTimingFunction::create):
     58        (WebCore::CCEaseOutTimingFunction::create):
     59        (WebCore::CCEaseInOutTimingFunction::create):
     60        * platform/graphics/chromium/cc/CCTimingFunction.h: Added.
     61        (WebCore):
     62        (CCTimingFunction):
     63        (CCCubicBezierTimingFunction):
     64        (CCEaseTimingFunction):
     65        (CCEaseInTimingFunction):
     66        (CCEaseOutTimingFunction):
     67        (CCEaseInOutTimingFunction):
     68
    1692012-02-29  Shinya Kawanaka  <shinyak@chromium.org>
    270
  • trunk/Source/WebCore/WebCore.gypi

    r109192 r109297  
    33633363            'platform/graphics/chromium/cc/CCTileDrawQuad.cpp',
    33643364            'platform/graphics/chromium/cc/CCTileDrawQuad.h',
     3365            'platform/graphics/chromium/cc/CCTimingFunction.cpp',
     3366            'platform/graphics/chromium/cc/CCTimingFunction.h',
    33653367            'platform/graphics/chromium/cc/CCThread.h',
    33663368            'platform/graphics/chromium/cc/CCThreadProxy.cpp',
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCKeyframedAnimationCurve.cpp

    r108727 r109297  
    4343
    4444template <class Keyframe>
    45 bool keyframesAreSorted(const Vector<Keyframe>& keyframes)
    46 {
    47     if (!keyframes.size())
    48         return true;
    49 
    50     for (size_t i = 0; i < keyframes.size() - 1; ++i) {
    51         if (keyframes[i].time > keyframes[i+1].time)
    52             return false;
    53     }
    54 
    55     return true;
     45void insertKeyframe(PassOwnPtr<Keyframe> popKeyframe, Vector<OwnPtr<Keyframe> >& keyframes)
     46{
     47    OwnPtr<Keyframe> keyframe = popKeyframe;
     48
     49    // Usually, the keyframes will be added in order, so this loop would be unnecessary and
     50    // we should skip it if possible.
     51    if (!keyframes.isEmpty() && keyframe->time() < keyframes.last()->time()) {
     52        for (size_t i = 0; i < keyframes.size(); ++i) {
     53            if (keyframe->time() < keyframes[i]->time()) {
     54                keyframes.insert(i, keyframe.release());
     55                return;
     56            }
     57        }
     58    }
     59
     60    keyframes.append(keyframe.release());
     61}
     62
     63PassOwnPtr<CCTimingFunction> cloneTimingFunction(const CCTimingFunction* timingFunction)
     64{
     65    ASSERT(timingFunction);
     66    OwnPtr<CCAnimationCurve> curve(timingFunction->clone());
     67    return adoptPtr(static_cast<CCTimingFunction*>(curve.leakPtr()));
    5668}
    5769
    5870} // namespace
    5971
    60 PassOwnPtr<CCKeyframedFloatAnimationCurve> CCKeyframedFloatAnimationCurve::create(const Vector<CCFloatKeyframe>& keyframes)
    61 {
    62     if (!keyframes.size() || !keyframesAreSorted(keyframes))
    63         return nullptr;
    64 
    65     return adoptPtr(new CCKeyframedFloatAnimationCurve(keyframes));
    66 }
    67 
    68 CCKeyframedFloatAnimationCurve::CCKeyframedFloatAnimationCurve(const Vector<CCFloatKeyframe>& keyframes)
    69     : m_keyframes(keyframes)
     72CCKeyframe::CCKeyframe(double time, PassOwnPtr<CCTimingFunction> timingFunction)
     73    : m_time(time)
     74    , m_timingFunction(timingFunction)
     75{
     76}
     77
     78CCKeyframe::~CCKeyframe()
     79{
     80}
     81
     82double CCKeyframe::time() const
     83{
     84    return m_time;
     85}
     86
     87const CCTimingFunction* CCKeyframe::timingFunction() const
     88{
     89    return m_timingFunction.get();
     90}
     91
     92PassOwnPtr<CCFloatKeyframe> CCFloatKeyframe::create(double time, float value, PassOwnPtr<CCTimingFunction> timingFunction)
     93{
     94    return adoptPtr(new CCFloatKeyframe(time, value, timingFunction));
     95}
     96
     97CCFloatKeyframe::CCFloatKeyframe(double time, float value, PassOwnPtr<CCTimingFunction> timingFunction)
     98    : CCKeyframe(time, timingFunction)
     99    , m_value(value)
     100{
     101}
     102
     103CCFloatKeyframe::~CCFloatKeyframe()
     104{
     105}
     106
     107float CCFloatKeyframe::value() const
     108{
     109    return m_value;
     110}
     111
     112PassOwnPtr<CCFloatKeyframe> CCFloatKeyframe::clone() const
     113{
     114    return CCFloatKeyframe::create(time(), value(), timingFunction() ? cloneTimingFunction(timingFunction()) : nullptr);
     115}
     116
     117PassOwnPtr<CCTransformKeyframe> CCTransformKeyframe::create(double time, const TransformOperations& value, PassOwnPtr<CCTimingFunction> timingFunction)
     118{
     119    return adoptPtr(new CCTransformKeyframe(time, value, timingFunction));
     120}
     121
     122CCTransformKeyframe::CCTransformKeyframe(double time, const TransformOperations& value, PassOwnPtr<CCTimingFunction> timingFunction)
     123    : CCKeyframe(time, timingFunction)
     124    , m_value(value)
     125{
     126}
     127
     128CCTransformKeyframe::~CCTransformKeyframe()
     129{
     130}
     131
     132const TransformOperations& CCTransformKeyframe::value() const
     133{
     134    return m_value;
     135}
     136
     137PassOwnPtr<CCTransformKeyframe> CCTransformKeyframe::clone() const
     138{
     139    // We need to do a deep copy the m_value may contain ref pointers to TransformOperation objects.
     140    TransformOperations operations;
     141    for (size_t j = 0; j < m_value.size(); ++j) {
     142        TransformOperation::OperationType operationType = m_value.operations()[j]->getOperationType();
     143        switch (operationType) {
     144        case TransformOperation::SCALE_X:
     145        case TransformOperation::SCALE_Y:
     146        case TransformOperation::SCALE_Z:
     147        case TransformOperation::SCALE_3D:
     148        case TransformOperation::SCALE: {
     149            ScaleTransformOperation* transform = static_cast<ScaleTransformOperation*>(m_value.operations()[j].get());
     150            operations.operations().append(ScaleTransformOperation::create(transform->x(), transform->y(), transform->z(), operationType));
     151            break;
     152        }
     153        case TransformOperation::TRANSLATE_X:
     154        case TransformOperation::TRANSLATE_Y:
     155        case TransformOperation::TRANSLATE_Z:
     156        case TransformOperation::TRANSLATE_3D:
     157        case TransformOperation::TRANSLATE: {
     158            TranslateTransformOperation* transform = static_cast<TranslateTransformOperation*>(m_value.operations()[j].get());
     159            operations.operations().append(TranslateTransformOperation::create(transform->x(), transform->y(), transform->z(), operationType));
     160            break;
     161        }
     162        case TransformOperation::ROTATE_X:
     163        case TransformOperation::ROTATE_Y:
     164        case TransformOperation::ROTATE_3D:
     165        case TransformOperation::ROTATE: {
     166            RotateTransformOperation* transform = static_cast<RotateTransformOperation*>(m_value.operations()[j].get());
     167            operations.operations().append(RotateTransformOperation::create(transform->x(), transform->y(), transform->z(), transform->angle(), operationType));
     168            break;
     169        }
     170        case TransformOperation::SKEW_X:
     171        case TransformOperation::SKEW_Y:
     172        case TransformOperation::SKEW: {
     173            SkewTransformOperation* transform = static_cast<SkewTransformOperation*>(m_value.operations()[j].get());
     174            operations.operations().append(SkewTransformOperation::create(transform->angleX(), transform->angleY(), operationType));
     175            break;
     176        }
     177        case TransformOperation::MATRIX: {
     178            MatrixTransformOperation* transform = static_cast<MatrixTransformOperation*>(m_value.operations()[j].get());
     179            TransformationMatrix m = transform->matrix();
     180            operations.operations().append(MatrixTransformOperation::create(m.a(), m.b(), m.c(), m.d(), m.e(), m.f()));
     181            break;
     182        }
     183        case TransformOperation::MATRIX_3D: {
     184            Matrix3DTransformOperation* transform = static_cast<Matrix3DTransformOperation*>(m_value.operations()[j].get());
     185            operations.operations().append(Matrix3DTransformOperation::create(transform->matrix()));
     186            break;
     187        }
     188        case TransformOperation::PERSPECTIVE: {
     189            PerspectiveTransformOperation* transform = static_cast<PerspectiveTransformOperation*>(m_value.operations()[j].get());
     190            operations.operations().append(PerspectiveTransformOperation::create(transform->perspective()));
     191            break;
     192        }
     193        case TransformOperation::IDENTITY: {
     194            operations.operations().append(IdentityTransformOperation::create());
     195            break;
     196        }
     197        case TransformOperation::NONE:
     198            // Do nothing.
     199            break;
     200        } // switch
     201    } // for each operation
     202
     203    return CCTransformKeyframe::create(time(), operations, timingFunction() ? cloneTimingFunction(timingFunction()) : nullptr);
     204}
     205
     206PassOwnPtr<CCKeyframedFloatAnimationCurve> CCKeyframedFloatAnimationCurve::create()
     207{
     208    return adoptPtr(new CCKeyframedFloatAnimationCurve);
     209}
     210
     211CCKeyframedFloatAnimationCurve::CCKeyframedFloatAnimationCurve()
    70212{
    71213}
     
    75217}
    76218
     219void CCKeyframedFloatAnimationCurve::addKeyframe(PassOwnPtr<CCFloatKeyframe> keyframe)
     220{
     221    insertKeyframe(keyframe, m_keyframes);
     222}
     223
    77224double CCKeyframedFloatAnimationCurve::duration() const
    78225{
    79     return m_keyframes.last().time - m_keyframes.first().time;
     226    return m_keyframes.last()->time() - m_keyframes.first()->time();
    80227}
    81228
    82229PassOwnPtr<CCAnimationCurve> CCKeyframedFloatAnimationCurve::clone() const
    83230{
    84     return adoptPtr(new CCKeyframedFloatAnimationCurve(*this));
     231    OwnPtr<CCKeyframedFloatAnimationCurve> toReturn(CCKeyframedFloatAnimationCurve::create());
     232    for (size_t i = 0; i < m_keyframes.size(); ++i)
     233        toReturn->addKeyframe(m_keyframes[i]->clone());
     234    return toReturn.release();
    85235}
    86236
    87237float CCKeyframedFloatAnimationCurve::getValue(double t) const
    88238{
    89     if (t <= m_keyframes.first().time)
    90         return m_keyframes.first().value;
    91 
    92     if (t >= m_keyframes.last().time)
    93         return m_keyframes.last().value;
     239    if (t <= m_keyframes.first()->time())
     240        return m_keyframes.first()->value();
     241
     242    if (t >= m_keyframes.last()->time())
     243        return m_keyframes.last()->value();
    94244
    95245    size_t i = 0;
    96246    for (; i < m_keyframes.size() - 1; ++i) {
    97         if (t < m_keyframes[i+1].time)
    98             break;
    99     }
    100 
    101     float progress = static_cast<float>((t - m_keyframes[i].time) / (m_keyframes[i+1].time - m_keyframes[i].time));
    102     // FIXME: apply timing function here.
    103     return m_keyframes[i].value + (m_keyframes[i+1].value - m_keyframes[i].value) * progress;
    104 }
    105 
    106 PassOwnPtr<CCKeyframedTransformAnimationCurve> CCKeyframedTransformAnimationCurve::create(const Vector<CCTransformKeyframe>& keyframes)
    107 {
    108     if (!keyframes.size() || !keyframesAreSorted(keyframes))
    109         return nullptr;
    110 
    111     return adoptPtr(new CCKeyframedTransformAnimationCurve(keyframes));
    112 }
    113 
    114 CCKeyframedTransformAnimationCurve::CCKeyframedTransformAnimationCurve(const Vector<CCTransformKeyframe>& keyframes)
    115     : m_keyframes(keyframes)
    116 {
    117 }
    118 
    119 CCKeyframedTransformAnimationCurve::~CCKeyframedTransformAnimationCurve() { }
     247        if (t < m_keyframes[i+1]->time())
     248            break;
     249    }
     250
     251    float progress = static_cast<float>((t - m_keyframes[i]->time()) / (m_keyframes[i+1]->time() - m_keyframes[i]->time()));
     252
     253    if (m_keyframes[i]->timingFunction())
     254        progress = m_keyframes[i]->timingFunction()->getValue(progress);
     255
     256    return m_keyframes[i]->value() + (m_keyframes[i+1]->value() - m_keyframes[i]->value()) * progress;
     257}
     258
     259PassOwnPtr<CCKeyframedTransformAnimationCurve> CCKeyframedTransformAnimationCurve::create()
     260{
     261    return adoptPtr(new CCKeyframedTransformAnimationCurve);
     262}
     263
     264CCKeyframedTransformAnimationCurve::CCKeyframedTransformAnimationCurve()
     265{
     266}
     267
     268CCKeyframedTransformAnimationCurve::~CCKeyframedTransformAnimationCurve()
     269{
     270}
     271
     272void CCKeyframedTransformAnimationCurve::addKeyframe(PassOwnPtr<CCTransformKeyframe> keyframe)
     273{
     274    insertKeyframe(keyframe, m_keyframes);
     275}
    120276
    121277double CCKeyframedTransformAnimationCurve::duration() const
    122278{
    123     return m_keyframes.last().time - m_keyframes.first().time;
     279    return m_keyframes.last()->time() - m_keyframes.first()->time();
    124280}
    125281
    126282PassOwnPtr<CCAnimationCurve> CCKeyframedTransformAnimationCurve::clone() const
    127283{
    128     Vector<CCTransformKeyframe> keyframes;
    129     // We need to do a deep copy of all of the keyframes since they contain ref
    130     // pointers to TransformOperation objects.
    131     for (size_t i = 0; i < m_keyframes.size(); ++i) {
    132         CCTransformKeyframe keyframe(m_keyframes[i].time);
    133         for (size_t j = 0; j < m_keyframes[i].value.size(); ++j) {
    134             TransformOperation::OperationType operationType = m_keyframes[i].value.operations()[j]->getOperationType();
    135             switch (operationType) {
    136             case TransformOperation::SCALE_X:
    137             case TransformOperation::SCALE_Y:
    138             case TransformOperation::SCALE_Z:
    139             case TransformOperation::SCALE_3D:
    140             case TransformOperation::SCALE: {
    141                 ScaleTransformOperation* transform = static_cast<ScaleTransformOperation*>(m_keyframes[i].value.operations()[j].get());
    142                 keyframe.value.operations().append(ScaleTransformOperation::create(transform->x(), transform->y(), transform->z(), operationType));
    143                 break;
    144             }
    145             case TransformOperation::TRANSLATE_X:
    146             case TransformOperation::TRANSLATE_Y:
    147             case TransformOperation::TRANSLATE_Z:
    148             case TransformOperation::TRANSLATE_3D:
    149             case TransformOperation::TRANSLATE: {
    150                 TranslateTransformOperation* transform = static_cast<TranslateTransformOperation*>(m_keyframes[i].value.operations()[j].get());
    151                 keyframe.value.operations().append(TranslateTransformOperation::create(transform->x(), transform->y(), transform->z(), operationType));
    152                 break;
    153             }
    154             case TransformOperation::ROTATE_X:
    155             case TransformOperation::ROTATE_Y:
    156             case TransformOperation::ROTATE_3D:
    157             case TransformOperation::ROTATE: {
    158                 RotateTransformOperation* transform = static_cast<RotateTransformOperation*>(m_keyframes[i].value.operations()[j].get());
    159                 keyframe.value.operations().append(RotateTransformOperation::create(transform->x(), transform->y(), transform->z(), transform->angle(), operationType));
    160                 break;
    161             }
    162             case TransformOperation::SKEW_X:
    163             case TransformOperation::SKEW_Y:
    164             case TransformOperation::SKEW: {
    165                 SkewTransformOperation* transform = static_cast<SkewTransformOperation*>(m_keyframes[i].value.operations()[j].get());
    166                 keyframe.value.operations().append(SkewTransformOperation::create(transform->angleX(), transform->angleY(), operationType));
    167                 break;
    168             }
    169             case TransformOperation::MATRIX: {
    170                 MatrixTransformOperation* transform = static_cast<MatrixTransformOperation*>(m_keyframes[i].value.operations()[j].get());
    171                 TransformationMatrix m = transform->matrix();
    172                 keyframe.value.operations().append(MatrixTransformOperation::create(m.a(), m.b(), m.c(), m.d(), m.e(), m.f()));
    173                 break;
    174             }
    175             case TransformOperation::MATRIX_3D: {
    176                 Matrix3DTransformOperation* transform = static_cast<Matrix3DTransformOperation*>(m_keyframes[i].value.operations()[j].get());
    177                 keyframe.value.operations().append(Matrix3DTransformOperation::create(transform->matrix()));
    178                 break;
    179             }
    180             case TransformOperation::PERSPECTIVE: {
    181                 PerspectiveTransformOperation* transform = static_cast<PerspectiveTransformOperation*>(m_keyframes[i].value.operations()[j].get());
    182                 keyframe.value.operations().append(PerspectiveTransformOperation::create(transform->perspective()));
    183                 break;
    184             }
    185             case TransformOperation::IDENTITY: {
    186                 keyframe.value.operations().append(IdentityTransformOperation::create());
    187                 break;
    188             }
    189             case TransformOperation::NONE:
    190                 // Do nothing.
    191                 break;
    192             } // switch
    193         } // for each operation
    194         keyframes.append(keyframe);
    195     }
    196     return CCKeyframedTransformAnimationCurve::create(keyframes);
     284    OwnPtr<CCKeyframedTransformAnimationCurve> toReturn(CCKeyframedTransformAnimationCurve::create());
     285    for (size_t i = 0; i < m_keyframes.size(); ++i)
     286        toReturn->addKeyframe(m_keyframes[i]->clone());
     287    return toReturn.release();
    197288}
    198289
     
    201292    TransformationMatrix transformMatrix;
    202293
    203     if (t <= m_keyframes.first().time) {
    204         m_keyframes.first().value.apply(layerSize, transformMatrix);
     294    if (t <= m_keyframes.first()->time()) {
     295        m_keyframes.first()->value().apply(layerSize, transformMatrix);
    205296        return transformMatrix;
    206297    }
    207298
    208     if (t >= m_keyframes.last().time) {
    209         m_keyframes.last().value.apply(layerSize, transformMatrix);
     299    if (t >= m_keyframes.last()->time()) {
     300        m_keyframes.last()->value().apply(layerSize, transformMatrix);
    210301        return transformMatrix;
    211302    }
     
    213304    size_t i = 0;
    214305    for (; i < m_keyframes.size() - 1; ++i) {
    215         if (t < m_keyframes[i+1].time)
    216             break;
    217     }
    218 
    219     // FIXME: apply timing function here.
    220     double progress = (t - m_keyframes[i].time) / (m_keyframes[i+1].time - m_keyframes[i].time);
    221 
    222     if (m_keyframes[i].value.operationsMatch(m_keyframes[i+1].value)) {
    223         for (size_t j = 0; j < m_keyframes[i+1].value.size(); ++j)
    224             m_keyframes[i+1].value.operations()[j]->blend(m_keyframes[i].value.at(j), progress)->apply(transformMatrix, layerSize);
     306        if (t < m_keyframes[i+1]->time())
     307            break;
     308    }
     309
     310    double progress = (t - m_keyframes[i]->time()) / (m_keyframes[i+1]->time() - m_keyframes[i]->time());
     311
     312    if (m_keyframes[i]->timingFunction())
     313        progress = m_keyframes[i]->timingFunction()->getValue(progress);
     314
     315    if (m_keyframes[i]->value().operationsMatch(m_keyframes[i+1]->value())) {
     316        for (size_t j = 0; j < m_keyframes[i+1]->value().size(); ++j)
     317            m_keyframes[i+1]->value().operations()[j]->blend(m_keyframes[i]->value().at(j), progress)->apply(transformMatrix, layerSize);
    225318    } else {
    226319        TransformationMatrix source;
    227320
    228         m_keyframes[i].value.apply(layerSize, source);
    229         m_keyframes[i+1].value.apply(layerSize, transformMatrix);
     321        m_keyframes[i]->value().apply(layerSize, source);
     322        m_keyframes[i+1]->value().apply(layerSize, transformMatrix);
    230323
    231324        transformMatrix.blend(source, progress);
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCKeyframedAnimationCurve.h

    r108727 r109297  
    2828#include "TransformOperations.h"
    2929#include "cc/CCAnimationCurve.h"
     30#include "cc/CCTimingFunction.h"
    3031
    3132#include <wtf/OwnPtr.h>
     
    3435namespace WebCore {
    3536
    36 struct CCFloatKeyframe {
    37     CCFloatKeyframe(double time, float value)
    38         : time(time)
    39         , value(value)
    40     {
    41     }
     37class CCKeyframe {
     38public:
     39    double time() const;
     40    const CCTimingFunction* timingFunction() const;
    4241
    43     double time;
    44     float value;
     42protected:
     43    CCKeyframe(double time, PassOwnPtr<CCTimingFunction>);
     44    virtual ~CCKeyframe();
     45
     46private:
     47    double m_time;
     48    OwnPtr<CCTimingFunction> m_timingFunction;
    4549};
    4650
    47 struct CCTransformKeyframe {
    48     explicit CCTransformKeyframe(double time)
    49         : time(time)
    50     {
    51     }
     51class CCFloatKeyframe : public CCKeyframe {
     52public:
     53    static PassOwnPtr<CCFloatKeyframe> create(double time, float value, PassOwnPtr<CCTimingFunction>);
     54    virtual ~CCFloatKeyframe();
    5255
    53     CCTransformKeyframe(double time, const TransformOperations& value)
    54         : time(time)
    55         , value(value)
    56     {
    57     }
     56    float value() const;
    5857
    59     double time;
    60     TransformOperations value;
     58    PassOwnPtr<CCFloatKeyframe> clone() const;
     59
     60private:
     61    CCFloatKeyframe(double time, float value, PassOwnPtr<CCTimingFunction>);
     62
     63    float m_value;
     64};
     65
     66class CCTransformKeyframe : public CCKeyframe {
     67public:
     68    static PassOwnPtr<CCTransformKeyframe> create(double time, const TransformOperations& value, PassOwnPtr<CCTimingFunction>);
     69    virtual ~CCTransformKeyframe();
     70
     71    const TransformOperations& value() const;
     72
     73    PassOwnPtr<CCTransformKeyframe> clone() const;
     74
     75private:
     76    CCTransformKeyframe(double time, const TransformOperations& value, PassOwnPtr<CCTimingFunction>);
     77
     78    TransformOperations m_value;
    6179};
    6280
     
    6482public:
    6583    // It is required that the keyframes be sorted by time.
    66     static PassOwnPtr<CCKeyframedFloatAnimationCurve> create(const Vector<CCFloatKeyframe>& keyframes);
     84    static PassOwnPtr<CCKeyframedFloatAnimationCurve> create();
    6785
    6886    virtual ~CCKeyframedFloatAnimationCurve();
     87
     88    void addKeyframe(PassOwnPtr<CCFloatKeyframe>);
    6989
    7090    // CCAnimationCurve implementation
     
    7696
    7797private:
    78     explicit CCKeyframedFloatAnimationCurve(const Vector<CCFloatKeyframe>&);
     98    CCKeyframedFloatAnimationCurve();
    7999
    80100    // Always sorted in order of increasing time. No two keyframes have the
    81101    // same time.
    82     Vector<CCFloatKeyframe> m_keyframes;
     102    Vector<OwnPtr<CCFloatKeyframe> > m_keyframes;
    83103};
    84104
     
    86106public:
    87107    // It is required that the keyframes be sorted by time.
    88     static PassOwnPtr<CCKeyframedTransformAnimationCurve> create(const Vector<CCTransformKeyframe>& keyframes);
     108    static PassOwnPtr<CCKeyframedTransformAnimationCurve> create();
    89109
    90110    virtual ~CCKeyframedTransformAnimationCurve();
     111
     112    void addKeyframe(PassOwnPtr<CCTransformKeyframe>);
    91113
    92114    // CCAnimationCurve implementation
     
    98120
    99121private:
    100     explicit CCKeyframedTransformAnimationCurve(const Vector<CCTransformKeyframe>&);
     122    CCKeyframedTransformAnimationCurve();
    101123
    102124    // Always sorted in order of increasing time. No two keyframes have the
    103125    // same time.
    104     Vector<CCTransformKeyframe> m_keyframes;
     126    Vector<OwnPtr<CCTransformKeyframe> > m_keyframes;
    105127};
    106128
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerAnimationController.cpp

    r108727 r109297  
    3838namespace {
    3939
    40 template <typename Keyframe, typename Value>
    41 void appendKeyframe(Vector<Keyframe>& keyframes, double keyTime, const Value* value)
    42 {
    43     keyframes.append(Keyframe(keyTime, value->value()));
     40template <class Value, class Keyframe, class Curve>
     41void appendKeyframe(Curve& curve, double keyTime, const Value* value, PassOwnPtr<CCTimingFunction> timingFunction)
     42{
     43    curve.addKeyframe(Keyframe::create(keyTime, value->value(), timingFunction));
    4444}
    4545
    4646template <>
    47 void appendKeyframe<CCTransformKeyframe, TransformAnimationValue>(Vector<CCTransformKeyframe>& keyframes, double keyTime, const TransformAnimationValue* value)
    48 {
    49     keyframes.append(CCTransformKeyframe(keyTime, *value->value()));
     47void appendKeyframe<TransformAnimationValue, CCTransformKeyframe, CCKeyframedTransformAnimationCurve>(CCKeyframedTransformAnimationCurve& curve, double keyTime, const TransformAnimationValue* value, PassOwnPtr<CCTimingFunction> timingFunction)
     48{
     49    curve.addKeyframe(CCTransformKeyframe::create(keyTime, *value->value(), timingFunction));
    5050}
    5151
     
    6565        return nullptr;
    6666
     67    OwnPtr<Curve> curve = Curve::create();
    6768    Vector<Keyframe> keyframes;
    6869
     
    7071        const Value* originalValue = static_cast<const Value*>(valueList.at(i));
    7172
    72         // FIXME: add support for timing functions.
    73         if (originalValue->timingFunction() && originalValue->timingFunction()->type() != TimingFunction::LinearFunction)
    74             return nullptr;
     73        OwnPtr<CCTimingFunction> timingFunction;
     74        if (originalValue->timingFunction()) {
     75            switch (originalValue->timingFunction()->type()) {
     76            case TimingFunction::StepsFunction:
     77                // FIXME: add support for steps timing function.
     78                return nullptr;
     79            case TimingFunction::LinearFunction:
     80                // Don't set the timing function. Keyframes are interpolated linearly if there is no timing function.
     81                break;
     82            case TimingFunction::CubicBezierFunction:
     83                const CubicBezierTimingFunction* originalTimingFunction = static_cast<const CubicBezierTimingFunction*>(originalValue->timingFunction());
     84                timingFunction = CCCubicBezierTimingFunction::create(originalTimingFunction->x1(), originalTimingFunction->y1(), originalTimingFunction->x2(), originalTimingFunction->y2());
     85                break;
     86            } // switch
     87        } else
     88            timingFunction = CCEaseTimingFunction::create();
    7589
    7690        double duration = (animation && animation->isDurationSet()) ? animation->duration() : 1;
    77         appendKeyframe(keyframes, originalValue->keyTime() * duration, originalValue);
    78     }
    79 
    80     OwnPtr<Curve> curve = Curve::create(keyframes);
     91        appendKeyframe<Value, Keyframe, Curve>(*curve, originalValue->keyTime() * duration, originalValue, timingFunction.release());
     92    }
    8193
    8294    OwnPtr<CCActiveAnimation> anim = CCActiveAnimation::create(curve.release(), animationId, groupId, targetProperty);
  • trunk/Source/WebKit/chromium/ChangeLog

    r109288 r109297  
     12012-02-29  Ian Vollick  <vollick@chromium.org>
     2
     3        [chromium] Add impl-thread support for animation-timing-function
     4        https://bugs.webkit.org/show_bug.cgi?id=79819
     5
     6        Reviewed by James Robinson.
     7
     8        * tests/CCKeyframedAnimationCurveTest.cpp:
     9        (WebCore::TEST):
     10        (WebCore):
     11
    1122012-02-29  David Levin  <levin@chromium.org>
    213
  • trunk/Source/WebKit/chromium/tests/CCKeyframedAnimationCurveTest.cpp

    r108727 r109297  
    4949TEST(CCKeyframedAnimationCurveTest, OneFloatKeyframe)
    5050{
    51     Vector<CCFloatKeyframe> keyframes;
    52     keyframes.append(CCFloatKeyframe(0, 2));
    53     OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create(keyframes));
     51    OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
     52    curve->addKeyframe(CCFloatKeyframe::create(0, 2, nullptr));
    5453    EXPECT_FLOAT_EQ(2, curve->getValue(-1));
    5554    EXPECT_FLOAT_EQ(2, curve->getValue(0));
     
    6261TEST(CCKeyframedAnimationCurveTest, TwoFloatKeyframe)
    6362{
    64     Vector<CCFloatKeyframe> keyframes;
    65     keyframes.append(CCFloatKeyframe(0, 2));
    66     keyframes.append(CCFloatKeyframe(1, 4));
    67     OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create(keyframes));
     63    OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
     64    curve->addKeyframe(CCFloatKeyframe::create(0, 2, nullptr));
     65    curve->addKeyframe(CCFloatKeyframe::create(1, 4, nullptr));
    6866    EXPECT_FLOAT_EQ(2, curve->getValue(-1));
    6967    EXPECT_FLOAT_EQ(2, curve->getValue(0));
     
    7674TEST(CCKeyframedAnimationCurveTest, ThreeFloatKeyframe)
    7775{
    78     Vector<CCFloatKeyframe> keyframes;
    79     keyframes.append(CCFloatKeyframe(0, 2));
    80     keyframes.append(CCFloatKeyframe(1, 4));
    81     keyframes.append(CCFloatKeyframe(2, 8));
    82     OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create(keyframes));
     76    OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
     77    curve->addKeyframe(CCFloatKeyframe::create(0, 2, nullptr));
     78    curve->addKeyframe(CCFloatKeyframe::create(1, 4, nullptr));
     79    curve->addKeyframe(CCFloatKeyframe::create(2, 8, nullptr));
    8380    EXPECT_FLOAT_EQ(2, curve->getValue(-1));
    8481    EXPECT_FLOAT_EQ(2, curve->getValue(0));
     
    9390TEST(CCKeyframedAnimationCurveTest, RepeatedFloatKeyTimes)
    9491{
    95     Vector<CCFloatKeyframe> keyframes;
    96     // A step function.
    97     keyframes.append(CCFloatKeyframe(0, 4));
    98     keyframes.append(CCFloatKeyframe(1, 4));
    99     keyframes.append(CCFloatKeyframe(1, 6));
    100     keyframes.append(CCFloatKeyframe(2, 6));
    101     OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create(keyframes));
     92    OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
     93    curve->addKeyframe(CCFloatKeyframe::create(0, 4, nullptr));
     94    curve->addKeyframe(CCFloatKeyframe::create(1, 4, nullptr));
     95    curve->addKeyframe(CCFloatKeyframe::create(1, 6, nullptr));
     96    curve->addKeyframe(CCFloatKeyframe::create(2, 6, nullptr));
    10297
    10398    EXPECT_FLOAT_EQ(4, curve->getValue(-1));
     
    118113TEST(CCKeyframedAnimationCurveTest, OneTransformKeyframe)
    119114{
    120     Vector<CCTransformKeyframe> keyframes;
     115    OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create());
    121116    TransformOperations operations;
    122117    operations.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X));
    123     keyframes.append(CCTransformKeyframe(0, operations));
    124     OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create(keyframes));
     118    curve->addKeyframe(CCTransformKeyframe::create(0, operations, nullptr));
     119
    125120    IntSize layerSize; // ignored
    126121    expectTranslateX(2, curve->getValue(-1, layerSize));
     
    134129TEST(CCKeyframedAnimationCurveTest, TwoTransformKeyframe)
    135130{
    136     Vector<CCTransformKeyframe> keyframes;
     131    OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create());
    137132    TransformOperations operations1;
    138133    operations1.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X));
    139134    TransformOperations operations2;
    140135    operations2.operations().append(TranslateTransformOperation::create(Length(4, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X));
    141     keyframes.append(CCTransformKeyframe(0, operations1));
    142     keyframes.append(CCTransformKeyframe(1, operations2));
    143     OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create(keyframes));
     136    curve->addKeyframe(CCTransformKeyframe::create(0, operations1, nullptr));
     137    curve->addKeyframe(CCTransformKeyframe::create(1, operations2, nullptr));
    144138    IntSize layerSize; // ignored
    145139    expectTranslateX(2, curve->getValue(-1, layerSize));
     
    153147TEST(CCKeyframedAnimationCurveTest, ThreeTransformKeyframe)
    154148{
    155     Vector<CCTransformKeyframe> keyframes;
     149    OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create());
    156150    TransformOperations operations1;
    157151    operations1.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X));
     
    160154    TransformOperations operations3;
    161155    operations3.operations().append(TranslateTransformOperation::create(Length(8, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X));
    162     keyframes.append(CCTransformKeyframe(0, operations1));
    163     keyframes.append(CCTransformKeyframe(1, operations2));
    164     keyframes.append(CCTransformKeyframe(2, operations3));
    165     OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create(keyframes));
     156    curve->addKeyframe(CCTransformKeyframe::create(0, operations1, nullptr));
     157    curve->addKeyframe(CCTransformKeyframe::create(1, operations2, nullptr));
     158    curve->addKeyframe(CCTransformKeyframe::create(2, operations3, nullptr));
    166159    IntSize layerSize; // ignored
    167160    expectTranslateX(2, curve->getValue(-1, layerSize));
     
    177170TEST(CCKeyframedAnimationCurveTest, RepeatedTransformKeyTimes)
    178171{
    179     Vector<CCTransformKeyframe> keyframes;
     172    OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create());
    180173    // A step function.
    181174    TransformOperations operations1;
     
    187180    TransformOperations operations4;
    188181    operations4.operations().append(TranslateTransformOperation::create(Length(6, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X));
    189     keyframes.append(CCTransformKeyframe(0, operations1));
    190     keyframes.append(CCTransformKeyframe(1, operations2));
    191     keyframes.append(CCTransformKeyframe(1, operations3));
    192     keyframes.append(CCTransformKeyframe(2, operations4));
    193     OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create(keyframes));
     182    curve->addKeyframe(CCTransformKeyframe::create(0, operations1, nullptr));
     183    curve->addKeyframe(CCTransformKeyframe::create(1, operations2, nullptr));
     184    curve->addKeyframe(CCTransformKeyframe::create(1, operations3, nullptr));
     185    curve->addKeyframe(CCTransformKeyframe::create(2, operations4, nullptr));
    194186
    195187    IntSize layerSize; // ignored
     
    210202}
    211203
    212 // Tests that invalid lists of keyframes result in nothing being returned from ::create.
    213 TEST(CCKeyframedAnimationCurveTest, InvalidKeyframes)
    214 {
    215     // It is invalid to pass an empty vector of keyframes to create.
    216     Vector<CCTransformKeyframe> transformKeyframes;
    217     OwnPtr<CCKeyframedTransformAnimationCurve> transformCurve = CCKeyframedTransformAnimationCurve::create(transformKeyframes);
    218     EXPECT_FALSE(transformCurve.get());
    219 
    220     Vector<CCFloatKeyframe> floatKeyframes;
    221     OwnPtr<CCKeyframedFloatAnimationCurve> floatCurve = CCKeyframedFloatAnimationCurve::create(floatKeyframes);
    222     EXPECT_FALSE(floatCurve.get());
    223 
    224     // It is invalid to pass a vector of unsorted keyframes to create;
    225     TransformOperations operations1;
    226     operations1.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X));
    227     TransformOperations operations2;
    228     operations2.operations().append(TranslateTransformOperation::create(Length(4, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X));
    229     transformKeyframes.append(CCTransformKeyframe(1, operations1));
    230     transformKeyframes.append(CCTransformKeyframe(0, operations2));
    231     transformCurve = CCKeyframedTransformAnimationCurve::create(transformKeyframes);
    232     EXPECT_FALSE(transformCurve.get());
    233 
    234     floatKeyframes.append(CCFloatKeyframe(1, 2));
    235     floatKeyframes.append(CCFloatKeyframe(0, 4));
    236     floatCurve = CCKeyframedFloatAnimationCurve::create(floatKeyframes);
    237     EXPECT_FALSE(floatCurve.get());
    238 }
    239 
     204// Tests that the keyframes may be added out of order.
     205TEST(CCKeyframedAnimationCurveTest, UnsortedKeyframes)
     206{
     207    OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
     208    curve->addKeyframe(CCFloatKeyframe::create(2, 8, nullptr));
     209    curve->addKeyframe(CCFloatKeyframe::create(0, 2, nullptr));
     210    curve->addKeyframe(CCFloatKeyframe::create(1, 4, nullptr));
     211    EXPECT_FLOAT_EQ(2, curve->getValue(-1));
     212    EXPECT_FLOAT_EQ(2, curve->getValue(0));
     213    EXPECT_FLOAT_EQ(3, curve->getValue(0.5));
     214    EXPECT_FLOAT_EQ(4, curve->getValue(1));
     215    EXPECT_FLOAT_EQ(6, curve->getValue(1.5));
     216    EXPECT_FLOAT_EQ(8, curve->getValue(2));
     217    EXPECT_FLOAT_EQ(8, curve->getValue(3));
     218}
     219
     220// Tests that a cubic bezier timing function works as expected.
     221TEST(CCKeyframedAnimationCurveTest, CubicBezierTimingFunction)
     222{
     223    OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
     224    curve->addKeyframe(CCFloatKeyframe::create(0, 0, CCCubicBezierTimingFunction::create(0.25, 0, 0.75, 1)));
     225    curve->addKeyframe(CCFloatKeyframe::create(1, 1, nullptr));
     226
     227    EXPECT_FLOAT_EQ(0, curve->getValue(0));
     228    EXPECT_LT(0, curve->getValue(0.25));
     229    EXPECT_GT(0.25, curve->getValue(0.25));
     230    EXPECT_FLOAT_EQ(0.5, curve->getValue(0.5));
     231    EXPECT_LT(0.75, curve->getValue(0.75));
     232    EXPECT_GT(1, curve->getValue(0.75));
     233    EXPECT_FLOAT_EQ(1, curve->getValue(1));
     234}
    240235
    241236} // namespace
Note: See TracChangeset for help on using the changeset viewer.