2008-09-08 Chris Marrin <cmarrin@apple.com>
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 8 Sep 2008 22:46:41 +0000 (22:46 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 8 Sep 2008 22:46:41 +0000 (22:46 +0000)
        Reviewed by Sam Weinig

        - Animation of -webkit-transform matrix() function should not do linear interpolation
          https://bugs.webkit.org/show_bug.cgi?id=20667

        Test: animations/matrix-anim.html

        * ChangeLog:
        * platform/graphics/AffineTransform.cpp:
        (WebCore::affineTransformDecompose):
        (WebCore::affineTransformCompose):
        (WebCore::AffineTransform::blend):
        * platform/graphics/AffineTransform.h:
        * rendering/style/RenderStyle.cpp:
        (WebCore::MatrixTransformOperation::blend):

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

LayoutTests/ChangeLog
LayoutTests/animations/matrix-anim-expected.txt [new file with mode: 0644]
LayoutTests/animations/matrix-anim.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/platform/graphics/AffineTransform.cpp
WebCore/platform/graphics/AffineTransform.h
WebCore/rendering/style/RenderStyle.cpp

index 2f3d5e9..4b31f9f 100644 (file)
@@ -1,3 +1,13 @@
+2008-09-08  Chris Marrin  <cmarrin@apple.com>
+
+        Reviewed by Sam Weinig.
+
+        - Animation of -webkit-transform matrix() function should not do linear interpolation
+          https://bugs.webkit.org/show_bug.cgi?id=20667
+
+        * animations/matrix-anim-expected.txt: Added.
+        * animations/matrix-anim.html: Added.
+
 2008-09-08  Maciej Stachowiak  <mjs@apple.com>
 
         Reviewed by Anders Carlsson.
diff --git a/LayoutTests/animations/matrix-anim-expected.txt b/LayoutTests/animations/matrix-anim-expected.txt
new file mode 100644 (file)
index 0000000..42f43c3
--- /dev/null
@@ -0,0 +1,2 @@
+This test performs an animation of the matrix operator. It animates over 2 seconds. At 1 second it takes a snapshot and expects the result to be within range.
+PASS
diff --git a/LayoutTests/animations/matrix-anim.html b/LayoutTests/animations/matrix-anim.html
new file mode 100644 (file)
index 0000000..cfcd994
--- /dev/null
@@ -0,0 +1,80 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html lang="en">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+  <title>Animation using matrix()</title>
+  <style type="text/css" media="screen">
+    #box {
+      height: 100px;
+      width: 100px;
+      background-color: blue;
+      -webkit-animation-duration: 1s;
+      -webkit-animation-timing-function: linear;
+      -webkit-animation-name: "anim";
+    }
+    @-webkit-keyframes "anim" {
+        from { -webkit-transform: matrix(0.707, -0.707, 0.707, 0.707, 0, 0); }
+        to   { -webkit-transform: matrix(0.707, 0.707, -0.707, 0.707, 0, 0); }
+    }
+  </style>
+  <script type="text/javascript" charset="utf-8">
+    if (window.layoutTestController) {
+      layoutTestController.dumpAsText();
+      layoutTestController.waitUntilDone();
+    }
+    
+    result = "PASS";
+    const defaultTolerance = 0.1;
+    
+    const expected =  [ 1, 0, 0, 1, 0, 0 ];
+    
+    function isEqual(actual, desired, tolerance)
+    {
+        if (tolerance == undefined || tolerance == 0)
+            tolerance = defaultTolerance;
+        var diff = Math.abs(actual - desired);
+        return diff < tolerance;
+    }
+    
+    function snapshot(which)
+    {
+        if (result != "PASS")
+            return;
+            
+        var t = window.getComputedStyle(document.getElementById('box')).getPropertyCSSValue('-webkit-transform');
+
+        for (i = 0; i < 6; ++i) {
+            var val = t[0][i].getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
+            if (!isEqual(val, expected[i])) {
+                result = "FAIL(matrix["+i+"] was:"+val+", expected:"+expected[i]+")";
+                return;
+            }
+        }
+    }
+
+    function start()
+    {
+        setTimeout("snapshot(0)", 500);
+        
+        window.setTimeout(function() {
+            document.getElementById('result').innerHTML = result;
+            if (window.layoutTestController)
+                layoutTestController.notifyDone();
+        }, 600);
+    }
+    
+    document.addEventListener('webkitAnimationStart', start, false);
+    
+  </script>
+</head>
+<body>
+This test performs an animation of the matrix operator. It animates over 2 seconds.
+At 1 second it takes a snapshot and expects the result to be within range.
+<div id="box">
+</div>
+<div id="result">
+</div>
+</body>
+</html>
index c7b014b..bf5adf1 100644 (file)
@@ -1,3 +1,21 @@
+2008-09-08  Chris Marrin  <cmarrin@apple.com>
+
+        Reviewed by Sam Weinig
+
+        - Animation of -webkit-transform matrix() function should not do linear interpolation
+          https://bugs.webkit.org/show_bug.cgi?id=20667
+
+        Test: animations/matrix-anim.html
+
+        * ChangeLog:
+        * platform/graphics/AffineTransform.cpp:
+        (WebCore::affineTransformDecompose):
+        (WebCore::affineTransformCompose):
+        (WebCore::AffineTransform::blend):
+        * platform/graphics/AffineTransform.h:
+        * rendering/style/RenderStyle.cpp:
+        (WebCore::MatrixTransformOperation::blend):
+
 2008-09-08  Alexey Proskuryakov  <ap@webkit.org>
 
         Reviewed by Darin Adler.
