⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 294752 in webkit


Ignore:
Timestamp:
May 24, 2022, 9:57:53 AM (4 years ago)
Author:
Martin Robinson
Message:

REGRESSION (r289032): rotate animation doesn't interpolate between 0 and 1turn without forced 50% https://bugs.webkit.org/show_bug.cgi?id=239906

Reviewed by Simon Fraser.

When using CoreAnimation non-matrix animations to animate rotations,
CoreAnimation will use the shortest direction between two rotation
angles. This means that a rotation from 0 to 360 will not rotate at all.
This is different from how CSS works, where it expects the animation to
do a full turn. In order to avoid problems with this difference, when an
animation includes larger angles (> 180 degrees), fall back to software
animation.

No new tests. It is difficult to make a test for this because
when pausing animations the software path is used.

  • LayoutTests/animations/3d/full-rotation-animation-expected.html: Added.
  • LayoutTests/animations/3d/full-rotation-animation.html: Added.
  • Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp:

(WebCore::transformationAnimationValueAt):
(WebCore::hasBigRotationAngle):
(WebCore::GraphicsLayerCA::createTransformAnimationsFromKeyframes):
(WebCore::GraphicsLayerCA::setTransformAnimationEndpoints):

  • Source/WebCore/platform/graphics/transforms/TransformOperations.h:

(WebCore::SharedPrimitivesPrefix::primitives const):
(WebCore::SharedPrimitivesPrefix::primitives): Deleted.

Canonical link: https://commits.webkit.org/250920@main