index 664bf28..44c01d7 100644 (file)
 
 namespace WebCore {
 
+static void affineTransformDecompose(const AffineTransform& matrix, double sr[9])
+{
+    AffineTransform m(matrix);
+
+    // Compute scaling factors
+    double sx = sqrt(m.a() * m.a() + m.b() * m.b());
+    double sy = sqrt(m.c() * m.c() + m.d() * m.d());
+
+    /* Compute cross product of transformed unit vectors. If negative,
+        one axis was flipped. */
+
+    if (m.a() * m.d() - m.c() * m.b() < 0.0) {
+        // Flip axis with minimum unit vector dot product
+
+        if (m.a() < m.d())
+            sx = -sx;
+        else
+            sy = -sy;
+    }
+
+    // Remove scale from matrix
+
+    m.scale(1.0 / sx, 1.0 / sy);
+
+    // Compute rotation
+
+    double angle = atan2(m.b(), m.a());
+
+    // Remove rotation from matrix
+
+    m.rotate(rad2deg(-angle));
+
+    // Return results
+
+    sr[0] = sx; sr[1] = sy; sr[2] = angle;
+    sr[3] = m.a(); sr[4] = m.b();
+    sr[5] = m.c(); sr[6] = m.d();
+    sr[7] = m.e(); sr[8] = m.f();
+}
+
+static void affineTransformCompose(AffineTransform& m, const double sr[9])
+{
+    m.setA(sr[3]);
+    m.setB(sr[4]);
+    m.setC(sr[5]);
+    m.setD(sr[6]);
+    m.setE(sr[7]);
+    m.setF(sr[8]);
+    m.rotate(rad2deg(sr[2]));
+    m.scale(sr[0], sr[1]);
+}
+
 bool AffineTransform::isInvertible() const
 {
     return det() != 0.0;
@@ -100,4 +152,35 @@ FloatPoint AffineTransform::mapPoint(const FloatPoint& point) const
     return FloatPoint(static_cast<float>(x2), static_cast<float>(y2));
 }
 
+void AffineTransform::blend(const AffineTransform& from, double progress)
+{
+    double srA[9], srB[9];
+
+    affineTransformDecompose(from, srA);
+    affineTransformDecompose(*this, srB);
+
+    // If x-axis of one is flipped, and y-axis of the other, convert to an unflipped rotation.
+    if ((srA[0] < 0.0 && srB[1] < 0.0) || (srA[1] < 0.0 &&  srB[0] < 0.0)) {
+        srA[0] = -srA[0];
+        srA[1] = -srA[1];
+        srA[2] += srA[2] < 0 ? piDouble : -piDouble;
+    }
+
+    // Don't rotate the long way around.
+    srA[2] = fmod(srA[2], 2.0 * piDouble);
+    srB[2] = fmod(srB[2], 2.0 * piDouble);
+
+    if (fabs (srA[2] - srB[2]) > piDouble) {
+        if (srA[2] > srB[2])
+            srA[2] -= piDouble * 2.0;
+        else
+            srB[2] -= piDouble * 2.0;
+    }
+
+    for (int i = 0; i < 9; i++)
+        srA[i] = srA[i] + progress * (srB[i] - srA[i]);
+
+    affineTransformCompose(*this, srA);
+}
+
 }
index 358d157..83c8102 100644 (file)
@@ -106,6 +106,8 @@ public:
     bool isInvertible() const;
     AffineTransform inverse() const;
 
+    void blend(const AffineTransform& from, double progress);
+
 #if !PLATFORM(WX) || USE(WXGC)
     operator PlatformAffineTransform() const;
 #endif
index 862b763..2e5ae9f 100644 (file)
@@ -543,29 +543,21 @@ PassRefPtr<TransformOperation> MatrixTransformOperation::blend(const TransformOp
 {
     if (from && !from->isMatrixOperation())
         return this;
+
+    // convert the TransformOperations into matrices
+    IntSize size;
+    AffineTransform fromT;
+    AffineTransform toT(m_a, m_b, m_c, m_d, m_e, m_f);
+    if (from) {
+        const MatrixTransformOperation* m = static_cast<const MatrixTransformOperation*>(from);
+        fromT.setMatrix(m->m_a, m->m_b, m->m_c, m->m_d, m->m_e, m->m_f);
+    }
     
     if (blendToIdentity)
-        return MatrixTransformOperation::create(m_a * (1. - progress) + progress,
-                                            m_b * (1. - progress),
-                                            m_c * (1. - progress),
-                                            m_d * (1. - progress) + progress,
-                                            m_e * (1. - progress),
-                                            m_f * (1. - progress));
-
-    const MatrixTransformOperation* fromOp = static_cast<const MatrixTransformOperation*>(from);
-    double fromA = fromOp ? fromOp->m_a : 1.;
-    double fromB = fromOp ? fromOp->m_b : 0;
-    double fromC = fromOp ? fromOp->m_c : 0;
-    double fromD = fromOp ? fromOp->m_d : 1.;
-    double fromE = fromOp ? fromOp->m_e : 0;
-    double fromF = fromOp ? fromOp->m_f : 0;
-    
-    return MatrixTransformOperation::create(fromA + (m_a - fromA) * progress,
-                                        fromB + (m_b - fromB) * progress,
-                                        fromC + (m_c - fromC) * progress,
-                                        fromD + (m_d - fromD) * progress,
-                                        fromE + (m_e - fromE) * progress,
-                                        fromF + (m_f - fromF) * progress);
+        std::swap(fromT, toT);
+
+    toT.blend(fromT, progress);
+    return MatrixTransformOperation::create(toT.a(), toT.b(), toT.c(), toT.d(), toT.e(), toT.f());
 }
 
 bool KeyframeList::operator==(const KeyframeList& o) const