Location:
trunk
Files:
2 added
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp

    r294641 r294752  
    34643464}
    34653465
     3466static const TransformOperations& transformationAnimationValueAt(const KeyframeValueList& valueList, unsigned i)
     3467{
     3468    return static_cast<const TransformAnimationValue&>(valueList.at(i)).value();
     3469}
     3470
     3471static bool hasBigRotationAngle(const KeyframeValueList& valueList, const SharedPrimitivesPrefix& prefix)
     3472{
     3473    // Hardware non-matrix animations are used for every function in the shared primitives prefix.
     3474    // These kind of animations have issues with large rotation angles, so for every function that
     3475    // will be represented as a hardware non-matrix animation, check that for each of those functions
     3476    // the animation that's created for it will not have two consecutive keyframes that have a large
     3477    // rotation angle between them.
     3478    const auto& primitives = prefix.primitives();
     3479    for (unsigned animationIndex = 0; animationIndex < primitives.size(); ++animationIndex) {
     3480        auto type = primitives[animationIndex];
     3481        if (type != TransformOperation::ROTATE && type != TransformOperation::ROTATE_3D)
     3482            continue;
     3483        for (size_t i = 1; i < valueList.size(); ++i) {
     3484            // Since the shared primitive at this index is a rotation, both of these transform
     3485            // functions should be RotateTransformOperations.
     3486            auto prevOperation = downcast<RotateTransformOperation>(transformationAnimationValueAt(valueList, i - 1).at(animationIndex));
     3487            auto operation = downcast<RotateTransformOperation>(transformationAnimationValueAt(valueList, i).at(animationIndex));
     3488            auto angle = std::abs((prevOperation ? prevOperation->angle() : 0.0) - (operation ? operation->angle() : 0.0));
     3489            if (angle > 180.0)
     3490                return true;
     3491        }
     3492    }
     3493
     3494    return false;
     3495}
     3496
    34663497bool GraphicsLayerCA::createTransformAnimationsFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& animationName, Seconds timeOffset, const FloatSize& boxSize, bool keyframesShouldUseAnimationWideTimingFunction)
    34673498{
     
    34803511    SharedPrimitivesPrefix prefix;
    34813512    for (size_t i = 0; i < valueList.size(); ++i)
    3482         prefix.update(static_cast<const TransformAnimationValue&>(valueList.at(i)).value());
     3513        prefix.update(transformationAnimationValueAt(valueList, i));
     3514
     3515    // If this animation has a big rotation between two keyframes, fall back to software animation. CoreAnimation
     3516    // will always take the shortest path between two rotations, which will result in incorrect animation when
     3517    // the keyframes specify angles larger than one half rotation.
     3518    if (hasBigRotationAngle(valueList, prefix))
     3519        return false;
    34833520
    34843521    const auto& primitives = prefix.primitives();
     
    37093746    unsigned toIndex = forwards;
    37103747   
    3711     auto& startValue = static_cast<const TransformAnimationValue&>(valueList.at(fromIndex));
    3712     auto& endValue = static_cast<const TransformAnimationValue&>(valueList.at(toIndex));
     3748    const auto& startValue = transformationAnimationValueAt(valueList, fromIndex);
     3749    const auto& endValue = transformationAnimationValueAt(valueList, toIndex);
    37133750
    37143751    if (isMatrixAnimation) {
    37153752        TransformationMatrix fromTransform, toTransform;
    3716         startValue.value().apply(boxSize, fromTransform);
    3717         endValue.value().apply(boxSize, toTransform);
     3753        startValue.apply(boxSize, fromTransform);
     3754        endValue.apply(boxSize, toTransform);
    37183755
    37193756        // If any matrix is singular, CA won't animate it correctly. So fall back to software animation
     
    37263763        if (isTransformTypeNumber(transformOpType)) {
    37273764            float fromValue;
    3728             getTransformFunctionValue(startValue.value().at(functionIndex), transformOpType, boxSize, fromValue);
     3765            getTransformFunctionValue(startValue.at(functionIndex), transformOpType, boxSize, fromValue);
    37293766            basicAnim->setFromValue(fromValue);
    37303767           
    37313768            float toValue;
    3732             getTransformFunctionValue(endValue.value().at(functionIndex), transformOpType, boxSize, toValue);
     3769            getTransformFunctionValue(endValue.at(functionIndex), transformOpType, boxSize, toValue);
    37333770            basicAnim->setToValue(toValue);
    37343771        } else if (isTransformTypeFloatPoint3D(transformOpType)) {
    37353772            FloatPoint3D fromValue;
    3736             getTransformFunctionValue(startValue.value().at(functionIndex), transformOpType, boxSize, fromValue);
     3773            getTransformFunctionValue(startValue.at(functionIndex), transformOpType, boxSize, fromValue);
    37373774            basicAnim->setFromValue(fromValue);
    37383775           
    37393776            FloatPoint3D toValue;
    3740             getTransformFunctionValue(endValue.value().at(functionIndex), transformOpType, boxSize, toValue);
     3777            getTransformFunctionValue(endValue.at(functionIndex), transformOpType, boxSize, toValue);
    37413778            basicAnim->setToValue(toValue);
    37423779        } else {
    37433780            TransformationMatrix fromValue;
    3744             getTransformFunctionValue(startValue.value().at(functionIndex), transformOpType, boxSize, fromValue);
     3781            getTransformFunctionValue(startValue.at(functionIndex), transformOpType, boxSize, fromValue);
    37453782            basicAnim->setFromValue(fromValue);
    37463783
    37473784            TransformationMatrix toValue;
    3748             getTransformFunctionValue(endValue.value().at(functionIndex), transformOpType, boxSize, toValue);
     3785            getTransformFunctionValue(endValue.at(functionIndex), transformOpType, boxSize, toValue);
    37493786            basicAnim->setToValue(toValue);
    37503787        }
  • trunk/Source/WebCore/platform/graphics/transforms/TransformOperations.h

    r290667 r294752  
    119119    void update(const TransformOperations&);
    120120    bool hadIncompatibleTransformFunctions() { return m_indexOfFirstMismatch.has_value(); }
    121     const Vector<TransformOperation::OperationType>& primitives() { return m_primitives; }
     121    const Vector<TransformOperation::OperationType>& primitives() const { return m_primitives; }
    122122
    123123private:
Note: See TracChangeset for help on using the changeset viewer.